Chapter 18
テスト自動化ハーネスを作る — 手技を仕組みに
この章のゴール.
単発の手作業を、繰り返し自動で回るテストハーネスに育てる設計をつかむこと。 刺激・観測・判定の抽象化、機器ドライバの分離、pytest での記述、計測器連携(pyvisa/ppk2)、 ログとレポート、そして CI で「生成 AI が直すたびに全部走る」形にするところまで。
この章で使う既出の用語(定義は各リンク先). USB(01 章 2 節)、オラクル(01 章 8 節)、仕様書(01 章 3 節)、再現性(01 章 4 節)、記録(01 章 4 節)、ハーネス(02 章 7 節)、判定(02 章 1 節)、刺激(02 章 1 節)、電源(02 章 2 節)、オシロ(04 章 8 節)、UART(05 章 1 節)、モード(06 章 5 節)、DAC(07 章 2 節)、接続(09 章 2 節)、read(10 章 2 節)、CAN(13 章 5 節)、ばらつき(14 章 1 節)、HIL(15 章 8 節)、時系列(16 章 5 節)、道具(16 章 2 節)
1. なぜ仕組みにするのか
03 章〜17 章の技は、手でやると一度きりで終わる。生成 AI は何度もコードを直すから、そのたびに同じ検証を手で繰り返すのは非現実的。テストハーネス——刺激を与え、観測を集め、判定し、記録する自動装置——にまとめれば、「変更→全テスト自動実行→回帰検出」が回る。
テストハーネス(test harness) とは、テスト対象を組み込んで自動実行する土台のこと。対象への刺激注入、応答の収集、期待との照合、結果の記録を一手に担う「テストを走らせる骨組み」。
2. 三層に分ける — 抽象化が命
良いハーネスは、変わりやすい所と変わりにくい所を分ける。おすすめは三層。
- トランスポート層(下): 実際に線・電波を叩く部品。UART ドライバ、
bleakラッパ、ロジアナ制御、DAC 制御、電流計(ppk2)。ハードが変わってもここだけ直す - 機器モデル層(中): 対象を「意味のある操作」で表す。
dut.set_mode("run"),dut.read_temperature(),dut.wait_for_alarm()。02 章の刺激・観測・判定をメソッドにする - テスト層(上): 筋書き。「起動→モード設定→温度異常注入→0.5s 以内に警報」。上ほど読みやすく、ハード非依存
こうしておくと、テスト(上)は日本語の仕様書のように読め、ハードの都合(下)に汚されない。
class DUT: # 機器モデル層
def __init__(self, uart, ppk, fake_temp):
self._uart, self._ppk, self._temp = uart, ppk, fake_temp
def set_temp(self, c): # 刺激(偽センサ, [07 章](07_アナログ周辺とセンサ.md))
self._temp.set_celsius(c)
def state(self): # 観測(コンソール, [05 章](05_UARTとシリアルコンソール.md))
return self._uart.query("state?")
def current_mA(self): # 観測(電流, [03 章](03_電源と消費電流から状態を読む.md))
return self._ppk.mean_mA()
def test_overheat_alarm(dut): # テスト層(読みやすい筋書き)
dut.set_temp(25); assert dut.state() == "IDLE"
dut.set_temp(85); assert dut.wait_for("ALARM", 0.5)3. pytest を土台にする
車輪を再発明せず、成熟したテストランナーに乗る。Python なら pytest が定番。
- fixture: 機器の接続・電源投入・後始末を共通化(
dutを各テストに配る) - パラメータ化: 同じテストを多数の値・個体で回す(
@pytest.mark.parametrize)——ばらつき・境界(02 章・16 章) - マーカー:
@slow(ソーク)、@hw(実機必須)で選別実行 - アサーション: 期待との照合をそのまま書け、失敗時に値を表示
4. 計測器とつなぐ
ハーネスの下層は、実際の計測器・注入器を叩く。標準的な口を押さえる。
- SCPI / pyvisa: オシロ・電源・DMM など多くの計測器が SCPI という共通コマンド言語を話す。
pyvisaで Python から設定・取得(INIT,MEAS:VOLT?等) - 専用 API: PPK2(
ppk2-api、03 章)、bleak(BLE)、pyocd/pylink(デバッグ、08 章)、python-can(CAN)、pyusb(USB) - 自作注入器: 19 章の自作マイコンを、シリアルの簡単なコマンドで操る
5. ログとレポート — 落ちたとき再現できるか
自動テストの価値は、失敗を正確に残すことにある(02 章の再現性)。
- 構造化ログ: 各ステップの刺激・観測・時刻を機械可読に(JSON/CSV)。生の波形は別ファイルに
- 失敗時アーティファクト: 落ちた瞬間の入力・波形・スクショ・ログを一括保存。「再現できないバグは直せない」
- レポート: pytest の HTML/JUnit 出力、傾向(レイテンシ時系列、14 章)のグラフ
- 相関: 「送った刺激」と「起きた観測」を時刻で突き合わせられる形に
6. CI に載せる — 生成 AI と噛み合わせる
最終形は、コード変更のたびに自動でテスト一式が走ること。
- CI パイプライン: リポジトリに push → ビルド → 実機ハーネスで回帰テスト → 結果をレポート
- 実機 CI(HIL ファーム, 15 章): 自己ホストのランナーに実機と HIL 装置をつなぎ、夜間に全シナリオ
- 段階化: 速いスモークは毎コミット、重いソーク(16 章)は夜間・週次
- 生成 AI ループ: AI が直す→CI が落ちる→失敗ログを AI に戻す、で自動修正の質が上がる
7. 何を大事にするか(この章のオラクル観)
- テストが読みやすく(テスト層がハード非依存)、再現可能(失敗を完全に保存)
- 回帰を確実に捕まえる(前は通ったのに落ちた、を自動検出)
- 遅いテストと速いテストを分けて回せる
- 計測器・注入器が共通の抽象で差し替えられる
8. 手を動かす
ハーネスを設計する
この章のポイント
- 手技をテストハーネス(刺激・観測・判定・記録の自動装置)に育てると、AI の反復修正に回帰テストで噛み合う
- 三層(トランスポート/機器モデル/テスト)に分け、テスト層を仕様書のように読める形に
- pytest を土台に fixture・パラメータ化・マーカーで整理
- 計測器は SCPI/pyvisa や専用 API(ppk2-api, bleak, pyocd, python-can)で叩く
- ログと失敗アーティファクトで再現性を担保し、CI(自己ホスト+HIL)で自動回帰