ラベル UnitTest の投稿を表示しています。 すべての投稿を表示
ラベル UnitTest の投稿を表示しています。 すべての投稿を表示

2013年2月6日水曜日

CppUnitLite

CppUnit のオリジナル作者である Michael Feathers が作成した CppUnitLite を試してみる。著書「レガシーコード改善ガイド」によると CppUnit よりシンプルに使えることを目指しているとのこと。
  1. Object Mentor Downloads にある CppUnitLite から CppUnitLite.zip をダウンロードする
  2. ダウンロードしたファイルを展開すると StackMain.cpp, StackTest.cpp, Stack.h とCppUnitLite フォルダがある
  3. Makefile を書く
    CC = g++
    STACK_SRC = \
     StackMain.cpp \
     StackTest.cpp
    
    CPPUNITLITE_SRC = \
     CppUnitLite/Test.cpp \
     CppUnitLite/TestResult.cpp \
     CppUnitLite/TestRegistry.cpp \
     CppUnitLite/Failure.cpp \
     CppUnitLite/SimpleString.cpp
    
    default: StackMain.exe
    
    StackMain.exe:
     $(CC) -o $@ $(STACK_SRC) $(CPPUNITLITE_SRC)
    
  4. コンパイル & 実行
    > make
    g++ -o StackMain.exe StackMain.cpp StackTest.cpp CppUnitLite/Test.cpp CppUnitLite/TestResult.cpp CppUnitLite/TestRegistry.cpp CppUnitLite/Failure.cpp CppUnitLite/SimpleString.cpp
    
    > StackMain.exe
    There were no test failures
    

自分でクラスを作ってテストを書いてみる
Example 4. Counter.h
#ifndef __COUNTER_H__
#define __COUNTER_H__

class Counter {
 private:
 int count;

 public:
 Counter();
 ~Counter();
 int get() const;
 void increment();
 void clear();
};

#endif /* __COUNTER_H__ */
Example 5. Counter.cpp
#include "Counter.h"

// Constractor
Counter::Counter()
{
 count = 0;
}

// Destructor
Counter::~Counter()
{

}

// 値を取得
int Counter::get(void) const
{
 return count;
}

void Counter::increment(void)
{
 count++;
}

void Counter::clear(void)
{
 count = 0;
}
Example 6. CounterTest.cpp
#include "CppUnitLite\TestHarness.h"
#include "Counter.h"

#include <string>

SimpleString StringFrom(const std::string& value)
{
 return SimpleString(value.c_str());
}

TEST(Counter, creation)
{
 Counter c;

 LONGS_EQUAL(0, c.get()); // 初期値は 0
 c.increment();
 LONGS_EQUAL(1, c.get()); // +1 されている
 c.clear();
 LONGS_EQUAL(0, c.get()); // clear したら 0 になっている
 LONGS_EQUAL(-1, c.get()); // 失敗例
}

int main(void)
{
 TestResult tr;
 TestRegistry::runAllTests(tr);

 return 0;
}
Example 7. Makefile
CC = g++
CPPUNITLITE_SRC = \
 CppUnitLite/Test.cpp \
 CppUnitLite/TestResult.cpp \
 CppUnitLite/TestRegistry.cpp \
 CppUnitLite/Failure.cpp \
 CppUnitLite/SimpleString.cpp

COUNTER_SRC = \
 Counter.cpp \
 CounterTest.cpp

CounterTest.exe:
 $(CC) -o $@ $(COUNTER_SRC) $(CPPUNITLITE_SRC)

clean:
 rm -rf *.exe
コンパイル & 実行
> make CounterTest.exe
g++ -o CounterTest.exe Counter.cpp CounterTest.cpp CppUnitLite/Test.cpp CppUnitLite/TestResult.cpp CppUnitLite/TestRegistry.cpp CppUnitLite/Failure.cpp CppUnitLite/SimpleString.cpp

> CounterTest.exe
Failure: "expected -1 but was: 0" line 20 in CounterTest.cpp
There were 1 failures

CppUnit

インストール
  1. CppUnit - C++ port of JUnit | Download CppUnit - C++ port of JUnit software for free at SourceForge.net からダウンロードする
  2. パッケージを解凍する
  3. ./configure
  4. make
  5. make check
  6. sudo make install

使い方
テスト対象のクラスを準備する (Counter.cpp, Counter.h)
Example 1. Counter.h
#ifndef COUNTER_H__
#define COUNTER_H__

class Counter {
 private:
 int count;

 public:
 Counter(); // コンストラクタ

 int get() const;
 void incr();
 void clear();
};
#endif
Counter.cpp
#include "Counter.h"

// コンストラクタ
Counter::Counter() {

}

int Counter::get() const {
 return count;
}

void Counter::incr() {
 count++;
}

void Counter::clear() {
 count = 0;
}
Counter クラスをテストするためのクラスを準備する
Example 2. CounterTest.h
#ifndef COUNTER_TEST_H__
#define COUNTER_TEST_H__

#include <cppunit/extensions/HelperMacros.h>
#include "Counter.h"

class CounterTest : public CppUnit::TestFixture {
 CPPUNIT_TEST_SUITE( CounterTest );
 CPPUNIT_TEST(test_init);
 CPPUNIT_TEST(test_incr);
 CPPUNIT_TEST(test_clear);
 CPPUNIT_TEST_SUITE_END();

protected:
  Counter* c;

public:
 void setUp();
 void tearDown();

protected:
 /* テスト */
 void test_init();
 void test_incr(void);
 void test_clear(void);
};

#endif
CounterTest.cpp
#include "CounterTest.h"

// 以下は CounterTest クラスの実装
CPPUNIT_TEST_SUITE_REGISTRATION(CounterTest);

// 各テスト・ケースの実行直前に呼ばれる
void CounterTest::setUp() {
 c = new Counter();
}

// 各テスト・ケースの実行直後に呼ばれる
void CounterTest::tearDown() {
 delete c;
}

// これ以降はテスト・ケースの実装内容
void CounterTest::test_init() {
 CPPUNIT_ASSERT_EQUAL(0, c->get());
}

void CounterTest::test_incr() {
 for (int i = 0; i < 10; i++) {
  c->incr();
  CPPUNIT_ASSERT_EQUAL((i+1), c->get());
 }
}

void CounterTest::test_clear() {
 if (c->get() == 0) {
  c->incr();
 }
 c->clear();
// CPPUNIT_ASSERT_EQUAL(0, c->get());
 // わざとエラーを発生させてみる
 CPPUNIT_ASSERT_EQUAL(1, c->get());
}
実行プログラム本体を準備する (Main.cpp)
Example 3. Main.cpp
#include <cppunit/BriefTestProgressListener.h>
#include <cppunit/CompilerOutputter.h>
#include <cppunit/TextOutputter.h>
#include <cppunit/XmlOutputter.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <cppunit/TestResult.h>
#include <cppunit/TestResultCollector.h>
#include <cppunit/TestRunner.h>
#include <fstream>

int CompilerOutputterVersion(void)
{
 printf("\n\n=== CompilerOutputter ===\n");

 // イベント・マネージャとテスト・コントローラを生成する
 CPPUNIT_NS::TestResult controller;

 // テスト結果収集リスナをコントローラに attach する
 CPPUNIT_NS::TestResultCollector result;
 controller.addListener(&result);

 // 「.」で進行状況を出力するリスナをアタッチする
 CPPUNIT_NS::BriefTestProgressListener progress;
 controller.addListener(&progress);

 // テスト・ランナーにテスト群を与え、テストする
 CPPUNIT_NS::TestRunner runner;
 runner.addTest(CPPUNIT_NS::TestFactoryRegistry::getRegistry().makeTest());
 runner.run(controller);

 // テスト結果を標準出力に吐き出す
 CPPUNIT_NS::CompilerOutputter outputter(&result, CPPUNIT_NS::stdCOut());
 outputter.write();

 return result.wasSuccessful() ? 0 : 1;
}

int TextOutputterVersion(void)
{
 printf("\n\n=== TextOutputter ===\n");

 // イベント・マネージャとテスト・コントローラを生成する
 CPPUNIT_NS::TestResult controller;

 // テスト結果収集リスナをコントローラに attach する
 CPPUNIT_NS::TestResultCollector result;
 controller.addListener(&result);

 // 「.」で進行状況を出力するリスナをアタッチする
 CPPUNIT_NS::BriefTestProgressListener progress;
 controller.addListener(&progress);

 // テスト・ランナーにテスト群を与え、テストする
 CPPUNIT_NS::TestRunner runner;
 runner.addTest(CPPUNIT_NS::TestFactoryRegistry::getRegistry().makeTest());
 runner.run(controller);

 // テスト結果を標準出力に吐き出す
 CPPUNIT_NS::TextOutputter outputter(&result, CPPUNIT_NS::stdCOut());
 outputter.write();

 return result.wasSuccessful() ? 0 : 1;
}

int XmlOutputterVersion(void)
{
 printf("\n\n=== XmlOutputter ===\n");

 // イベント・マネージャとテスト・コントローラを生成する
 CPPUNIT_NS::TestResult controller;

 // テスト結果収集リスナをコントローラに attach する
 CPPUNIT_NS::TestResultCollector result;
 controller.addListener(&result);

 // 「.」で進行状況を出力するリスナをアタッチする
 CPPUNIT_NS::BriefTestProgressListener progress;
 controller.addListener(&progress);

 // テスト・ランナーにテスト群を与え、テストする
 CPPUNIT_NS::TestRunner runner;
 runner.addTest(CPPUNIT_NS::TestFactoryRegistry::getRegistry().makeTest());
 runner.run(controller);

 // テスト結果を result.xml に書き出す
 std::ofstream ofs("result.xml");
 CPPUNIT_NS::XmlOutputter outputter(&result, ofs, "shift_jis");
 outputter.write();

 return result.wasSuccessful() ? 0 : 1;
}

int main(int argc, char* argv[])
{
 CompilerOutputterVersion();
 TextOutputterVersion();
 XmlOutputterVersion();
}

コンパイル
CppUnit のライブラリが置いてある場所を指示するために以下を実行してからコンパイルする。
$ export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
コンパイルオプションに cppunit と dl (動的読み込みライブラリ) をロードするように指定しておく
CMP = g++
CMPOPT = -lcppunit -ldl

Main.out: Counter.cpp Counter.h CounterTest.cpp Main.cpp
 $(CMP) $(CMPOPT) -o $@ Counter.cpp CounterTest.cpp Main.cpp

default:
 Main.out

clean:
 -rm *.out

CUnit

ダウンロード
C Unit Testing Framework - SourceForge.net
使い方
  1. VC++ 2008 EE で libcunit.lib を作成する
  2. CUnit-2.1-0/vc8/libcunit.vcproj を開くと VC++9 用に変換される
  3. libcunit をコンパイルすると libcunit.lib が作成される
  4. libcunit.lib と CUnit-2.1-0/CUnit にある /Headers ディレクトリをテストを行うソースファイルのディレクトリにコピーする
XML で結果を出力
CUnit-2.1.0/Share にある CUnit-List.dtd, CUnit-List.xsl, CUnit-Run.dtd, CUnit-Run.xsl をコピーする


  • CUnit を使う場合は CUnit.h を include する
  • Automated でテストする場合は Automated.h を include する
Test は registory, suite, test から構成され、下記のような配置になる。
Test registory
  +--- suite0
  |       +--- test_001
  |       +--- test_002
  +--- suite1
          +--- test_101
          +--- test_102
#include 
#include "CUnit/Headers/Automated.h"
#include "CUnit/Headers/Cunit.h"

/* テスト対象の関数を定義 */

int func(int a)
{
 return (a + 1);
}

int func_ng(int a)
{
 /* わざとエラーを仕込んだ場合 */
 return (a - 1);
}

/* 各テストを準備 */

void test_001(void)
{
 int ret;

 ret = func(1);

 CU_ASSERT(ret == 2);
}

void test_002(void)
{
 int ret;

 ret = func(2);

 CU_ASSERT(ret == 3);
}

void test_101(void)
{
 int ret;

 ret = func_ng(1 + 3);

 CU_ASSERT(ret == 5);
}

void test_102(void)
{
 int ret;

 ret = func_ng(3 * 5);

 CU_ASSERT(ret == 16);
}
テストを実行する main プログラムを作成する。
  1. CU_initialize_registory() でレジストリを準備
  2. CU_add_suite() でスイートを定義する
  3. 各スイートに CU_add_test() でテストを追加する
int main(void)
{
 CU_pSuite suite0, suite1;

 CU_initialize_registry();
 suite0 = CU_add_suite("suite0", NULL, NULL);
 CU_add_test(suite0, "test_001", test_001);
 CU_add_test(suite0, "test_002", test_002);

 /* こちらはエラーになるように仕込んだ関数のテスト */
 suite1 = CU_add_suite("suite1", NULL, NULL);
 CU_add_test(suite1, "test_101", test_101);
 CU_add_test(suite1, "test_102", test_102);

 /* コンソール版を使う場合はこちら */
 CU_console_run_tests();

 /* 結果を XML 出力する場合はこちら */
 CU_list_tests_to_file();
 CU_automated_run_tests();

 CU_cleanup_registry();

 return 0;
}
  • コンソール版でテストをする場合は CU_console_run_tests() で実行する
  • XML に出力する場合は CU_list_tests_to_file(), CU_automated_run_tests() で実行する
  • 最後に CU_cleanup_registory() で後片付けをする
サンプルソース全体は以下の通り
#include 
#include "CUnit/Headers/Automated.h"
#include "CUnit/Headers/Cunit.h"

/* Test registory
 *   +--- suite0
 *   |       +--- test_001
 *   |       +--- test_002
 *   +--- suite1
 *           +--- test_101
 *           +--- test_102
 */
int func(int a)
{
 return (a + 1);
}

int func_ng(int a)
{
 /* わざとエラーを仕込んだ場合 */
 return (a - 1);
}

void test_001(void)
{
 int ret;

 ret = func(1);

 CU_ASSERT(ret == 2);
}

void test_002(void)
{
 int ret;

 ret = func(2);

 CU_ASSERT(ret == 3);
}

void test_101(void)
{
 int ret;

 ret = func_ng(1 + 3);

 CU_ASSERT(ret == 5);
}

void test_102(void)
{
 int ret;

 ret = func_ng(3 * 5);

 CU_ASSERT(ret == 16);
}

int main(void)
{
 CU_pSuite suite0, suite1;

 CU_initialize_registry();
 suite0 = CU_add_suite("suite0", NULL, NULL);
 CU_add_test(suite0, "test_001", test_001);
 CU_add_test(suite0, "test_002", test_002);

 /* こちらはエラーになるように仕込んだ関数のテスト */
 suite1 = CU_add_suite("suite1", NULL, NULL);
 CU_add_test(suite1, "test_101", test_101);
 CU_add_test(suite1, "test_102", test_102);

 /* コンソール版を使う場合はこちら */
 // CU_console_run_tests();

 /* 結果を XML 出力する場合はこちら */
 CU_list_tests_to_file();
 CU_automated_run_tests();

 CU_cleanup_registry();

 return 0;
}