#pragma twice

KAB-studio > プログラミング > #pragma twice > 240 Version 13.04 テストプログラム

#pragma twice 240 Version 13.04 テストプログラム

前のページへ 表紙・目次へ 次のページへ

 Version 13.04
テストプログラム

前回説明したように、関数には【コントラクト】というものが存在しま

仕様通りに関数が動きますよ、っていう契約ってことね
でも、その仕様はコメントやドキュメントだけの、いわば〈口約束〉。仕
様通りに動かない可能性は十分にあります
でもさ、それってやっぱり仕方ないんだよね
仕方ない、って割り切らない方がいいかな
そなの?
なぜかというと、仕様通りに動くかどうかを確認する方法はあるから
あるんだ……
どうやって確認するかというと、関数を呼ぶためだけのプログラムを作っ
て確認すればいいんです
関数をテストするプログラムを作る、ってこと?
そういうこと。たとえば、こういう仕様の関数を作る場合

・入力:整数1 
・出力:整数1 * 10 
    ただし、整数1が 214748364 より大きい時は 1 を返す

これを実際に作ると、こういう関数になります

int TenTimes_WithError( int p_i )
{
    if( p_i > 214748364 )
    {
        // エラー!
        return 1;
    }
    
    return p_i * 10;
}

仕様通りね
この関数をテストするプログラムは、こういうふうになります

void testTenTimes_WithError()
{
    if( !( 2147483640 == TenTimes_WithError( 214748364 ) ) )
    {
        TRACE( "エラー1\n" );
    }

    if( !( 1 == TenTimes_WithError( 214748365 ) ) )
    {
        TRACE( "エラー2\n" );
    }
}

2回呼び出して……それぞれ引数が違ってて、その結果とチェックしてて
違っていたら TRACE で出力……
こういうふうに、プログラムでプログラムをチェックするわけ。これを、
プログラミングの用語で【ユニットテスト】と言います
ユニットテスト?
日本語で書くと【単体テスト】って意味になるんだけど、別物と考えた方
がいいかな。ユニットテストには次のようなメリットがあります

・関数が仕様を満たしているかチェックできる。
・関数のチェックを自動的に行うことができる。
・仕様をプログラムとして記述できる。

まず〈関数が仕様を満たしているかチェックできる〉はそのままの意味
ってゆーかそれだけじゃないんだ
そう。次の〈関数のチェックを自動的に行うことができる〉っていうのは
重要。この関数が自分で作ったものの場合、あとで修正することがあるで
しょ。そのときのチェックもこの関数を使えば
自動的にチェックできる!
そういうこと。逆に言うと、このチェックがあるから修正が楽になる、
っていうこともあります
どういうこと?
この業界には〈ちゃんと動いてるものには手を触れるな〉っていう業界用
語があります
なんかすごく後ろ向きな業界用語ね……
でしょう。でもユニットテストがあれば大丈夫。手を触れても自動的に
〈ちゃんと動いているか〉をチェックできるから
手作業でもチェックできるじゃない
手作業だと時間掛かりすぎるし、どうしても抜けがあるからね
プログラムで自動化すれば問題なし、と
そういうこと。ちなみに、このユニットテストを利用して関数を修正する
ことを【リファクタリング】といいます
リファクタリング?
そう。関数を改造したり作り直すことを差します。普通に作り直すと大変
だけど、ユニットテストがあれば
作り直した後のチェックが簡単ってことね
そういうこと
最後の〈仕様をプログラムとして記述できる〉って?
テストプログラムそのものが、プログラムの仕様書にもなってる、ってこ
と。たとえば

    if( !( 2147483640 == TenTimes_WithError( 214748364 ) ) )
    {
        TRACE( "エラー1\n" );
    }

これは〈 214748364 を入力すると 2147483640 が返る〉っていう意味だ


    if( !( 1 == TenTimes_WithError( 214748365 ) ) )
    {
        TRACE( "エラー2\n" );
    }

これは〈 214748365 を入力すると 1 が返る〉っていう意味。プログラム
自体が、その関数がどう動作するのかを示してるわけ
だからプログラムが仕様書になる、ってことね。でもさ、これって不十分
じゃない?
だね、これだと〈10倍する〉とか〈 214748364 より大きい数〉とかが
含まれてない、たった2つの例だけだからね。かといって int の範囲の数
すべてチェックすると時間が掛かるし
掛かってもいいんじゃないの?
たまにしかしないならそれもいいかな。ただ……ここからはちょっとやや
こしい話になるんだけど、本当のテストプログラムはこうじゃないんです
本当は違う、って?
本当のテストプログラムは、専用のライブラリを使うんです。 C++ だと 
CppUnit っていうライブラリを使います
でも今みたいに if 使ってもいいんじゃない?
一応いいんだけど、 CppUnit を使うと次のようなメリットがあります

・書き方が統一される。
・エラーメッセージがわかりやすい。
・一般的。

まず、 CppUnit のライブラリを使うことでテスト方法が統一される、
っていうのがメリットのひとつ。上の例でも if の中がややこしいけど、 
CppUnit を使うと次のように簡単に書けます

    CPPUNIT_ASSERT_EQUAL( 1, TenTimes_WithError( 214748365 ) );

 CPPUNIT_ASSERT_EQUAL ってマクロ使うの?
他にも方法はあるけど一例ってことで。この場合、第1引数と第2引数が
合っているかチェックして、間違っていたらエラーメッセージ表示します
そのメッセージがわかりやすい、と
メッセージに、左側の値=期待する値と、右側の値=実際の値が表示され
て、どこが違うのかがわかるから
確かにさっきの方法だとわかんないもんね
あと、自分でテストのプログラム作るよりは、 CppUnit を使った方が一
般的に見やすいプログラムになると思うから
……ってほど有名じゃないような気がするけど
 JUnit で検索してみて
あ、いっぱいヒットした
 JUnit の C++ 版が CppUnit 。でもあまり一般的じゃないし、そういっ
た理由で導入がちょっと面倒だから、実際の使用方法はパス
って意味ないじゃん!
使い方自体はネットで調べて。それよりも重要なのは、テストプログラム
を作る理由
……大事なのは根本的なこと、ってことね
そういうこと。さっきの〈時間がかかる〉って話だけど、ユニットテスト
は、本当はコンパイルする度くらい頻繁に実行します
それは多いね……あ、だから時間掛かるのいけないんだ
そういうこと。ユニットテストを常に掛けておくことで、プログラムが常
に仕様通りに動くってことが保証されるわけ
まさにコントラクトね
さらに、このテストプログラムを先に作って、実際の関数をあとで作るっ
ていう方法もあります
へ? 何それ、なんか逆じゃない?
テストプログラムは何の代わりにもなるんだっけ
仕様書……あ、そっか、関数の仕様は先に作るから
それと同じようにテストプログラムを作って、そのあとで、仕様通りに動
くように関数を作るわけ
それなら確実よね……
こういう考え方を【テストファースト】って言います
テストを先に、ね
テストプログラムを作ることで、仕様通りに関数が動くかどうかを常に
チェックできる、つまり契約に効力を持たせることができるわけです
仕様通りじゃなければ契約違反、それがわかるんだもんね
ただ、実際にはこうはうまくいかないところもあります

・作るプログラムの量が増える。
・テストプログラムにバグができる可能性がある。
・仕様が変わるとテストプログラムも変える必要がある。
・テストしきれない場合がある。

作る量が増えるのは当然だけど、でもちゃんと仕様通りに動くんならあと
で直すところとか減るからどっこいどっこいなんじゃない?
それはそうだと思うよ。ただ、プログラムがどれくらいでできる、ってい
う見積もりの時点で〈うまく動かないときにあとで修正する分〉って含みづ
らいから
確かにそうね、動かなくなるっていうのを前提にはできないよね
でも、テストプログラムを作る分っていうのは確実に増えるから、その分
プログラムを作る時間は増えるよね
テストプログラムを作ると確実に時間が掛かる……って、なんかだまされ
てない?
もちろん〈うまく動かないときにあとで修正する分〉が存在しないわけが
ないんだけど……
……次のテストプログラムのバグ、ってありがちそうよね
特にコピペが多いとすごく出やすいから、これは問題かも
……テストプログラムのテストプログラム?
きりないし……
仕様の変更、っていうのもありそうよね。それにあわせてテストプログラ
ムも変えなきゃいけないんだ
そういう点を考えても、テストプログラムは仕様が重要ってこと。元々、
コントラクトベースのプログラミングは仕様がしっかりしてないと成り立た
ないから
契約しようがないもんね
仕様がしっかり決められない状態でプログラムを組み始めると前提が成り
立たないかも
最後のテストしきれない、っていうのは?
 Version 13.02 ( No.238 ) で〈関数の中で閉じている〉って言ったけ
ど、この閉じてない関数をテストするときには状態が多すぎるでしょ
そか、ファイルとか関係してくるとテストしきれないね
それに、関数から関数を呼んでいたりすると、その分入力と出力のバリ
エーションが増えるから
テストし切れない……
それでも、アルゴリズム関数の仕様をしっかり決めたり、それに基づいて
assert() を使ったりテストプログラムを作ることは重要
デメリットがあっても?
デメリットではあるけど、時間があれば解決できることが多いから。プロ
グラムを組む時間をどれだけ掛けられるか、を考えてどこまで理想を追求で
きるか、が重要かな

/*
    Preview Next Story!
*/
な、なんか今回っていつもと違う感じ……
プログラミングの話っぽくなかった?
全然ない! なんか概念ってゆーかなんてゆーか……
僕としてはこういう方が重要だと思うんだけどね
でもコーディングしないとつまんないなー
う”
というわけで次回
< Version 13.05 エラーの返し方 >
につづく!
次もおんなじ感じ?
ちょっとそうかも

 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。