お気に入りツール紹介:C++test (2)
お気に入りツール紹介:C++test (1)からの続きです。
今回は、C++testを使用してユニットテストを実行する際のお話です。次のようなテーマで書いていきます。
- CppUnitのテスト実行が面倒。もっと楽をしたい → ユニットテスト支援機能
- テスト品質を高めたい → コードカバレッジ測定機能
- コード品質を上げ、同時にエンジニアの教育もしたい → 静的解析機能
CppUnitのテスト実行が面倒。もっと楽をしたい
ユニットテストの課題
ユニットテストはとても便利なものですが、テストならではの課題を持っています。
CppUnitではユニットテストがすべてパスすると緑バーが表示されます。うーん、実に気持ちイイ!しかし、この緑バーには2つの可能性があるのです。
- 意図した通りにテストが行われ、すべて合格した
- 意味不明なテストが行われ、バグを見つけられなかった
どっちがどっちなのか、残念ながら区別をつけることはできません。
緑バーの持つ2種類の可能性
たとえば極端な話、テストケースのメソッドを空っぽにしちゃいましょう。そしてそれを実行すると・・・見事合格!そう、「エラーさえ起こらなければ合格」なんです。でも、空っぽのテストケースでいったい何をテストしているのか、サッパリ意味不明ですよね?
ユニットテストに限らず、テストというものの持つ宿命とでも言うべきでしょうか。緑バーはエラーが起きなかった、という事しか表していません。ですから、「意図した通りにテストが行われた」事を、なにか別の方法で確かめないといけないのです。これが、ユニットテストの課題です。
CppUnitにおけるやり方
CppUnitを使って、上記の課題を克服するにはどうすればいいのでしょうか?
テスト駆動開発では、次のような手法を勧めています。
- テストケースだけを書き、テスト対象コードは書かない → コンパイルエラーが起きることを確認
- テスト対象コードに、あえて失敗するような中身を書く → テストが失敗する事を確認
- テスト対象コードをきちんと書く → テストが成功する事を確認
要するに、「テスト対象のコードを書く前は、ユニットテストが失敗した」 → 「テスト対象のコードを書いたら、ユニットテストが成功した」 → 「だから、テストは正しく行われているはずだ!」と考えるわけです。
まぁ、それほど大きな手間でもないのですが、面倒なんですよねやっぱり・・・
C++testにおけるやり方
要するに、「ちゃんとテストが実行されている事」を確かめたいんですね。C++testを使うと、それが比較的ラクに行えます。
C++testでは、テストケース中に「実行した結果、この変数がどうなるかを確かめよう」という記述が出来ます。
POST_CONDITION記述
このテストケースを実行すると、「未検証の結果があるよ」という事を教えてくれます。
未検証の結果
この内容をみて、「ふむふむ、この値でオッケーですぞ」と思ったら、「結果を検証」してやります。
結果を承認する
そうすると、ユニットテストのコードが自動的に書き変わります。
ユニットテストのコードが書き換わる
この機能を使うと、CppUnitにおけるやり方は次のようになります。
- この値を確認しましょう、という形でユニットテストを書く
- ユニットテストを実行する
- 結果を確認して、OK / NG を判断してやる
こういうステップで作業することで、次のようなメリットが得られます
- わざわざ失敗させるだなんて、面倒な事しなくて済む
- そもそも期待値を書くのすら面倒なので、それをC++testにやらせられる
2つ目は・・・かんぜんに堕落してますね!テストケースを書いていて、「あー、この結果はこうなるだろうなー」とか思います。でも、それを手で打つのが面倒なんです!
だからC++testのPOST_CONDITION記述を使って、「とにかく、この変数の値を確かめよう」とだけ書くんです。実行結果をみてOK/NGの判断を一回しちゃえば、期待値がテストコードに埋め込まれます。そうすれば、次からは自動的に判定が行われます。
このように、CppUnitと違って余計なステップを省けるだけでなく、期待値すら手で入れなくていい、というとっても堕落できる環境が手に入ります。1粒で二度おいしいとはこの事ですね。
CppUnit, C++testどちらでも通じるやり方
実は、どちらでも使えるうまいやり方もあります。それはステップ実行しちゃうことです。
そうすれば、間違いなくテストが実行されている事を、それこそ微に入り細を穿って確認できます。おまけにコード理解度まで深まる!ですから、わざわざユニットテストを失敗させるような事をしなくてもOKです。
テスト駆動開発でも、もうちょっとデバッガを活用するような方法を勧めればいいのになー、なんて思います。(現場によって環境がそれぞれ違うので、そういうのも難しいんでしょうか。。。)
テスト品質を高めたい
CppUnitでたくさんテストをすると、がんばった!という気にはなります。でも、どこまでキチンとテストをしたのかを計ることはできません。どこまでテストしたかを見るには、カバレッジ測定を行うのが一般的です。
C++testでは、ユニットテストを実行した際に、自動的にカバレッジ測定が行われます。ユニットテスト終了後、コードの画面がこんなふうに変わります。
コードカバレッジ測定
緑の行が実行済み、赤の行が未到達です。この画面はC0の測定ですが、それ以外の種類のカバレッジも(自動的に)測定されています。
カバレッジの種類を選択
私は主に「行カバレッジ」「判断文カバレッジ」を使用していますが、お好みでどんなカバレッジでも選択できます。(1回実行すればすべての種類のカバレッジが測定されています。カバレッジの種類毎にテスト実行するような面倒な作業は不要です。)
また、プロジェクト全体のカバレッジ集計も見られます。
カバレッジ集計
ですから、どのファイルのカバレッジが甘いか、ひと目で確かめられます。
C++testには、およそカバレッジに関してやりたかった事はすべて含まれていますので、私は十分に満足しています。
コード品質を上げ、同時にエンジニアの教育もしたい
コードの品質を上げるためには、ある程度のコーディングルールを守る必要があります。このとき、
- ルールをいかに守るか?
- ルールをいかに学ぶか?
の2種類の課題が存在します。C++testの静的解析機能を使用すると、これらの課題を2つ同時にクリアできます。
ルールを守る
プロジェクト毎に、コーディングルールは違うでしょう。また、コーディングルールとは別に、安全で効率的なコードを作製するために守るべきルールというものがあります。
そこで、C++testでは1500近くのルールの中から、どのチェックを実施するのかを選択する事ができます。
静的解析ルール
たとえばメトリクスの所をひらくと、こんな感じになります。
メトリクスのルール
さすがに企業秘密とかもあるでしょうから、具体的なルール名はぼかしてあります。とにかく、ルール1つ1つに対して、「これはチェックする、これはチェックしない」という選択を行うことが出来ます。
個人的に嬉しいのは、「Qt ベストプラクティス」です。どうやらC++test自体にもQtが使われているんじゃないかと思うのですが、そのせいかQtサポートが意外と手厚かったりします。具体的な内容は伏せさせていただきますが・・・けっこう突っ込んだ部分まで考慮されていて、なかなかグッドです!
さらに、これらのルールは編集する事もできます。ですから、プロジェクトにあった形に改変する事が出来ます。そして、コードレビューより前に静的解析を実施すれば・・・その場で問題を指摘してくれます。ですから、他人にチェックしてもらうのをまたなくても、ルールをある程度守ることが出来るようになります。
ルールを学ぶ
ルールを守るのは重要です。しかしそのためには、ルールを学ぶ必要があります。特に、C++という言語は難しい言語です。とりあえず使うことはできますが、安全で効率のよいコードを書くには、学ぶべきことがたくさんあります。
C++testの静的解析機能には、ルールを効率的に学ぶための仕掛けもしてあります。静的解析の結果、次のような画面が表示されたとしましょう。
静的解析結果
ここで、そもそもルールを学んでいないエンジニアはこう思います。「うん?何が悪いって?」
そう思ったら、まよわず右クリックです。
ルールドキュメントを参照
すると、このルールの詳細がヘルプで表示されます。
ルールドキュメント
ルールドキュメントには、「なんで、このルールを守る必要があるの?」といった事や、具体例、参考文献などが事細かに記載されています。
私は、「静的解析でひっかかる」 → 「ルールドキュメントを読む」 → 「参考文献を買い、さらに詳しく勉強する」 といった感じで、ずいぶん勉強する事ができました。
大学では全然関係ない分野を専攻していたため、プログラミングはずっと独学です。そのため、結構あちこちにスキがあったりします。そういうスキをフォローしてくれるツールとして、静的解析機能には非常に助けられています。
C++testの不満な点
C++testは基本的にすごく重宝しているのですが、不満な点もあります。それは、コマンドラインからテストケース実行する事ができない点です。(もっと正確にいうと、私の持っているエディションではそれが出来ません。)
ビルドサーバー上で、ユニットテストを実施したいんです。毎晩、私が寝ている間に・・・
- ビルドサーバーが、リポジトリから最新のコードをチェックアウトする
- ビルドサーバー上にて、登録されているユニットテストを自動実行する
- ユニットテストの結果を、メールにて報告する
というような事をさせたいのです。しかし、そのためにはサーバー向けのエディションを購入しないといけません。このエディションは、単にコマンドラインからテストケース実行できるだけでなく、もっといろいろな事ができるっぽいです。
でも私は、コマンドラインからテストケース実行さえできれば、あとのシステムアップは全部自分でやります。たったこれだけのために、サーバー向けエディション買ってものすごい金額を支払うのはちょっと・・・
というわけで、これはポリシーの問題だと思うのですが、「コマンドラインからのテストケース実行」を一般エディションにも開放してほしい!というのが不満点です。
次回は
C++testについて書きたかったのはこれくらいです。
しかし、残念ながらC++testにかんするTipsってなかなかネット上に落ちていないため、苦労した事も多々あります。そこで、このシリーズの締めとしてTipsのご紹介をしようと思います。