読者です 読者をやめる 読者になる 読者になる

テストを自動化できない場合でのリファクタリング

ソフトウェア開発

 とりあえず組み込みではホスト上でテストを自動化できない場面が結構多い。
 もちろんそれは設計や実装の工夫である程度削減することはできる。しかし例えば

  • 処理系が生成するコード周り
  • ハードウェア制御周り
  • カーネルといったローレイヤー周り

のような所では、自動化できない領域や、自動化するメリットがデメリットを上回らない領域がどうしても残ってしまう。

 ただ一方で、困ったことに組み込みであってもそれなりの規模のコードを書いていくとリファクタリングの需要がかなり出てくる。

 そこで「テストを自動化できないコードでありながら、その挙動の等価性の証明しなければならない」なんてことになるのだけれど、今回はその方法について、試したり考えたりしているものをまとめていた。以下に列挙してみる。

※今回の話は「テスト(自動化された単体テスト)のないリファクタリングリファクタリングにあらず」なんていった格言と矛盾している。が、コードの等価性を維持しつつコードを良くするという手法の良い呼び方が他に思いつかなかったので、今回は触れないようにしている。

※ICEやエミュレーションサーバを使うなんてリッチな方法は、多用できない点で同じく今回は触れない。

実機テスト

 システムテストレベルで、特定のコードの挙動をテストする。言い換えると、QAテストのように実機を直接操作することで、特定のコードの挙動の等価性を評価する。これは場合によっては単機能テスト的な形をとることになると思う。


 この方法は、ファームウェアの規模が小さかったり、テスト対象がUI周りのコードであったりする場合に導入可能であることが多い。ただし評価対象のコードと評価対象でないコードを分離して評価できるように、設計や実装でそれなりの工夫を凝らす必要はある。

 なお注意として、この方法では俗にいうモンキーテストみたいな手順を取ってはならない。特定のコードの挙動を網羅的にチェックするのに十分なテストケースを用意し、それをきちんとしたテスト手順書にまとめないといけない。そうしないと、リファクタリングを支えるテストとして、全く信用できなくなる。

デバッガによるデバッグ

 ステップ実行やブレークポイントといったハードウェアデバッガの機能を活用する。
 具体的には、ブレークポイントでとめてテスト初期状態をセットアップし、特定ステップまでコードを実行させて、変数値や停止地点のブレークポイントが正しいか判定するアプローチをとる。なおこの方法ではメモリイメージの保存やロードが可能な環境ならば、テストのセットアップがより柔軟にできるようになる。


 この方法は、デバッガ環境を整える必要があるものの、ステートメント単位のテストが可能という他にないメリットを持つ。

 ただこの方法も、通常のアドホックなデバッギングのような手順を取ってはならない。テストが再現可能になるように、きちんとテストケースを定義して、それをきちんとしたテスト手順書にまとめる必要がある。そうしないと、上記と同じくリファクタリングを支えるテストとして、全く信用できなくなる。

テストランナーを実機に焼きこむ

 CUnitといったテスティングフレームワークを含むテストランナーを、実機に焼きこむためのイメージファイルにしてしまう。そして実機のUIを使用して単体テストの結果を取得し、挙動の等価性を評価する。なおここでの「実機のUI」は、UARTによる文字列出力だったり、オシロスコープだったり、LEDだったりと、とりえる手段に応じて最適なものを選択する。
 要は、ホスト上で自動化できないなら、ターゲット上で自動化を進めてみよう、という対応策。ただ結果の取得方法を自動化しない場合がある点、一般的な方法と異なる。


 おそらくこれは開発環境的にプアな組み込みエンジニアが取りえる、最も妥当な選択肢だと思う。
 問題は、実機への焼きこみに手間がかかる場合は軽快なテストができない点と、テストごとにイメージファイルを用意するのが得てして予想以上に面倒くさい点。さらシリアル通信といったテスト結果を取得するための機能を新たに追加しなければならない場合もあるだろう。また人の手や人の目を使う場合は、前述の通りテスト手順書としてその方法をきちんと定義しておく必要がある。

コードレビュー

 挙動の等価性を文字通り人の目でチェックする。具体的には「修正→レビュー→修正→レビュー→」のサイクルを非常に細かくまわしていくことで、挙動の等価性を維持しつつコードの改善を進めていく。

 形式はパスアラウンドでも一般的なレビューでもいい。ただパスアラウンドでレビューを進めていく場合は、コーダーとレビューアは分けた方が絶対に安全だと感じる。そこではレビューアが複数人でいるとより安全。


 この方法では、バージョン管理やdiffといったツールの支援や、レビュー状況の記録やレビュー要求の通知などのシステム化がとても重要。当然、その作業の一連の流れは、開発プロセスとして手順化しておく必要がある。
 なおこの方法は、修正範囲が小さい実装レベルのリファクタリングに結構向いていると思う。チームで役割を分担してうまく運用すると、結構軽快に作業を進められると感じる。

まとめると

 とりあえず、テストを自動化できない場合では、以下のような心構えが必要だと考える。

  • テストを自動化できない領域は人の手や人の目で補助しないとどうしようもない時がある
  • 人の手や人の目に頼るときは、テストケースをきちんと設計し、それを実行する手段をテスト仕様書としてきちんと定義するアプローチが必要

 ただこういった考え方は別段トリッキーなものだとは感じていない。なぜならこのアプローチに基づいた方法を、テスターの方々がQAテスティングの世界で実践してきている事実があるからだ。その点、デベロッパーテスティングを扱う場合でも、QAテスティングの方法論や知識を学ぶのはとても重要なことだと思う。