m-fileに品質を!(2)
m-fileに品質を!からの続きです。
m-fileの品質を少しでも上げるため、動的チェックの一環として単体テストをやります。
- 自動実行できる → くりかえし実行できるように!
- カバレッジ測定できる → テストの妥当性がわかるように!
そのために、MATLAB xUnitを使用します。
さいしょにタネあかししちゃいますと、やることはこれだけです。
MATLABコマンドプロンプトにて、
profile on;runtests;profile viewer
これで、単体テストの自動実行→カバレッジの表示 とできます。あー分かった!と思われた方とはここでお別れです。おつかれさまでした。
意味わからん、と思われた方は続きをどうぞ。
MATLAB xUnitのワークフロー
世に出回っているxUnit(単体テストを自動実行してくれるフレームワーク)と、考え方はまったく同じです。
(1)単体テストを実行するためのプログラム(m-file)を書いておく
(2)書き溜めたテストを、一気に実行する
(3)テスト対象のm-fileを更新するたびに、(2)を実行
正直、(1)は面倒くさいです。でもここで苦労しておけば、あとですごくラクをできます。(2)を何度も何度も実行することで、開発途中でバグを入れ込んでもすぐに検出できるからです。
スクリプト群さえ書いておけば、あとはxUnitがやってくれる
単体テストのスクリプト
単体テストのスクリプトでは、次の作業をします。
(1)テスト対象のm-fileを呼び出してやる
(2)戻り値や、生成したファイルなどが、期待通りのものかチェックする
MATLAB xUnitについてるサンプルスクリプト(をちょいと修正したもの)だと、こんなかんじです。
function testFliplrMatrix
%testFliplrMatrix fliplr にマトリクスを入力したときのテスト
in = magic(3);
result = fliplr( in ); % これが(1)に該当する。fliplrをテストしたい
assertEqual( result, in(:, [3 2 1])); % これが(2)。結果をチェック。
ここで出てきている、「assertEqual」というのは、MATLAB xUnitが用意してくれている関数です。
第一引数: result
第二引数: in(:, [3 2 1])
で、これが一致しなければエラーです。一致してくれますように!と願いを込めて書きましょう。
こういうスクリプトを、1つのフォルダーにわーっと作ります。
実行
MATLABコマンドプロンプトにて、
profile on;runtests;profile viewer
これで、単体テストの自動実行→カバレッジの表示 とできます。(このコマンドを、1つのm-fileに書いておくと便利です)
xUnitが、カレントフォルダにあるテストを検索して自動的に実行してくれるため、いちいち手で登録したりする必要はありません。
実行結果レポート
結果、MATLABのコマンドプロンプトにはこうでます。
合格したところ
それと同時に、カバレッジ表示画面がポップアップします。

カバレッジ結果というか、プロファイル統計。個別の関数をクリックできる
本当は、これはプロファイラなんですね。実行時間を測定してくれます。そのついでに、カバレッジについても測定してくれています。
件のfliplrを表示
いいですね。ただし、ステートメントカバレッジのみです。かなり不安になりますが・・・ないよりはずっといいですよね!
ちなみに、テストに失敗するとこうなります。
fliplrに引数を渡さないで呼び出すテストを追加したら、エラー発生
xUnitの使いどころ
すごく個人的な意見なので、割り引いて読んでください。
私は、ぜんぶがぜんぶxUnitでテストするべきだとは考えていません。特にUI系なんかは、毎回手でテストしないとしょうがないですし、それでいいと思います。
ポイントは、設計時にこういう構成にしておくことです。
少数の複雑なm-fileと、その他大勢の単純なm-file
ほとんどのシステムは、上記のようにできると思っています。
たとえば、ちょいとややこしいテーブルデータを用意したい場合。そういう時には、データを一元管理するクラスを1つ作りましょう。そして、テーブルを扱う上で面倒な作業は、ぜーんぶそのクラスに押し付けちゃうんです。もちろん、テーブルデータそのものもクラスに持たせておいて、外部からはいじらせない。
ポイントは、「面倒なこと、ややこしい事はぜーんぶ押し付けちゃう」という所です。
具体例を挙げましょう。たとえば、シミュレーションデータを管理するモジュールを作ります。
シミュレーションデータ管理モジュール
- インターフェース (モジュールの外部が知らないといけないこと)
- 信号登録 (信号名、時刻、信号データ)→( )
- 信号取り出し (信号名)→(時刻配列、信号データ配列)
- ファイルへ保存 (ファイル名)→(保存の成否)
- ファイルから読み出し (ファイル名)→(読み込みの成否)
- 内部責務 (モジュールの外部は知らなくていい。むしろ知らせてはいけない)
- 信号を、どういう形でメモリに保持するのか?
- ファイルは、どのようなフォーマットにするのか?
ポイントは、「このモジュールを使うにあたって、知らないといけない事」を最小化することです。シミュレーションデータが、どういうファイルフォーマットで保存されているとか、外部からすれば知ったこっちゃない!!というのが理想です。逆に、モジュールを使うにあたっての注意事項が山のようにあったらダメです。それは、複雑さを外部に振りまいているわけで、バグのもとになります。
何度も繰り返しますが、「ややこしい事はぜんぶ押し付ける。そとからは、それを見えなくする」というのが重要です。そして、その面倒を押し付けたモジュールだけを、xUnitでもってテストしまくります。テスト!テスト!テスト!
こういう複雑なモジュールに不具合が起きると手がつけられません。だから、みっちりとテストをしておくべきです。
一方、わりかし単純なm-fileはいいんです。バグがあってもすぐにわかりますから、結合テストでみつけてもいいくらい。(いや、ホントはダメなんですけどね)
そんなかんじで、ここぞというモジュールだけを徹底的に叩くために、xUnitでテストしながらカバレッジ100%を目指すというのは、有効なアプローチだと考えています。(すくなくとも、C++における開発では、その方式でとてもうまくいっています)
まとめ
MATLAB xUnitは、よく出来たツールです。これと profile コマンドを組み合わせるだけで、かなりの事ができます。
ダウンロードすると、わかりやすいマニュアル、そしていくつかのサンプルスクリプトがついています。上に出てきた assertEqualや、それに類する関数についても、全てマニュアルで詳しく説明されています。
興味をもたれた方は、ぜひお試しください。