C/C++単体テストツールCutterにハマる
周囲で「プログラムのテストを綺麗にやろう」という風潮が高まっているので,次のフレームワークを試してみました.
はじめに: Cutterリファレンスマニュアル - Cutter
CにもC++にも対応しているということなので,簡単なサンプルを作りました.
まず,ヘッダファイルとソースファイル.
#ifndef __CALC_H #define __CALC_H #include <iostream> using namespace std; class Calc { public: Calc(); ~Calc(); int add(int a, int b); } #endif #include <Calc.h> Calc::Calc() { cout<<"Calc constructor"<<endl; } Calc::~Calc() { cout<<"Calc destructor"<<endl; } int Calc::add(int a, int b) { return(a+b); }
このサンプルを,共有ライブラリ化します.
> g++ -o lib/Calc.o -c lib/Calc.cc -I./lib > ar crv lib/libCalc.so lib/Calc.o a - lib/Calc.o
ライブラリを呼び出すメインルーチンを作成し,ライブラリとリンクさせて動作確認します.
#include <Calc.h> int main() { Calc *calc = new Calc(); cout<<calc->add(1,2)<<endl; delete calc; return(0); }
> g++ -o src/main src/main.cc -Llib -lCalc -Ilib > ./src/main Calc constructor 3 Calc destructor
確かに,コンストラクタが呼ばれて,足し算して,デストラクタが呼ばれました.
ただし,いちいちメインルーチンを作って目視で動作確認をするのが大変なので,Cutter用のテストプログラムを作ります.
#include <cppcutter.h> #include <Calc.h> void test_Calc() { Calc *calc = new Calc(); cppcut_assert_equal(3, calc->add(1,2)); delete calc; }
Cutterの仕様に従って,このテストプログラムも共有ライブラリ化します.
そしてCutterを通して実行しようとしても...実行されません...
> g++ -o test/test_Calc.so test/test_Calc.cc -I/usr/local/include/cutter -Llib -lCalc -Ilib -L/usr/local/lib -lcppcutter -shared > ls test/ test_Calc.cc test_Calc.so > cutter test/ Finished in 0.000144 seconds (total: 0.000000 seconds) 0 test(s), 0 assertion(s), 0 failure(s), 0 error(s), 0 pending(s), 0 omission(s), 0 notification(s) 0% passed
無視されます.
ここで3日くらい悩みました...
結局分かったことは,CじゃなくてC++の場合は,namespaceでテストルーチンを囲むと出来ました.
#include <cppcutter.h> #include <Calc.h> namespace testCalc { void test_Calc() { Calc *calc = new Calc(); cppcut_assert_equal(3, calc->add(1,2)); delete calc; } }
> g++ -o test/test_Calc.so test/test_Calc.cc -I/usr/local/include/cutter -Llib -lCalc -Ilib -L/usr/local/lib -lcppcutter -shared > cutter test/ Calc constructor Calc destructor . Finished in 0.002072 seconds (total: 0.000615 seconds) 1 test(s), 1 assertion(s), 0 failure(s), 0 error(s), 0 pending(s), 0 omission(s), 0 notification(s) 100% passed
1+2=3になったので,全てのテストにパスしたと出ました.
さらっと終わってしまうと,本当にテストされているのか心配になるのが人情です.
そこで,テストが失敗するであろう例を作ってみます.
#include <Calc.h> Calc::Calc() { cout<<"Calc constructor"<<endl; } Calc::~Calc() { cout<<"Calc destructor"<<endl; } int Calc::add(int a, int b) { return(a+b+1); }
わざとバグを混入です.足し算の結果がちゃんと出るのではなく,足し算の結果に1だけさらに足された結果が出ます.
実行確認+Cutter確認してみますと...
> g++ -o lib/Calc.o -c lib/Calc.cc -I./lib > ar crv lib/libCalc.so lib/Calc.o r - lib/Calc.o > g++ -o src/main src/main.cc -Llib -lCalc -Ilib > ./src/main Calc constructor 4 Calc destructor > g++ -o test/test_Calc.so test/test_Calc.cc -I/usr/local/include/cutter -Llib -lCalc -Ilib -L/usr/local/lib -lcppcutter -shared > cutter test/ Calc constructor F 1) Failure: testCacl::test_Calc <calc->add(1,2) == 4> expected: <3> actual: <4> test/test_Calc.cc:9: void testCacl::test_Calc()(): cppcut_assert_equal(3, calc->add(1,2), ) Finished in 0.003860 seconds (total: 0.001037 seconds) 1 test(s), 0 assertion(s), 1 failure(s), 0 error(s), 0 pending(s), 0 omission(s), 0 notification(s) 0% passed
ちゃんとエラーを検知してくれました.
「1+2=3を期待しているけど,4になってる」って言ってくれてます.