はじめに
ここまでで、
IdentifySMART LogError Information Log
のような read-only 系の command を少しずつ実装してきました。
これらの実装の際に、今回は mock を導入しました。
最初の動機はかなり単純で、「mock があると検証がそれっぽくなるらしい」という程度のものでした。
今回のサブテーマである「品質向上」の一助になるかな?と思って導入してみました。
実際に使ってみると、確かに便利です。
が、しかし、それは「mock があれば何でも楽になる」という意味ではありませんでした。
残念なことに、mock は実機の動作をある程度想像できないと作れないです。
つまり、mockを使いこなせない初心者は、mockを使うために実機を触る、という本末転倒な状態になりかねないです。
mock の役割
今回の mock は、実機の代わりに command を受けて、想定した返り値を返す疑似実装です。
例えば、
IdentifyControllerならVID / serial / model / firmware / ELPESMART Logなら温度、spare、used、read / write、時間系の項目Error Information LogならError CountやOpcodeを含む entry
のような値を、自分で用意して返す形にしていました。
役割としては、
- 実機が無くても command の流れを進められる
- 表示や decode を先に確認できる
- real device では出にくいケースも作れる
というものです。
ただ、ここで大事なのは、「何を返すべきか」を自分で決めなければいけないことです。
実際には、mock を作ること自体が難しかった
使ってみて分かったのは、mock は思っていたより難しいということでした。
理由は単純で、
- 実機がどう返すのか
- その field はどういう意味なのか
- どんな値が自然なのか
をある程度想像できないと、mock の中身を作れないからです。
例えば SMART Log なら、
- 温度はどれくらいの値が自然か
Available SpareやPercentage Usedはどう見せるかData Units Read/Writeはどのくらいの桁になるかWCTT / CCTTは threshold ではなく time なのではないか
のような理解が必要でした。
Error Information Log でも、
countは page 数ではなく entry 数ではないかError Count == 0は invalid entry ではないかELPE + 1が最大件数なのではないか
ということが分からないと、mock 側の値も自然に決められません。
つまり今回の体験では、mock は「仕様が分からなくても先に進める道具」というより、ある程度仕様を想像できる人が使うと強い道具に近いものでした。
今回は AI がそのギャップをかなり埋めてくれた
とはいえ、自分は最初からそこまで想像できていたわけではありません。
今回かなり助かったのは、AI と相談しながら
- どういう値を mock に入れると自然か
- その field は何を意味しているのか
- 単位は何か
- この項目は本当に threshold なのか、time なのか
を一緒に整理できたことです。
つまり、今回は
- 自分だけで実機を想像して mock を作った
というより、
- AI を使って想定動作を補いながら mock を組み立てた
という進め方でした。
この意味では、mock が便利だったというより、mock を作るための理解を AI が補ってくれたという方が実感に近いです。
それでも mock があると進めやすかった
難しさはあったものの、mock を入れた効果はかなり大きかったです。
例えば、
SMART Logの表示形式を先に決めるNo Error Log in Nvmeのような出力を先に決める--countの上限超えや warning を試す- 温度センサ
0をno reportとして扱う
のようなことは、mock があるとかなり進めやすくなりました。
real device だけだと、
- 今その error が本当に出ているか
- 実機の値が都合よく欲しい形で出るか
にかなり左右されます。
一方で mock なら、
- 今確認したい値を自分で置ける
- 出したいケースを自分で作れる
ので、表示や decode の確認がしやすくなります。
今回の mock は静的だからこそ成立した
今回の実装では、mock はかなり静的です。
- command ごとに決め打ちの値を返す
- call 回数や順番で状態は変えない
- retry や failure injection までは扱わない
という形です。
これは read-only 系の command を扱っていたからこそ成立した面があります。
今回の範囲では、
IdentifySMARTError Information Log
のように「取得系」が中心だったので、まずは静的な mock でも十分役に立ちました。
逆に、もっと状態を持つ command や複雑な挙動を扱うなら、
- 状態を持つ mock
- 呼び出し順に応じて返り値が変わる mock
のような形も必要になるはずです。
また、今回の mock の粒度についても、後から振り返ると少し考える余地がありました。
今の実装は感覚として、
Identify用の mockSMART用の mockError Information Log用の mock
のように、1機能に対して 1 つの想定返り値を置く形に近いです。
これは v0 の read-only 実装には十分でしたが、正常系の device、温度が高い device、error を複数持つ device のように、device 自体の状態を先に決めて mock を作るやり方もありそうです。
そうすると、Identify、SMART、Error Log の返り値に一貫性を持たせやすくなります。
real は何を確認する場所だったか
では real device は不要だったかというと、もちろんそうではありません。
real 側で見たかったのは、
- command が本当に通るか
- decode が実機でも破綻していないか
- mock で置いた値のスケール感が大きく外れていないか
- 実機特有の挙動はないか
という部分です。
特に Error Information Log では、
- 実際の
ELPEはいくつか - error が無い時にどう見えるか
Transport TypeやOpcodeはどんな値になるか
のようなことは、real で確認する意味がありました。
なので、自分の中では
- mock は仕様を固定して進める場所
- real はその理解が実機でも通るかを見る場所
という役割分担になっています。
まとめ
今回 mock と real の両方を持ってみて感じたのは、mock は単純な「実機の代用品」ではなかったということです。
mock には確かに便利さがあります。
ただ、その前提として、
- 実機がどう返すかをある程度想像できること
- field の意味や単位をある程度理解していること
が必要でした。
今回の体験としては、
- mock があるから楽だった
というより、
- AI と相談しながら mock を作れるようになったことで、実装と仕様理解が前に進んだ
という感覚に近いです。
そのうえで、
- mock で仕様を固定する
- real で実機確認をする
という二段構えにしたことで、Identify、SMART、Error Log をかなり進めやすくなりました。
一方で、ここまで書いてくると、lower layer のテストは今の静的 mock だけでは足りないとも感じます。
open / ioctl / timeout / retry のような挙動を見たいなら、
- stateful な mock
- あるいは fake executor / fake driver
のようなものが必要になりそうです。