戻る

まだやります。
もう1つクラスを追加したいと思います。今度はいちいち自作スクリプトの吐くコードを確認せずに出来るだけテスト関係の労力を少なくしてやりたいと思いま す。
えーとまずはテストケースを書くのが筋・・・なんですが、コンパイルが通らないことが分かっていながらテストから書くのはやっぱりめんどくさいので、1ク ラス定義、2テストの作成、3テスト(失敗させる)←繰り返し→4実装(完成)という手順で書きたいと思います。
Stackを作ろうと思います。一文字づつスタックを詰んでスタックのサイズは10個にします。今はtestsにいます。

vim ../Stack.h

 1  #ifndef DEFINE_MYTESTCLASS_STACK
2 #define DEFINE_MYTESTCLASS_STACK
3 #include <iostream.h>
4 #define STACKOVERFLOW -1
5 #define STACKUNDERFLOW -2
6 class Stack {
7 private:
8 int _pt;
9 char* _stack;
10
11 public:
12 Stack();
13 ~Stack();
14 void push(char c);
15 char pop();
16 };
17 #endif
と宣言を書きます。んではテストの雛型を作ります。

tclsh createTestCaseTemp.tcl ../Stack.h > StackTest.cpp


で、雛型を開いて編集します。

vim StackTest.cpp

 1  #include "StackTest.h"
2
3 /*
4 @TARGET
5 ../Stack.h
6 */
7
8 /*
9 @PRIVATE
10
11 */
12
13 void StackTest::setUp () {
14 }
15
16 void StackTest::tearDown () {
17 }
18
19 //========================================
20 // Test Cases
21 //========================================
22
23 void StackTest::test_Stack () {
24 CPPUNIT_FAIL("NO TEST CODE");
25 }
26
27 void StackTest::test_~Stack () {
28 CPPUNIT_FAIL("NO TEST CODE");
29 }
30
31 void StackTest::test_push () {
32 CPPUNIT_FAIL("NO TEST CODE");
33 }
34
35 void StackTest::test_pop () {
36 CPPUNIT_FAIL("NO TEST CODE");
37 }
38

編集前↑
編集後↓
  1  #include "StackTest.h"
2
3 /*
4 @TARGET
5 ../Stack.h
6 */
7
8 /*
9 @PRIVATE
10 Stack* stack;
11 */
12
13 void StackTest::setUp () {
14 stack = new Stack;
15 }
16
17 void StackTest::tearDown () {
18 delete stack;
19 }
20
21 //========================================
22 // Test Cases
23 //========================================
24
25 //基本的な操作のテスト
26 void StackTest::test_pushpop () {
27 stack->push('a');
28 stack->push('b');
29 stack->push('c');
30 stack->push('d');
31 stack->push('e');
32 stack->push('f');
33 stack->push('g');
34 stack->push('h');
35 stack->push('i');
36 stack->push('j'); //10個までは追加可能
37 CPPUNIT_ASSERT_EQUAL( 'j', stack->pop() );
38 CPPUNIT_ASSERT_EQUAL( 'i', stack->pop() );
39 CPPUNIT_ASSERT_EQUAL( 'h', stack->pop() );
40 CPPUNIT_ASSERT_EQUAL( 'g', stack->pop() );
41 CPPUNIT_ASSERT_EQUAL( 'f', stack->pop() );
42 CPPUNIT_ASSERT_EQUAL( 'e', stack->pop() );
43 CPPUNIT_ASSERT_EQUAL( 'd', stack->pop() );
44 CPPUNIT_ASSERT_EQUAL( 'c', stack->pop() );
45 CPPUNIT_ASSERT_EQUAL( 'b', stack->pop() );
46 CPPUNIT_ASSERT_EQUAL( 'a', stack->pop() );
47 }
48
49 //基本的な操作のテスト2
50 void StackTest::test_pushpop2 () {
51 stack->push('a');
52 stack->push('b');
53 CPPUNIT_ASSERT_EQUAL( 'b', stack->pop() );
54 stack->push('c');
55 CPPUNIT_ASSERT_EQUAL( 'c', stack->pop() );
56 CPPUNIT_ASSERT_EQUAL( 'a', stack->pop() );
57 }
58
59 //--------------------------------------------
60 //エラーのテスト
61 //--------------------------------------------
62 //オーバーフロー
63 void StackTest::test_overflow () {
64 stack->push('a');
65 stack->push('b');
66 stack->push('c');
67 stack->push('d');
68 stack->push('e');
69 stack->push('f');
70 stack->push('g');
71 stack->push('h');
72 stack->push('i');
73 stack->push('j'); //10個までは追加可能
74 try {
75 stack->push('k');
76 } catch (int err) {
77 CPPUNIT_ASSERT_EQUAL(STACKOVERFLOW, err);
78 return;
79 }
80 CPPUNIT_FAIL("overflowエラーが出ない");
81 }
82
83 //アンダーフロー1
84 void StackTest::test_underflow1 () {
85 try {
86 stack->pop();
87 } catch (int err) {
88 CPPUNIT_ASSERT_EQUAL(STACKUNDERFLOW, err);
89 return;
90 }
91 CPPUNIT_FAIL("underflowエラーが出ない");
92 }
93 //アンダーフロー2
94 void StackTest::test_underflow2 () {
95 stack->push('a');
96 CPPUNIT_ASSERT_EQUAL( 'a',stack->pop() );
97 try {
98 stack->pop();
99 } catch (int err) {
100 CPPUNIT_ASSERT_EQUAL(STACKUNDERFLOW, err);
101 return;
102 }
103 CPPUNIT_FAIL("underflowエラーが出ない");
104 }

ではテストを実行・・するんですが、テストランナーのmakefileは作り直さないといかんので、作り直します。前に作ったCalcTestと今回の StackTestを一緒に実行するようにします。

tclsh createTestRunner.tcl CalcTest StackTest


これでmakefileを作り直しました。あとはnmakeして実行するだけです。test.batを前に作ったのがあるはずなので、testと打てばビ ルドとテスト実行をしてくれるはずです。・・・。が、その前に今までのtest.batだとTestRunnerが必ず起動してしまってたので、コンパイ ルに失敗したときはTestRunnerを実行しないようにしたいと思います。

vim test.bat

1  @ECHO OFF
2 call "C:\Program Files\Microsoft Visual Studio\VC98\Bin\VCVARS32.BAT"
3 nmake -f makefile TestRunner.exe
4 if errorlevel 1 goto END
5 ECHO ######################## TEST RUN ########################
6 TestRunner.exe
7
8 :END

んでは

test


../Stack.cppのビルド方法が指定されていないと言われます。実装してない(というかファイルもまだ無い)ので当たり前ですが・・・。それでは Stack.cppを書きます。

vim ../Stack.cpp

 1  #include "Stack.h"
2
3 Stack::Stack() {
4 _pt = 0;
5 _stack = new char[10];
6 }
7
8 Stack::~Stack() {
9 delete[] _stack;
10 }
11
12 void
13 Stack::push(char c) {
14 }
15
16 char
17 Stack::pop() {
18 return 'a'
19 }

空の実装を書いたところでテストを実行します。vimからだと:!testでテストを実行できます。

test


(テストの実行結果)

.....F.F.F.F.F

!!!FAILURES!!!
Test Results:
Run:  9   Failures: 5   Errors: 0


1) test: StackTest.test_pushpop (F) line: 37 StackTest.cpp
expected: j
but was:  a

2) test: StackTest.test_pushpop2 (F) line: 53 StackTest.cpp
expected: b
but was:  a

3) test: StackTest.test_overflow (F) line: 80 StackTest.cpp
 "overflowエラーが出ない"

4) test: StackTest.test_underflow1 (F) line: 91 StackTest.cpp
 "underflowエラーが出ない"

5) test: StackTest.test_underflow2 (F) line: 103 StackTest.cpp
 "underflowエラーが出ない"



一応テストは全て失敗しているようです。Run:9でFailures:5というのは、CalcTestも一緒に実行するようにmakefileを作った ので、CalcTestの成功分が4つなんですが、ちょっと分かりにくかったかな・・・。まあ1つのテストだけで作ることもできるので今回はこれで我慢と いうことで・・・。そんでは実装を進めます。

 1  #include "Stack.h"
2
3 Stack::Stack() {
4 _pt = 0;
5 _stack = new char[10];
6 }
7
8 Stack::~Stack() {
9 delete[] _stack;
10 }
11
12 void
13 Stack::push(char c) {
14 if(_pt==10) {
15 throw STACKOVERFLOW;
16 } else {
17 _stack[_pt++] = c;
18 }
19 }
20
21 char
22 Stack::pop() {
23 if(_pt==0) {
24 throw STACKUNDERFLOW;
25 } else {
26 return _stack[--_pt];
27 }
28 }

ヘッダの宣言とテストを見ながら書くのちょっとめんどくさいというか、どっちか片方だけの方が楽な気がしてきましたが・・・まあとりあえずテストを通して からリ ファクタリングなんでしょう。テスト=仕様書というXPの考え方は理解できるんですが、実装を進めるときに日本語で書かれた仕様が無いと考えがまとまらな いというか、まあどっかに書くんでしょうが、そういう日本語でまとめた仕様とかコメントは、普通どこに書くべきなんでしょうね?クラス宣言のところに 書くべきか、テストに書くべきか・・・。うーむ・・・まあそれは課題として置いといて・・・

test


(テストの結果)

.........

OK (9 tests)



というわけでテストにも無事通ってStackクラスの完成となりました。


自作スクリプトでは幾つかの決まりごとに沿って書かないといけないということはありますが、テスト関係では、一度makefileを作成したら、テストの 追加や変更はテストの実装部分だけを触ればいいだけなので、まったくCppUnitを意識しなくてよかったと思います。
あと、このスクリプトはあまりコードの解析に融通が利かないので、というか正規表現でパースして出力してるだけの単純なものなので、場合場合で改造してい くことになるでしょう(汗)
ともあれCppUnitを使ってプログラムを書く道筋が大体理解できたと思います。



おわり