T-TestCase 「単体評価編」

T-TestCase システム
「単体評価編」

by TNB 製作所-1-

T-TestCase 「単体評価編」

はじめに

昔、C やアセンブラで BIOS や DOS（パッチや DOS コール）を書いている時、テストと言えば「机上チェック」、「実際動作を見る」と言うものでした。
しかし、実際は本番の形として組み立てる前に、必ずと言っていいほど普通のアプリの形で個々の部品（関数）の動作確認をしていました。本番の形にするとデバッグ環境がほとんど皆無だったからです。

最近、Visual C++を使った大きなソフト開発に携わっています。規模が大きいので当然個々の部品（主にクラス）の数も多くなります。

その個々の部品のテストはどうしているのでしょうか？

昔に比べ、今は開発環境がかなりよくなっています。ソースコードデバッガもあたりまえです。
そのせいなのか、単体テストはあまり実施されない傾向があります。実際、結合テストにより、単体テストもある程度出来てしまいます。しかし、単体テストで簡単にバグになる内容も、結合テストのやり方によっては発生せず、逆にシステムテストで発見される、というケースも稀ではありません。

では、なぜ単体テストを実施しないのでしょうか？

私が単体テストを省略した時のことを思い返すと、「小規模だから大丈夫」「結合して流したらわかる」｢自信がある｣「テストプログラムを作るのが面倒」という感じでした。

「小規模だから大丈夫」。確かに単純なクラスであれば、ミスの出しようが無いかも知れません。

「結合して流したらわかる」。クラスは結合して使うのが目的なので、結合対象の限られているクラスは実際に結合したほうが手っ取り早いかもしれません。

「自信がある」。そういう時もあるかもしれません。

-2-

T-TestCase 「単体評価編」「テストプログラムを作るのが面倒」。単体テストをしない理由、結局はこれが原因ではないでしょうか。単体テストのプログラムはプロジェクトで規定されているケースは多くありません。

◆

本書で扱っている「T-TestCase システム」というものは、この単体テストの書き方を規定する意味で用意しました。T-TestCase で提供されるクラス自体は Java 用の「JUnit」と言うものを参考に設計されています。

このシステムは、単体テストを「自動化」してくれるものですが、単体テスト項目を自動的に作成してくれるわけではありません。やはり、「実装クラスのコード」と「単体テスト用のコード」の二種類のコードを書く必要があります。しかも、ほとんどの場合、「実装クラスのコード」より「単体テスト用のコード」のほうがコードが大きくなります。
そこで、T-TestCase では、この「単体テスト用のコード」の書き方にルールを作り、さらにサポートする関数を用意することで、「単体テストを行いやすくすること」と「単体テストの管理」
を目的にしているのです。

◆

T-TestCase を使用するにあたり、「実装クラスのコード」に対するコーディングルールも多少含まれております。しかし、それは本システムを使うための規約、というわけではなく、少しでもバグを減らすための書き方と言うことが出来ます。

本システムは、まだまだ改善の余地は残っているかもしれません。ただ、このシステムを使うことで、単体テストに対する意識が強くなることを期待しています。

2004/9 小松-3-

T-TestCase 「単体評価編」

目次

1

T-TestCase「単体評価」とは？ ..................................................................................... 6

2

使用する前に................................................................................................................... 9
2.1

環境 ........................................................................................................................ 10

2.2

用語 .........................................................................................................................11

2.2.1「T-TestCase」 ...............................................................................................112.2.2

実装クラス（実装プログラム） ......................................................................11

2.2.3

テストクラス（テストプログラム）...............................................................11

2.2.4

テストケース（テスト関数）..........................................................................11

2.2.5

テスト内容（評価内容文） .............................................................................112.2.6

テスト式（評価式） ....................................................................................... 12

2.2.7

テストシナリオ............................................................................................... 12

2.3

2.3.1

テストプログラムを書く手間が増える（Demerit）........................................ 13

2.3.2

実装プログラムの設計が洗練される（Merit） ............................................. 14

2.3.3

テストプログラムの書き方が統一できる（Merit）...................................... 14

2.3.4

テストの結果を自動集計できる（Merit） .................................................... 15

2.3.5

テスト仕様書、成績書を自動生成できる（Merit）...................................... 16

2.3.6

テストプログラムの管理が統一できる（Merit） ......................................... 17

2.3.7

一つのテストでどのルートを通ったか確認できる（Merit） ....................... 17

2.4

3「T-TestCase」のコーディング規約..................................................................... 18

2.4.1

テスト対象クラスの役割を一つに ................................................................. 18

2.4.2

UI 部とデータ部は分離する........................................................................... 18

2.4.3

テストする実装クラスに friend でテストクラスを追加............................... 19

2.4.4

テストクラスのコメントは細かく ................................................................. 19

2.4.5

テストクラスは、基本的にヘッダにすべて書く............................................ 20

2.4.6

テストクラスに多重継承を使う場合順番に注意............................................ 21「T-TestCase」の構造 .................................................................................................. 22
3.1

4

使用するメリット .................................................................................................. 13「T-TestCase」のクラス ....................................................................................... 23

3.1.1「T-TestCase」に含まれるクラス ................................................................. 23

3.1.2

CTestCase クラス .......................................................................................... 24

3.1.3

CTestCaseSuite クラス.................................................................................. 25

3.1.4

CTestCaseView クラス .................................................................................. 26

使用手順 ........................................................................................................................ 27-4-

T-TestCase 「単体評価編」
4.1

4.1.1

テストケース宣言 ........................................................................................... 28

4.1.2

テスト説明宣言............................................................................................... 29

4.1.3

テスト評価宣言............................................................................................... 30

4.1.4

その他 ............................................................................................................. 32

4.2「T-TestCase」の実行........................................................................................... 33

4.2.1

Suite クラスに登録 ........................................................................................ 33

4.2.2

手っ取り早くテスト ....................................................................................... 33

4.3「T-TestCase」を使用した開発 ............................................................................ 34

4.3.1

仮設計 ............................................................................................................. 35

4.3.2

でっち上げ（空関数の作成）......................................................................... 36

4.3.3

テスト枠（空テストクラス作成） ................................................................. 374.3.4

関数仕様（テストクラス作成） ..................................................................... 39

4.3.5

再検討（リファクタリング）......................................................................... 44

4.3.6

実装～完成（Red～Green） .......................................................................... 46

4.3.7

他の手順例...................................................................................................... 47

4.4

5

CTestCase のメンバ関数・マクロ関数................................................................. 28「T-TestCase」の限界........................................................................................... 48

4.4.1

UI 部の単体テストは不可能........................................................................... 48

4.4.2

情報を外部から与えることは不可能.............................................................. 48

4.4.3

テストクラス内に virtual 関数は禁止 ........................................................... 48

4.4.4

実動作中のテストは非対象 ............................................................................ 48

4.4.5

デストラクタの動作確認は未対応 ................................................................. 49

その他............................................................................................................................ 50
5.1

拡張性 .................................................................................................................... 51

5.1.1

出力先、フォーマットの変更......................................................................... 51

5.1.2

LOG への書き出し＆ビュワーの使用 ............................................................ 54-5-

T-TestCase 「単体評価編」

1

T-TestCase「「単体評価」
単体評価」とは？
とは？

-6-

T-TestCase 「単体評価編」

T-TestCase「単体評価」とは、Microsoft Visual C++用の「単体テスト用のフレームワーク」のことです。
記述方法は単純で、T-TestCase に含まれる CTestCase というクラスを継承した、テストシナ
リオのクラスを作成するだけです。

以下は、CPatientInfo
CPatientInfo と言う実装クラスをテストするクラスです。

class CPatientInfoTest : public CTestCase{
TC_DECLARATION_CAPTION("患者情報クラスのテスト");

CTestCase を 継
承します
テストクラスの内容を記述します。

CPatientInfo m_cPatInfo;
void SetUp( void ){必要に応じてメンバを用//テスト前準備（クラスやメモリ確保）
意できます。
}
void TearDownt( void ){テストケースを用意します。関数//後片付け（開放など）
宣言がちょっと特殊になります。
}
TC_DECLARATION_CASE( Test_SetData ){TC_CAPTION_CASE( "SetData の処理確認" );TC_CAPTION_TEST( "患者名の頭に SP がある場合" );テストケース
m_cPatInfo.SetData( PI_NAME, " Abcdefg" );の説明を記述TC_EQUAL( m_cPatInfo.GetString(PI_NAME), "Abcdefg" );します。
TC_CAPTION_TEST( "患者名の後に SP がある場合" );m_cPatInfo.SetData( PI_NAME, "Abcdefg " );TC_EQUAL( m_cPatInfo.GetString(PI_NAME), "Abcdefg" );テスト内容を};記述します。
TC_DECLARATION_CASE( Test_GetString )この関数の二つの引数が{一致しない場合(false のTC_CAPTION_CASE( "GetString の処理確認" );場合)、Assert(主張)しま…略…
TC_DECLARATION_END

テストケースの終端の意です。

す。また、false の数もカウントしています。

};

細かくは吹き出し内を参照してください。見てのとおり、（マクロ関数が入っていますが）普通にクラスを作っているだけです。
テストケースは TC_DECLARATION_CASE()マクロ関数で追加したメンバ関数に記述していきます。

CTestCase クラスは、評価用のマクロ関数（大きく TC_EQUAL()、TC_TRUE()の 2 種）と Run()と言うメンバ関数を持っています。

-7-

T-TestCase 「単体評価編」

評価用マクロ関数は単純に、「結果が偽なら表示してカウントする」と言うものです。そしてRun()は、そのクラス内のテストケースを全部実行する、というものです。

void foo(){
CPatientInfoTest cPatTest;
cPatTest.Run();//全テストケース実行}

作成したクラスを上記のように実行すると、以下のような結果が表示されます（デフォルトでは、Visual Studio のアウトプット＜デバッグ＞のところに表示されます）。

>>>>CPatientInfoTest[患者情報クラスのテスト]-----患者名（first）確認
D:\Home\Private\TnbCmn\TestCase.cpp(315):
cPatient.a == 5-> 10ms-----患者名（Mid）確認
D:\Home\Private\TnbCmn\TestCase.cpp(326):
cPatient.strName == _T("Hide")** 'cPatient.strName == _T("Hide")' は true になりませんでした。
-> 20ms-----患者名（Last）確認
D:\Home\Private\TnbCmn\TestCase.cpp(332):
cPatient.a == 8** 'cPatient.a' は '8' になりませんでした。
** 例外が発生しました>>Case>>Exception>>Failures3
1
2

各テストケース内で真にならなかった数（=Failures）、例外処理になった数（=Exception）もカウントし、最後に表示しています。
評価対象の変数（関数）と期待結果が一致するかどうかを表示する、実は、T-TestCase の機
能はたったこれだけなのです。

-8-

T-TestCase 「単体評価編」

2

使用する
使用する前
する前に

本章では、T-TestCase の「単体評価」機能を使う前に、用意すること、意識することを記述しています。

既に認識している場合、使用手順から参照してください。

-9-T-TestCase 「単体評価編」

2.1

環境

T-TestCase システムは、Microsoft Visual C++ Ver. 6.0、Microsoft Visual Studio.NET 2003 での動作を確認しています。

- 10 -

T-TestCase 「単体評価編」

2.2

用語

本書で書かれている一部の言葉は、一般的ではないかもしれません。本項で、一通り記述しておきます。

2.2.1「T-TestCase」

本書において、斜体の「T-TestCase」は、本 T-TestCase システムのことを指します。それには、CTestCase クラス、および、単体テストのコーディングルールが含まれます。T-TestCase システム
自体には、カバレッジ機能も含まれていますが、本書で「T-TestCase」と書かれているものは「単体評価」機能と捉えてください。

2.2.2

実装クラス（実装プログラム）

製品など実際の動きのために作成するプログラムのことを指します。
→【対】テストクラス

2.2.3

テストクラス（テストプログラム）

実装クラスをテストするためのクラスです。製品などには含まれないプログラムのことを指します。

T-TestCase の CTestCase クラスを継承して作るのはこのテストクラスです。
→【対】実装クラス

2.2.4

テストケース（テスト関数）

テストクラス内に TC_DECLARATION_CASE()マクロ関数で宣言したメンバ関数のことを指します。またテストケースの説明を、TC_CAPTION_CASE()マクロ関数で指定します。
テストクラス内に、複数含まれます。

2.2.5

テスト内容（評価内容文）

テストケース内で、テストする個々の内容を指します。日本語の説明を TC_CAPTION_TEST()マクロ関数で指定します。
テストケース内に、複数含むことが出来ます。

- 11 -

T-TestCase 「単体評価編」

2.2.6

テスト式（評価式）

テスト内容内で、実際に評価する式です。評価用マクロ関数、TC_EQUAL()、TC_TRUE()などを使い、指定します。
テスト内容に、複数含むことが出来ます。

テストクラス～テスト式を図にすると以下のような関係になります。

→

テスト内容→

テスト式

テストケース→

→

テスト式

→

テスト内容→

テスト式

→

テスト式

テスト内容→

テスト式

テストケース→

→

テスト式

→

テスト内容→

テスト式

→

テスト式

テストクラス→

2.2.7

テストシナリオ

テストケースは、並べた順に実行されることから、「テストのシナリオ」と呼ぶことがあります。

- 12 -

T-TestCase 「単体評価編」

2.3

使用する
使用するメリット
するメリットT-TestCase の適用、および、規約を適用する Merit、および、Demerit は、以下のようなものがありま
す。

2.3.1

テストプログラムを書く手間が増える（Demerit）

実装クラス（実装プログラム）以外に、そのプログラムをチェックするプログラムを書く必要があります。ですので、ほとんどの場合、プログラム
プログラムのプログラムの量は 2 倍以上になります
倍以上になります。
になります

実装クラスの関数一つに付き、テストは数パターンあるはずです。

実装クラス
class CXyz{
void aa(a1);void bb(a2);void cc(a3);void dd(a4);};- 13 -

テストクラスclass CXyzTest{
void xx();
void xx();
void xx();
void xx();
void xx();
void xx();
void xx();
void xx();
void xx();
void xx();
void xx();
void xx();
void xx();
void xx();
void xx();
void xx();
void xx();
void xx();
void xx();
void xx();
void xx();};

T-TestCase 「単体評価編」

2.3.2

実装プログラムの設計が洗練される（Merit）

T-TestCase を使う場合、原則（後述）設計と共にテストプログラムを記述していきます（設計からMT まで使用することを推奨しています）。
このテストプログラムは、「実装クラスのメンバ関数にある引数を与えると、戻り値はこうなる」という、いわゆる In と Out を表す一種の要求仕様（モジュール仕様）となっていきます。
つまり、クラスの設計と共にテストプログラムを書くことで、実装クラスに必要なもの、要求されるものが同時に洗い出すことが出来ます。
結果、クラスを使使う側の立場も立場も同時に同時に考えながら、
えながら、より使より使いやすいクラス
いやすいクラスのクラスの設計が設計が必然的に必然的に行えます。
えます

2.3.3

テストプログラムの書き方が統一できる（Merit）

テストプログラムは、必ず書くべきものです。しかし、今まで、ガイドライン的なものが無いため、各自がそれぞれで作成していました。ものによっては、ちょっと凝った GUI のものであったり、単純に、Main 関数のみの Exe であったり、それぞれの使用方法も結果表示方法もそれぞれまちまちであり、統一性がありませんでした。後に第 3 者が再利用出来ないケースも多々ありました。

T-TestCase では、CTestCase クラスを継承し作成することで、テストプログラム
テストプログラム（
テストプログラム（テストクラス）
テストクラス）の使用方法、
使用方法、結果表示を
結果表示を統一できます
統一できます。
できます- 14 -

T-TestCase 「単体評価編」

2.3.4

テストの結果を自動集計できる（Merit）

今まで、各自作成したテストプログラムには、「値を表示し、人間が目視して、OK／NG の判断をする」ものが多く存在します。しかし、これでは第 3 者がテストプログラムを使用することは出来ません。
ですので、テスト内容として「ある関数の結果を表示する」ではなく、必ず「ある
ある関数
ある関数の
関数の結果は
結果は、
こういう値
人間は
こういう値になる」というように記述します。そうすることで、人間になる
人間は、最後の最後の集計された
集計された OK／
OK／NGの数を見るだけでよいことになります。
るだけでよい1;X=1
2; X=23cm

Test Program
void xx(){…;}
void xx(){…;}
void xx(){…;

テスト結果として

3; X=Pat

値を表示

4; X=1

どんな値になれば正しいか、知っている人が見ないとわからない。

5; X=5.6
6; X=5.32

1;Ok
2;Ok
テスト結果として

3;Ok

内容と OK,NG を表示

4;X は 34 でな
い、Bad
5;Ok
NG 一つ

- 15 -

だれがみても正否が一目瞭然。

T-TestCase 「単体評価編」

2.3.5

テスト仕様書、成績書を自動生成できる（Merit）

MT 書（単体テスト）や IT 書（結合テスト）を作成したことはありますか？

一度は書いたことがあると思いますが、非常に面倒に感じたはずです。
特にテストプログラムと MT 書は対になっているはずです。プログラムとドキュメント、同じ内容のものを二つ書く、という行為が面倒に感じる要因だと思います。

T-TestCase ではテスト内容とテスト結果を同時にプログラム内に書いていきます。そして、その内容は CTestCase クラスに渡され、テスト内容と結果が表示されるようになっているのです。
つまり、このこの自動
この自動に自動に実行され実行され表示され表示される表示される内容
される内容は
内容は、MT 仕様書であり仕様書であり、であり、成績書にすることが出来るの
成績書
です。

現在
Test Program
void xx()
1;………
{
…;
2;… ……
}
仕様書に沿って3;…………
TestProgram はvoid xx(){
作られる。
4;…… …
…;}
5;………
void xx(){
6;……… …
…;テスト仕様書Document

1;X=1

テスト成績書2; X=23cm

1;Ok

3; X=Pat4; X=1
5; X=5.6

プログラム

2;Ok

表示を見て
成績書を 3;Ok
記入する。

4;Ok

6; X=5.32

5;Bad

7; X=1.6

6;Ok

実行(表示)

Document

TestCase 使用
Test Program
void xx(){…;}
作成するのはvoid xx()
Program だけ{…;}
void xx()
{
…;

プログラム

1;Ok

テスト仕様書

テスト成績書

2;Ok

1;………

1;Ok

3;Ok

2;… ……

2;Ok

4;Ok

3;…………

3;Ok

5;Bad

4;…… …

4;Ok

6;Ok

5;………

5;Bad

7;Ok

6;……… …

6;Ok

実行(表示)- 16 -

自動生成

T-TestCase 「単体評価編」

2.3.6

テストプログラムの管理が統一できる（Merit）

T-TestCase の場合、実装プログラムにテストプログラムのための専用コードは含まれません（一つ、friend 宣言を入れますが、バイナリとしては 1byte もありません）。
そのため、テストプログラムは実装プログラムといっしょに管理する必要はありませんが、T-TestCaseでは、実装プログラムを作成中（作成前）にテストプログラムを作成しますので、一緒に管理することをを推奨しています（プロジェクト毎に管理フォルダを規約として用意してください）。
こうすることで、プログラムを変更時、すぐにテストプログラムを更新し、テストを行うことが出来ます。

添付されている TcTest プロジェクトは、TcTest フォルダにテストプログラム（ヘッダ）をいれ、
TestSuite.h に追記するだけで、テストケース環境が出来るようなテンプレートになっています。
このテンプレートを開発のプロジェクトに入れ、リンク先、出力先をプロジェクトに合わせ、テスト
プログラムを追加していってください。
サンプルとして添付されている PslCmn プロジェクトの形を参考にしてください（PslCmn.dll が実装
プログラムになります）
。

2.3.7

一つのテストでどのルートを通ったか確認できる（Merit）

T-TestCase の場合、単体評価機能だけでなく、コードカバレッジ機能も持っています。このコードカバ
レッジ機能を使用して単体評価を行い、専用ビュワーで見ると、一つ一つのテストを実行するのにどのルートを通ったかすぐに確認することができます。これにより、テストに対して期待する処理が呼ばれているか、すぐにわかります。
基本的に、単体評価の対象になるクラス（関数）の全ルートを通るようにテストケースを書くべきです。

- 17 -

T-TestCase 「単体評価編」

2.4「T-TestCase」」のコーディング規約
コーディング規約

T-TestCase「単体評価」機能を使用する場合、実装プログラムのクラス設計時や作成時にいくつか考慮する必要があります。T-TestCase「単体評価」機能を使用して開発する場合の手順に関しては後述しています。ここでは、いわゆるコーディング規約を箇条書きしておきます。

2.4.1

テスト対象クラスの役割を一つに

C++のコーディング規約でも触れていますが、一つのクラスは一つの機能で設計します。こうする
ことで、テストクラスのテスト内容も複雑になりません。実装クラスが複雑であればあるほど、テスト項目が 2 乗 3 乗と増える可能性があります。
（T-TestCase を使わない場合でも、こうするべきです。
）

2.4.2

UI 部とデータ部は分離する

T-TestCase は、UI 部の開発、及び、テストには向きません。元々自動テスト用のフレームワーク
ですので、UI 部は対象外と言えます。しかし、UI 部で扱う情報に関しては（あたりまえですが）テ
ストしたいところです。
対応は容易です。UI 設計時、「UI は画面遷移とデータ入出力のみ」とし、表示するデータはすべて下位のクラスで管理するようにするのです（本来そうしておくのが、あたりまえなのですが…）。
UI は下位のクラスからもらった文字列をそのまま表示するだけ。入力された文字を下位のクラスに渡すだけ、こうすることで、情報を扱っている下位クラスの単体テストをこの T-TestCase で行う
ことが容易になるのです。
（T-TestCase を使わない場合でも、こうするべきです。
）- 18 -

T-TestCase 「単体評価編」

2.4.3

テストする実装クラスに friend でテストクラスを追加

テスト対象になるクラス宣言に、テストクラスを friend で宣言しておくようにします。例えば、
CPatientInfo であれば、宣言の末に「friend class CPatientInfoTest;」と追記しておきます。
こうすることで、T-TestCase を使ったテストクラスで、テスト対象クラス、すべてのメンバ変数の確認が可能になります。

T-TestCase において、テストのために、実装プログラムに手を入れなければならないのは、この行のみです。friend 宣言のみなので、実行コードにはまったく影響を及ぼしません。

class CPatientInfo{
private:int
m_iId;
public:bool SetData(～);…略…
friend class CPatientInfoTest
CPatientInfoTest;
Test};

このように、
テストクラスを追加しておきます。

何らかの理由で、テストクラスを作成しなかった場合でも、この宣言は入れておくようにしてくだ
さい。

2.4.4

テストクラスのコメントは細かく

C++のコーディング規約でも触れていますが、実装プログラムの場合、あまりコメントをプログラ
ム内に書くべきではありません。しかし、それに対し、テストプログラムは、なぜこのような値になるのか、プログラムに現れないことは、コメントで記述するようにします。
テスト内容に関しては簡潔に Caption で記述するようにします。

- 19 -

T-TestCase 「単体評価編」

2.4.5

テストクラスは、基本的にヘッダにすべて書く

テストクラスはすべてヘッダに書くようにします（メンバ関数も inline でヘッダ記述）。そうすることで、テストクラスはファイルひとつ移動するだけでどこでも利用可能になり、管理が容易になります。
テストケースですが、TC_DECLARATION_CASE() の直下に実体を書く以外に以下のようにクラス宣言の後に書くことも可能です（void xx(void)と言う型になります）。また、テストケース以外のメンバ関数も書くことが出来ます。テストで何度も使う構文はメンバ関数化しておきましょう。

class CPatientInfoTest : public CTestCase{…略…
CString GetIdString( int iId );TC_DECLARATION_CASE( TestNameTopSp );TC_DECLARATION_CASE( TestNameBottomSp );…略…
};

テストメソッドで使う構文を関数分けしました。

inline CString CPatientInfoTest::GetIdString( int iId ){…略…
}
inline void CPatientInfoTest::TestNameTopSp( void ){TC_CAPTION_CASE( "患者名の頭に SP がある場合、先頭の SP は登録されない" );m_cPatInfo.SetData( PI_NAME, " Abcdefg" );TC_EQUAL( GetIdString(PI_NAME), "Abcdefg" );};inline void CPatientInfoTest::TestNameBottomSp( void ){TC_CAPTION_CASE( "患者名の末に SP がある場合、末の SP は登録されない" );m_cPatInfo.SetData( PI_NAME, "Abcdefg " );TC_EQUAL( GetIdString(PI_NAME), "Abcdefg" );};- 20 -

T-TestCase 「単体評価編」

2.4.6

テストクラスに多重継承を使う場合順番に注意

抽象クラスのテストなど、 CTestCase と共にもう一つ継承する必要がある場合、必ず先にCTestCase を継承するようにしてください。これを誤ると、例外が発生することもあります。

class CSirialTest : public CTestCase, public CSirial{…略…
};
CTestCase を一番最初に継承させる。

- 21 -

T-TestCase 「単体評価編」

3「T-TestCase」」の構造

本章では、T-TestCase で提供されるクラスの構造の説明を記述しています。

- 22 -

T-TestCase 「単体評価編」

3.1

3.1.1

「T-TestCase」」のクラス「T-TestCase」に含まれるクラス

T-TestCase には大きく３つのクラスがあります。

*斜体は virtual 宣言の意

- 23 -

T-TestCase 「単体評価編」

3.1.2

CTestCase クラス

CTestCase クラスは、本 T-TestCase の心臓部のクラスです。これを継承しテストクラスを作成します。そのテストクラスにテストケースを実装します（下図の「CUserClassTest」の TestMethod~()。
いくつでも用意可能）。そして、スーパークラスの CTestCase の Run()を実行すると、テストケースが順次実行されていきます。

製品に使うクラスです。

実装クラスを評価する
テストメソッドを用意します。

- 24 -

T-TestCase 「単体評価編」

3.1.3

CTestCaseSuite クラス

CTestCaseSuite クラスは、CTestCase クラスのサブクラスを複数コレクションし、連続して実行することが出来ます。単にコレクトしているテストを行うだけでなく、最後に集計を表示することが出来ます。

- 25 -

T-TestCase 「単体評価編」

3.1.4

CTestCaseView クラス

CTestCaseView クラスは、CTestCase クラスから情報種類と文字列を受け、表示するクラスです。
本クラスは、

デバグステップ実行時；Win32API の OutputDebugString()を使用し文字列を出力。

通常実行時；LOG ファイルに書き出し、最後にビュワー(TcViewer)を起動。

と言う動きをします。TcViewer を使うと、コードカバレッジとの連携や、MT ドキュメント生成などが出来ます。

また、本クラスを継承し、新たに出力先、フォーマットを用意することも出来ます。

- 26 -

T-TestCase 「単体評価編」

4

使用手順

本章では、
「T-TestCase」の具体的な使い方を記述しています。すでにインストールされている環境をターゲットにしています。

- 27 -

T-TestCase 「単体評価編」

4.1

CTestCase のメンバ関数
メンバ関数・
関数・マクロ関数
マクロ関数

テストクラスを作る時、CTestCase を継承して作成します。その際使用するメンバ関数、およびマクロ関数は以下のものがあります。

4.1.1

テストケース宣言

以下は、CTestCase クラスを継承したサブクラスにテストケースを登録するためのものです。

関数/マクロ関数/マクロ名内容

macro

TC_DECLARATION_CAPTION(文字列)

テストクラスの説明を指定します。テストク
ラスのコンストラクタの最初に記述します。

Ver1.20 より、新規

日本語の記述が可能です。
TC_CAPTION_CLASS()はコンストラクタで使用しますが、本マクロはクラス宣言直下に記
述します。

macro

TC_DECLARATION_CASE(関数名)テストケースの宣言に使用します。通常の関数名と同じく、英数字のみ使用可能です。関
数名はユニークであれば、何でもかまいませ
ん、

macro

TC_DECLARATION_END

テストケースの終端をあらわします。かならずテストクラスの最後に一つ記述します。

- 28 -

T-TestCase 「単体評価編」

4.1.2

テスト説明宣言

以下は、テストクラスの説明を記述するために使用します。

関数/マクロ関数/マクロ名内容

macro

TC_CAPTION_CLASS(文字列)

テストクラスの説明を指定します。テストクラスのVer1.20 より、非推奨になりました。TC_DECLARATION_CAPTION()

コンストラクタの最初に記述します。日本語の記述が可能です。

を使用してください。
macro

TC_CAPTION_CASE(文字列)

テストケースの説明を指定します。各テストケース
内の最初に原則一つ、記述します。日本語の記述が
可能です。

macro

TC_CAPTION_TEST(文字列)評価マクロ関数の TC_TRUE()や TC_EQUAL()などのすぐ上で、これらの補足説明を記述するために使用します。日本語の記述が可能です。

- 29 -

T-TestCase 「単体評価編」

4.1.3

テスト評価宣言

以下は、テストクラスのテストを記述するために使用します。

関 数 /
マクロ
macro

関数/マクロ名内容

TC_EQUAL( 評価元,期待結果 )「評価元」と「期待結果」が一致しているか、評価します。
偽だった場合、Assert します。
「評価元」==「期待結果」と言う判定をするため、
operator == をサポートしているクラス同士なら比較可能です。
範囲チェック評価や、以上、以下など評価が必要な場合、TC_TRUE()を使用します。
文字列の評価は TC_EQUAL_STRING()を使用します（TC_EQUAL()ではポインタの比較になる可能性があります）。

macro

TC_EQUAL_STRING(評価元,期待結果)
上記の「文字列評価」用です。

macro

TC_TRUE( 評価文 )「評価結果」が真か評価します。
偽だった場合、Assert します。== で評価できる の で あ れ ば 、 極 力 TC_EQUAL() 、
TC_EQUAL_STRING()で記述して下さい。
「評価結果」が偽であることを評価する場合､
TC_EQUAL( 評価文,false )というようにして下さ
い。

macro

TC_EXCEPTION( 評価文,期待例外 )評価文を実行し、期待する例外が発生することを評価します。期待通り例外が発生しない場合、Assert します。
「期待例外」は開放する必要のないものを指定してください。また、CException のサブクラスは下記の TC_EXCEPTION_MFC()を使用してください。

Ver1.20 より、新規

macro

TC_EXCEPTION_MFC( 評価文,期待例外 )macro

Ver1.10 までは、TC_EXCEPTION()。
Ver1.20 より、この名前。
TC_ISASSERT( 回数 )Ver1.30 より、新規

macro

評価文を実行し、期待する例外が発生することを評価します。
期待通り例外が発生しない場合、Assert します。
（コードカバレッジコンパイルした時のみ有効になります。通常は常に成功になります）
こ の マ ク ロ は 、 一 つ 前 の テ ス ト で MFC のASSERT マクロ、VERIFY マクロにおいて「失敗」が何回出たかをチェックするマクロです。
「失敗」の数が０か指定の回数なら真になります。
異なる場合、Assert します。

TC_VOID( 評価文 )Ver1.30 より、新規評価文を実行します。結果は常に true とします。
このマクロは、戻り値が void の関数だけど、一連のテストで必要な関数で、LOG にも残したい場
合などに使用します。

- 30 -

T-TestCase 「単体評価編」
補足）これらのマクロ関数を使用すると、評価元、期待結果、評価文を、「テスト式」として文字列化し結果として表示します。

- 31 -

T-TestCase 「単体評価編」

4.1.4

その他

以下は、テスト開始や表示設定、そして開発の支援のために用意された、サービス関数などです。

関数/マクロ関数/マクロ名

内容関数

SetUp()

テストクラスで継承して用意します。テ
スト開始前に呼ばれます。

Ver1.20 より、新規☆テストクラスのメンバの初期化はコ
ンストラクタではなく、この関数を使用
してください。
関数

TearDown()

テストクラスで継承して用意します。テスト終了後に呼ばれます。

Ver1.20 より、新規☆デストラクタで後処理をせずこの関数を使用してください。
関数

Run()

TC_ DECLARATION_CASE()で追加したテス
トケースを順次実行します。

関数

SetViewClass(CTestCaseView *)CTestCaseView のサブクラスのインス
タンスを渡します。サブクラスにより表
示先、フォーマットを任意に変更することが出来ます。

関数

Dump( アドレス、長さ)指定されたアドレスから指定された長さを 16 進数で表示します。

- 32 -

T-TestCase 「単体評価編」

4.2「T-TestCase」」の実行推奨としては、「TcTest プロジェクトテンプレートを使用する」ですが、他に実行する方法をご紹介して
おきます。

4.2.1

Suite クラスに登録

CTestCase クラスを継承したテストクラスを登録し、一括で実行することが出来ます。

{//宣言
CTestCaseSuite cTestCaseSuite;//登録
cTestCaseSuite.AddTestCase( CPatientInfoTest );cTestCaseSuite.AddTestCase( CLabelMasterTest );…略…
//実行
cTestCaseSuite.Suite();}

添付の TcTest プロジェクトはこの方法で実装してあります。

4.2.2

手っ取り早くテスト

手っ取り早く、テストクラスを実行する方法もあります。

例）//宣言
namespace{
CPatientInfoTest s_cPatientInfoTest;
CTestCaseSuite s_cTestCaseSuite(s_cPatientInfoTest);}

VS2003 では、名無しインスタンス指定は、
コンパイラのバグのため、処理されません
のでこう書くようにしてください。
namespace ではなく、static でも OK です。

このように Global に宣言することで、Exe 実行時、あるいは Dll Load 時にテストが実行されます。

- 33 -

T-TestCase 「単体評価編」

4.3「T-TestCase」」を使用した使用した開発した開発

設計＆実装が終わっているクラスに対して自動テストを作成、と言う使用方法も可能ですが、ここでは、T-TestCase を使用してクラスを開発する、テストファーストと言う考え方の手順の一例を挙げていきます。
開発のサイクルとしては、以下の図のようになります。
「T-TestCase」は実装クラスを設計するところ（開始）から使用します。

ここでは例として「CLoopQueue クラス」を作っていきます。

仮設計

リファクタ

完成

開始

Red

Green

開発のサイクル- 34 -

T-TestCase 「単体評価編」

4.3.1

仮設計

実装クラスの仕様を考える方法は、UML を使用するなり、関数一覧を書き出してみるなり、きちんとしたモジュール仕様書を作るなり、特に決まった形はありません。最低限、どのような関数が必要で、どのような動きをするかの洗い出しが出来れば OK です。

この洗い出しを『仮設計』と呼んでおきます。

なぜ『仮』なのでしょうか。この時点で『完璧な設計』が出来れば申し分ないのですが、それがこの時点で出来る自信のある人はいないはずです。現実的には、実装していく上で、関数の不足や引数、
戻り値の変更が必ずと言っていいほど行われています。

この段階での設計に対して、T-TestCase を使用して、より洗練させていくので、ここでは『仮』と呼ぶようにしています。

◎メンバ変数・メンバ関数一覧メモ例
処理速度を期待し、Loop キューを採用。
属性一覧

変数名
m_iQueueSize
m_pcData
m_iReadPos
m_iAddPos型
intchar *
int
int

操作一覧

関数名
CLoopQueue(int iSize)
AddData(char*p, int iSize)
GetData(char *p,int iSize)

内容
キーのサイズキューの実体
データの開始位置
データの追加位置

型
bool
bool

コンストラクタ。引数でサイズ指定。
キューにデータ追加
キューからデータ取得。

＊もう少し詳しく個々のメンバ関数について設計してもよい。

◎UML での設計例- 35 -

T-TestCase 「単体評価編」

4.3.2

でっち上げ（空関数の作成）

仮設計が出来たところで、プロジェクト（DLL）を作り、クラスを一つ作ります（inline クラスならプロジェクトを作る必要はありません）。その際、ヘッダの実装クラス宣言の最後に、実装クラスのテストクラス名を friend 宣言しておきます。こうすることで、private 宣言のメンバ変数もテストの対象にすることが出来ます（この friend 宣言は実行ファイルに影響は一切出ません）。

LoopQueue.hclass CLoopQueue{
private:int
m_iQueueSize; //キューのサイズ
char* m_pcData;/キューの実体
int
m_iReadPos;//データの開始位置
int
m_iAddPos;//データの追加位置
public:CLoopQueue( int iSize );bool AddData( const char* p, int iSize );bool GetData( char* p, int iSize );friend class CLoopQueueTest
CLoopQueueTest;
Test};

このように、テスト
クラスを追加しておきます。

クラスに続きメンバも作成します。中身は実装しません。ただ、コンパイルは通るように戻り値ぐらいは記述します（戻り値は、基本的に「エラー」な値にしておきましょう）。

LoopQueue.cpp
CLoopQueue::CLoopQueue( int iSize ){}bool CLoopQueue::AddData( const char* p, int iSize ){return false;}bool CLoopQueue::GetData( char* p, int iSize ){return false;}

これでクラスのでっちあげの完了です。最低コンパイルが通り、クラスのインスタンスが出来るよ
うになりました。
この時点でも『仮設計』時、気が付かなかったことがあれば直していきます（上記例では、AddDataの第一引数に const をつけました）。

- 36 -

T-TestCase 「単体評価編」

4.3.3

テスト枠（空テストクラス作成）

でっち上げが出来たところで、今度は、この実装クラスをテストするプロジェクト（EXE）を作成します。仕様がはっきりしていないから作れない？

はい。 ここでは、テストクラスの枠だけを作成するのです。

基本的に実装プログラムとは別に専用のテスト EXE プロジェクトを作成します。
このテスト EXE
が実装プログラムの DLL をリンクしてテストを行います（実装プログラムにはテストクラスなど入らないようにします）。
ここでは、添付の TcTest プロジェクトを使用します。このプロジェクトを先の実装プログラムのプロジェクトに追加し、リンク先やインクルード先、出力先を合わせます。

テストクラス、TcTest フォルダ内に、「T-TestCase」に含まれる CTestCase を継承して作成します（環境に関しては、「「T-TestCase」の環境構築」を参照してください）。
CLoopQueue クラスの場合、以下のような形になります。異なるクラスの場合でもアンダーバーの部分以外、同じになります。

LoopQueueTest.h#pragma once#include "TestCase.h "#include "LoopQueue.h "class CLoopQueueTest : public CTestCase{
TC_DECLARATION_CAPTION( "LoopQueue クラスのテスト" );TC_DECLARATION_CASE( XXX ){TC_CASE_CAPTION( "" );};TC_DECLARATION_END};

テストクラスは原則、ヘッダファイルのみに記述する方法を使用してください。後々、扱いが容易になります。テストクラスのソース（ヘッダ）の管理方法は「テストプログラムの管理が統一できる（Merit）」を参考にしてみてください（実際プロジェクトの規約として統一するようにしてください）。

- 37 -

T-TestCase 「単体評価編」

さて、TcTest プロジェクトを開き、TestSuite.h にテストクラスを追加します。

ここまで出来たら、デバッグ実行してみましょう（両方ビルドし、TcTest.exe のほうをデバッ
グ実行します）。以下のようにアウトプットに出力されたら成功です。

…略…
::::TestCase byTNB 製作所

Ver 1.** (Fri Oct 8 09:20:18 2004)[LoopQueue クラスのテスト]>> Case>> Exception>> Failures

バージョンにより表示が異
なります。

1
0
0

…略…

- 38 -

T-TestCase 「単体評価編」

4.3.4

関数仕様（テストクラス作成）

でっち上げ、および、空テストクラスが出来たところで、『仮設計』で考えたそれぞれの関数の詳細を検討します。
テストクラスの TC_DECLARATION_CASE() を使い、実装クラスの各関数のテストケースを用意します。
CLoopQueue クラスの場合、メンバ関数が３つしかないので、まずはテストケースを３つ用意します。名前は特に決まっていませんが、最低限重複しない、わかるような名前をつけましょう。
そして、それぞれに Case の説明を TC _CAPTION_CASE()で記述しておきます。

LoopQueueTest.h#pragma once#include "TestCase.h "#include "LoopQueue.h "class CLoopQueueTest : public CTestCase{
TC_DECLARATION_CLASS( "LoopQueue クラスのテスト" );TC_DECLARATION_CASE( Test_Constractor ){TC_CAPTION_CASE( "コンストラクタ。引数により内部状態を確認" );};TC_DECLARATION_CASE( Test_AddData ){TC_CAPTION_CASE( "キューにデータ追加。サイズにより戻り値、内部状態を確認" );};TC_DECLARATION_CASE( Test_GetData ){TC_CAPTION_CASE( "キューからデータ取得。サイズにより戻り値、内部状態を確認" );};TC_DECLARATION_END};

これらのテストケースに関数の仕様を記述して行きます。もちろん先に、モジュール仕様など、紙や別のドキュメントで記述してからこの作業に入ってもかまいません。

- 39 -

T-TestCase 「単体評価編」

例えば、コンストラクタの仕様をプログラムすると以下のようになります。

…略…
TC_DECLARATION_CASE( Test_Constractor ){TC_CAPTION_CASE( "コンストラクタ。引数により内部状態を確認" );①
TC_CAPTION_TEST( "引数 100 の場合" );CLoopQueue cQueue( 100 );②
TC_EQUAL( cQueue.m_iQueueSize, 100 );TC_TRUE( cQueue.m_pcData != NULL );TC_EQUAL( cQueue.m_iReadPos, 0 );③
TC_EQUAL( cQueue.m_iAddPos, 0 );};…略…

①；これ以降は、コンストラクタの引数に 100 を与えた場合、と言うことを日本語で表しています。
②；メンバの値はこうなる、と言う予想結果を記述しています。この場合、m_iQueueSize はコンストラ
クタの引数と同じになるはずなので、このような書き方になります。
③；②と同じなのですが、TC_TRUE()マクロ関数を使用しています。それは、正しい値が NULL 以外、
つまり、結果が真(true)だからです。

上記例では、「引数 100」についてのみ確認しています。しかし実際には、
「最小値」
「最大値」
「異常値」というように、引数を変更して確認する必要があります。

- 40 -

T-TestCase 「単体評価編」

さて、ここまで出来たら一度実行してみましょう。さっきと同じ様にデバッグ実行することで、テスト結果がアウトプットされます。

…略…
::::TestCase byTNB 製作所

Ver 1.00 (Mon Oct 11 15:39:53 2004)[LoopQueue クラスのテスト]-----コンストラクタ。引数により内部状態を確認
引数 100 の場合
d:\home\private\pslcmn\tnbcmn\testsrc\loopqueuetest.h(20):cQueue.m_iQueueSize == 100** 'cQueue.m_iQueueSize' は '100' になりませんでした
d:\home\private\pslcmn\tnbcmn\testsrc\loopqueuetest.h(21):cQueue.m_pcData != NULL
d:\home\private\pslcmn\tnbcmn\testsrc\loopqueuetest.h(22):cQueue.m_iReadPos == 0** 'cQueue.m_iReadPos' は '0' になりませんでした
d:\home\private\pslcmn\tnbcmn\testsrc\loopqueuetest.h(23):cQueue.m_iAddPos == 0** 'cQueue.m_iAddPos' は '0' になりませんでした-->Time 291s>> Case>> Exception>> Failures

2
0
3

…略…

実装関数はでっち上げられているので、期待通りにならないものがほとんどです。上記の場合、
一つたまたま一致してしまいました。
このように未実装でも、あるいはテスト内容が間違えていても、「OK」になる可能性があります（後述していますが OK の場合でも、テストクラス完成後、および、実装クラス完成後は、必ずコードレビューは必ず行うようにしてください）。

（この時点から、LOG を書き出すようにしても良いのですが、手軽なのでアウトプットを使用しておいたほうが良いと思います。詰めの作業に入った段階で、LOG に出すようにしましょう）- 41 -

T-TestCase 「単体評価編」

この調子で、他のメンバ関数についてもテストを実装していきます。

…略…
TC_DECLARATION_CASE( Test_AddData ){TC_CAPTION_CASE( "キューにデータ追加。引数により内部状態を確認" );TC_CAPTION_TEST( "サイズ 10 の空キューにデータ追加" );CLoopQueue cQueue( 10 );char abBuf[100];
TC_EQUAL( cQueue.AddData( abBuf, 100 ), false );TC_TRUE( cQueue.AddData( abBuf, 5 ) );TC_EQUAL( cQueue.m_iReadPos, 0);
TC_EQUAL( cQueue.m_iAddPos, 5);
TC_CAPTION_TEST( "サイズ 10 に 5Byte 入ったキューにデータ追加" );TC_EQUAL( cQueue.AddData( abBuf, 100 ), false );TC_TRUE( cQueue.AddData( abBuf, 4 ) );TC_EQUAL( cQueue.m_iAddPos, 9 );//TC_CAPTION_TEST( "そこに 8Byte とってからデータ追加(Loop の確認)" );TC_TRUE( cQueue.GetData( abBuf, 8 ) );TC_EQUAL( cQueue.m_iReadPos, 8 );TC_EQUAL( cQueue.m_iAddPos, 9 ); //Get では変わらない
TC_TRUE( cQueue.AddData( abBuf, 4 ) );TC_EQUAL( cQueue.m_iAddPos, ( 9 + 4 ) % 10 );};TC_DECLARATION_CASE( Test_GetData ){TC_CAPTION_CASE( "キューからデータ取得" );TC_CAPTION_TEST( "サイズ 10 の空キューから情報取得" );CLoopQueue cQueue( 10 );char abBuf[100];
TC_EQUAL( cQueue.GetData( abBuf, 3 ), false );//TC_CAPTION_TEST( "サイズ 10 に 5Byte 入ったキューからデータ取得" );cQueue.AddData( abBuf, 5 );TC_EQUAL( cQueue.AddData( abBuf, 100 ), false );TC_TRUE( cQueue.AddData( abBuf, 4 ) );//TC_CAPTION_TEST( "そこに 8Byte 追加し、8byte 取得(Loop の確認)" );cQueue.AddData( abBuf, 8 );TC_TRUE( cQueue.GetData( abBuf, 8 ) );};…略…

この調子で、思いつくだけ書いていきます。
上記ではメンバ関数ごとに分けましたが、GetData()、AddData()は複合的にテストできるので、一つのテストケースにしてしまっても問題ありません。ですが、基本的に一テストケースに付き、一種類の評価のほうがわかりやすいのは確かです。

- 42 -

T-TestCase 「単体評価編」
また、実装クラスのメンバ関数一つに注目したテストケースだけではなく、
Test_GetAndAddData()など複合的なテストケースも用意し、実装クラスの使用方法の幅を広げ
色々な角度からのテストも用意できるといいでしょう。

なお、テストに必要なメンバ関数をテストクラス内に用意しても問題ありません（ただしvirtual 宣言しないでください）。TC_DECLARATION_CASE()を使用せず、追加した普通のメンバ関数は、Run()実行時、実行されません。

さて、この段階では、まだ実装関数の中身が空っぽなので、結果は Failures だらけです。しかし、実装クラスを使うにあたってこれでは足りないなど、仕様の問題点もあがってくるはずです。

- 43 -

T-TestCase 「単体評価編」

4.3.5

再検討（リファクタリング）

テストクラスを作ることで、『仮設計』で洗い出した関数の詳細が、決まったところで、以下の観点から仕様の見直しをします。テストクラスは、実装クラスを実際使っているプログラムです。使いにくい仕様がないか、ここで再検討します。

No

議題

１

足りない引数、余計な引数はな
いか？

詳細

現在の関数の中で意味のない引数はないでしょうか。例えば、一つの引数で表せる値が 2 つの引数に分かれていたり、メンバ変数で持っているものをわざわざ引数で渡すようになっていたりしていな
いでしょうか。あるいは、関数の中で固定にもっていて自由度のない値を引数で渡すなど、考えられないでしょうか。
C++では引数のデフォルトを用意することが出来ます（関数プロトタイプの仮引数に ‘= 100’ などつけられる）。これを使い、普通は設定する必要がない値だけど、変更できると便利、というものを実現できます。

２

足りない機能はないか？
あれば便利な機能はないか？

クラスの保持している情報を使う上で、あれば便利な機能はないでしょうか。あるいは、ないと困る機能はないでしょうか。
想定している以外の使い方が出来てしまわないようにしつつ、このクラスで必須なものがあれば、追加しましょう。

３

冗長な機能はないか？
同じ情報を取り出す機能（関数）が、複数存在していないでしょうか。あれば便利、と思い、追加した機能が冗長になっている可能性もあります。
本クラスと関係のない機能が含まれていないでしょうか。

関数を増やせば増やすほどクラスは複雑になり、テストも複雑になります。
暗黙的なキャストも考慮し、必要な機能、最適な機能を洗い出してください。
４

テスト内容ぬけはないか？
実装クラスだけでなく、テストクラスの内容も確認してください。

- 44 -

T-TestCase 「単体評価編」

LoopQueue クラスでは、以下の修正がはいりました。

No

議題

１

足りない引数、余計な引数はないか？

LoopQueue クラスのクラスの見直し
見直し内容

コンストラクタにおいて、キューのサイズのデフォルトを用意します。これにより、引数なしコンストラクタも使用可
能になります。
２

足りない機能はないか？
あれば便利な機能はないか？

キューに溜まったデータをクリアする方法が GetData()しかありませんでした。メンバ関数を二種追加します。
(EraseData()、EraseAll())
キューに溜まっているデータ長取得関数を用意します。
キューから取り出さずにデータの内容を参照する機能も追加します。

３

冗長な機能はないか？

４

テスト内容ぬけはないか？

－

キューの操作の後、ReadPos や AddPos などを見ていましたが、肝心のデータ内容が確認できていませんでした。

見直したところ、CLoopQueue クラスは、以下のようになりました。

class CLoopQueue{
private:int
m_iQueueSize; //キューのサイズ
char* m_pcData;//キューの実体
int
m_iReadPos;//データの開始位置
int
m_iAddPos;//データの追加位置
int
m_iDataSize;//キューのデータ数
public:CLoopQueue( int iSize = 10000 );~CLoopQueue( void );bool AddData( const char* p, int iSize );bool GetData( char* p, int iSize );char GetAt( int iOffset );//キューの先頭から指定の Offset 位置の char を取得
int GetDataSize( void );//キューのデータのサイズ取得
bool EraseData( int iSize ); //キューの先頭から指定サイズ削除
bool EraseAll( void );//キュー、すべて削除//
friend class CLoopQueueTest;};

ヘッダと共に、空クラスも追加し、テストクラスも更新します。

- 45 -

T-TestCase 「単体評価編」

4.3.6

実装～完成（Red～Green）

さて、仕様もほぼ決まり、テストクラスも出来たところで、いよいよ実装クラスのほうに着手します。

実装する前に T-TestCase を実行すると、最初は Failures が 0 ではないはずです（これを Red 状態と呼びます）。実装クラスのメンバ関数を正しく実装していくことで、この Failures の数がどんどん減っていきます。とりあえず Failures の数を 0 にすることを目的としていきます。0 になったら実装は完了です（Failures が 0 になった状態を Green 状態と呼びます）。

しかし、これでクラス作成終了ではありません。ここでもう一度 4.3.5 再検討（リファクタ）に戻ります。そして、数回「リファクタ
リファクタ」→「RedRed」→「Green
Green」→「リファクタ
リファクタ」→・・・を繰り返し、問
リファクタ
Red
Green
リファクタ
題が無ければ、仮設計で作成したドキュメントを更新して完成完成となります。
完成

また、T-TestCase Ver.1.10 からは、同システムに「コードカバレッジ機能」が追加されました。この機能を併用し、単体テストを実行した時、実装クラスのコードカバレッジが 100％になるように目指しましょう。

（参考）- 46 -

T-TestCase 「単体評価編」

4.3.7

他の手順例

上記に書かれた手順を推奨としますが、開発のサイクルの回し方はいくつかあります。逆にいえば、開発のサイクルが回れば、手順はあまり問題になりません。たとえば、テスト枠（空クラス）生成時に、仕様に問題があれば、即座に再検討に入っても問題ありません。

もう一つ、現実的な話として、一人で設計から実装完成までやることは大きなプロジェクトではあまりありません。設計者と施工者（プログラムする人）と分かれてきます。

T-TestCase を使った場合も、設計者と施工者は分けられます。

設計者は､『仮設計』～『関数仕様』を実施し一度目の『再検討』を行い、続きは施工者に渡す。
『再検討』時に設計者も立ち会う。

という方法や設計者は､『仮設計』を行い、続きは施工者に渡す。
『再検討』時に設計者も立ち会う。

という方法があります。

- 47 -

T-TestCase 「単体評価編」

4.4「T-TestCase」」の限界

本項では、現在の T-TestCase の単体評価機能において、限界や制限をあげておきます。

4.4.1

UI 部の単体テストは不可能

T-TestCase の単体評価機能は、基本的に「自動」のため、テストケース内に UI（人間の操作）を含めないようにします。特に、人間の操作によってテスト結果が変わってくるようなテスト内容では、万人が使用できるテストプログラムにはなりません。
ですので、極力 UI 部とデータ管理部は分けて実装するようにします（UI 部の代わりにテストケースからデータ部に代入することでテストが可能）
。

4.4.2

情報を外部から与えることは不可能

現在、T-TestCase の単体評価機能は、基本的に外部からデータファイルを読み込んだりする機構を用意していません。テストケース内でファイルをオープンしファイル内容を読み込みテストを行うことは容易
ですが、データファイルの有無や内容により、テストが Faliure になる可能性があるため、単体テストとしては不適切になります。

4.4.3

テストクラス内に virtual 関数は禁止

T-TestCase の単体評価機能の仕組みの関係で、テストクラス内に virtual 関数をおく場合、制限があり
ます。

4.4.4

実動作中のテストは非対象

T-TestCase の単体評価機能は、あくまでも、テスト用にクラスを使用するのであり、実際のメイン関数から呼ばれているクラスの動作をモニターすることは出来ません。

- 48 -

T-TestCase 「単体評価編」

4.4.5

デストラクタの動作確認は未対応

C++の動作の関係で、基本的にデストラクタ時の動きは評価が出来ません。しかし、デストラクタ内にコードが多くある場合は評価すべきです。

CPatientInfo::~CPatientInfo(void){
m_iData=0;
delete []m_pBuf;
m_pBuf = NULL;}

その場合、以下のように、関数を分離して実装するように、心がけてください。

void CPatientInfo::m_Delete(void){
m_iData=0;
delete []m_pBuf;
m_pBuf = NULL;}

CPatientInfo::~CPatientInfo(void){
m_Delete();}

上記のようになっていたら、m_Delete()関数の評価を行うことが、デストラクタ時の評価といえるのではないでしょうか。

- 49 -

T-TestCase 「単体評価編」

5

その他
その他

- 50 -

T-TestCase 「単体評価編」

拡張性

5.1

5.1.1

出力先、フォーマットの変更

CTestCase は CTestCaseView クラスを使用して、情報を出力しています。その CTestCase、および、CTestCaseSuite クラスは出力クラスを交換するためのメンバ関数を用意しています（それぞれに SetViewClass()というメンバ関数があります）。

CTestCaseView クラスは以下のように宣言されています。

//出力文字種別
enum ETcMsgType{
TMT_ClassName,
TMT_ClassCaption,
TMT_CaseCaption,
TMT_CaseLine,
TMT_TestCaption,
TMT_TestLine,
TMT_TestSuccess,
TMT_TestFailure,
TMT_ClassResult,
TMT_Etc,};//クラス名前//クラス説明//ケース説明//ケース行//テスト説明//テスト行//テスト結果(成功)//テスト結果(失敗)//クラス結果(集計)//その他

class CTestCaseView{
public:virtual void OutputMessage(LPCTSTR lpAssertMsg,ETcMsgType eTcMsgType);};
詳しくは TestCase.h を参照

OutputMessage()は情報出力時に呼ばれます。その際、同時にどのような情報が出力されるかも表示されます。
この情報を元に、文字整形を行うことで、それぞれのプロジェクトのドキュメント仕様にあわせた出力を作成することが出来ます。

- 51 -

T-TestCase 「単体評価編」

CTestCase のデフォルトでは以下のようになっています。

void CTestCaseView::OutputMessage(
LPCTSTR lpMsg,//[in]表示文字
ETcMsgType eTcMsgType )//[in]表示内容{
CString strTemp;
switch ( eTcMsgType ){case TMT_ClassName://クラス名strTemp.Format( _T(">>>> %s\n"),lpMsg );break;
case TMT_ClassCaption://クラス説明
strTemp.Format( _T("[%s]\n"),lpMsg );break;
case TMT_CaseCaption://ケース説明
strTemp.Format( _T("-----%s\n"),lpMsg );break;
case TMT_CaseLine://ケース行
strTemp.Format( _T("%s\n"),lpMsg );break;
case TMT_TestCaption://テスト説明
strTemp.Format( _T("%s\n"),lpMsg );break;
case TMT_TestLine://テスト行
strTemp.Format( _T("%s\n"),lpMsg );break;
case TMT_TestSuccess://テスト結果(成功)
strTemp = _T("\n");//成功は内容表示しない
break;
case TMT_TestFailure://テスト結果(失敗)
strTemp.Format( _T("** %s\n\n"),lpMsg );break;
case TMT_ClassResult://クラス結果(集計)
strTemp.Format( _T(">> %s\n"),lpMsg );break;
case TMT_Etc://その他
default:
strTemp.Format( _T("%s\n"),lpMsg );break;}::OutputDebugString( strTemp );}TMT_???

情報種により、出力時に飾りをつけ、そして、API の OutputDebugString()へ渡しています。

- 52 -

T-TestCase 「単体評価編」
CTestCaseView のサブクラスを使用する方法は、以下のような方法があります。

// View クラスを継承して用意します。
class CTestCaseViewForUs : public CTestCaseView{
public:virtual void OutputMessage( LPCTSTR lpAssertMsg, ETcMsgType eTcMsgType ){//情報により文字列を操作してある場所に表示するようになっている。
…略…
};
};

//インスタンスを作り、テストクラスに渡します。
{
CTestCaseViewForUs cTestCaseViewForUs;
CPatientInformationTest cPatientInformationTest;//
cPatientInformationTest.SetViewClass( &cTestCaseViewForUs );cPatientInformationTest.Run();}//インスタンスを作り、テストクラス Suite に渡します。
// Suite クラスに登録したテストクラスの出力がすべて指定のものになります。
{
CTestCaseViewForUs cTestCaseViewForUs;
CTestCaseSuite cSuite;//
cSuite.AddTestCase( CPatientInformationTest );cSuite.AddTestCase( CLabelInformationTest );cSuite.SetViewClass( &cTestCaseViewForUs );cSuite.Suite();}//手っ取り早くテストする場合にも使用可能です。
namescpase{
CTestCaseViewForUs cView;
CPatientInformationTest cTest;
CTestCaseSuite cSuite( cTest , &cView );}- 53 -

T-TestCase 「単体評価編」

5.1.2

LOG への書き出し＆ビュワーの使用

T-TestCase では通常実行すると、結果を LOG ファイルに書きだし、TcViewer を起動ます。この TcViewerを使い、更に高度なことが出来るようになります。

カバレッジ
T-TestCase の「カバレッジ」機能は、単体でも動きますが、「単体評価」機能と同時に使うことで、「単体評価」の抜けの確認、および、デバッグの手助けになります。
同梱の「T-TestCase カバレッジ編」を参照ください。

ドキュメント「テスト仕様書、成績書を自動生成できる（Merit）」で書かれている通り、Caption で書いたものを使い、Word を作るためのプラグインが用意されています。
同梱の「T-TestCase カバレッジ編」を参照ください。

- 54 -