14 Oct 2013, 13:28

『Modern C++ with TDD』学習メモ(Chapter2-5)

Modern C++ Programming with Test-Driven Development: Code Better, Sleep

Better

体育の日の祝日なので、図書館とカフェにこもってベンキョしてました。そんな体育の日もオツだよね。というわけで、今日勉強した記録を淡々とメモします。

今日は、『Modern C++ Programming with Test-Driven Development』の前半を読みました。これからあんまし時間がとれなくなる気がするので、後半はお正月休みあたりか。

各章の概略

  • Chapter1・・・ツールの準備について。
  • Chapter2・・・TDDの実践方法をsampleとともに、解説される。ここでは、GoogleMockをつかうものの、中身としてはGoogleTestの使い方解説。TDDを知らない初心者向けに丁寧に書いてある。リファクタリングの方法なども、sampleとともに、体験できる。けっこう優しめで丁寧。
  • Chapter3・・・『レッド・グリーン・リファクタリング』にはじまるTDDの思想や原則、規律について書いてある。Webや書籍ですでにいろいろと言われていることをまとめているだけなので、とりわけ目新しいことはない。
  • Chapter4・・・GoogleMockの細かい使い方など。テスト。フィクスチャ、テストのフィルターやパラメタライズドテストの方法が解説される。HamcrestやASSERT_THATがmodernなのだ!と強調される。また、例外のテストや浮動小数点数のテストなど、細かいTipsとか。
  • Chapter5・・・テスト・ダブルの紹介。ここでついに、GoogleMockが登場。まずは、マニュアルでモックをつくらせて、そのあとにGoogleMockでモックをつくり『ほ~ら、コンナに簡単にできちゃうんだぞ~』とさとされる。cURL、JSONCPPなどのサードパーティをつかって、それらとの依存関係を取り除く方法が紹介される。

Study Memo

以下、新しく知ったことを忘れないためのStudy Memo。なので、自分が知っていることは省略。

ASSERT_EQと ASSERT_THAT

いままで、テスト結果の検証には『ASSERT_EQ』しか利用してこなかったけれども、この本では 『ASSERT_THAT』を利用している。理由は、テストコードの可読性が高まるから。

比較してみる。 『1+1=2』( (1+1) equals to 2 )。

ASSERT_EQ( 2, (1+1) );

これを、ASSERT_THATで書きなおすと以下のようになる。

ASSERT_THAT( (1+1), Eq(2) );

なるほど、英語の文の並びと同じになる。 Assert that 1+1 equals to 2.

ASSERT_EQからASSERT_THATを利用しているところが、まずモダン。

Test Fixtureでのクラス生成はポインタで

クラスをフィクスチャ間で共有するには、どうすればいいか、いままでワカラなかった。こうすればよい。

#include "gmock/gmock.h"
#include "RetweetCollection.h"
#include "Tweet.h"

#include 

using namespace ::testing;
using namespace std

class ARetweetCollectionWithOneTweet: public Test {
public:
   RetweetCollection collection;
   shared_ptr tweet;

   void SetUp() override {
      tweet = shared_ptr(new Tweet("msg", "@user"));
      collection.add(*tweet);
   }
};

TEST_F(ARetweetCollectionWithOneTweet, IgnoresDuplicateTweetAdded) {
   Tweet duplicate(*tweet);
   collection.add(duplicate);

   ASSERT_THAT(collection.size(), Eq(1u));
}

Classの宣言に、shared_ptrを利用して、SetUp()でメモリを動的に獲得する。

Hamcrest Assertion

従来のAssertion(expect_eqとか)では、一つの宣言で一つのことしか検証できなかった。

しかし、モダンなHamcrest Assertionだと、Matcherを利用し、一つの宣言で複数のことを検証できる。

ASSERT_THAT(actual,
    AllOf(StartsWith("al"), EndsWith("ha"), Ne("aloha")));

GoogleMockはC++でHamcrestが利用できるxUnitなので、こういう方法も紹介されている。

Parameterized Test

複数のテストケースをまとめて実行できるというもの。TDDというよりは、スプレッドシートにかかれた仕様を検証するようなときに必要そうなテストの書き方。

#include "gmock/gmock.h"
using namespace ::testing;

struct Sumcase {
  int a, b, expected;
  Sumcase(int anA, int aB, int anExpected)
    : a(anA), b(aB), expected(anExpected) {}
};

class AnAdder: public TestWithParam {
};

class Adder {
public:
  static int sum(int a, int b) {
    return a + b;
  }
};

TEST(AnAdder, GenerateASumFromTwoNumbers) {
  ASSERT_THAT(Adder::sum(1, 1), Eq(2));
}

TEST_P(AnAdder, GenerateLotsOfSumsFromTwoNumbers) {
  Sumcase input = GetParam();
  ASSERT_THAT(Adder::sum(input.a, input.b), Eq(input.expected));
}

Sumcase sums[] = {
  Sumcase(1,1,2),
  Sumcase(1,2,3),
  Sumcase(2,2,4)
};

INSTANTIATE_TEST_CASE_P(BulkTest, AnAdder, ValuesIn(sums));

Test Double、Mock、Stub、Spy、Fake

これは、この本の本題ではないけれども、メモしとく。

  • Test Double・・・テストのために本番用コードと置き換わり、本番用コードをエミュレートするもの
  • Stub・・・ハードコーディングされた値を返すTest Double
  • Spy・・・テストの後の検証のために、オブジェクトに送られた情報をキャッチするTest Double
  • Mock・・・自分で検証機能をもつTest Double
  • Fake・・・本番用クラスを簡易版実装したTestDouble

02 May 2013, 16:41

Visual Studio 2012に GoogleMockを導入したメモ

Visual StudioにGoogleMockを導入する手順も忘れないようにメモしながら入れてみようと思います。GoogleTestはGoogleMockの中に含まれるので、GoogleMockを入れれば、GoogleTestもついてくる。一石二鳥、早起きは三文の得、結構毛だらけ猫灰だらけだ。

導入環境

  • Windows 7
  • Visual Studio Express 2012  for Windows Desktop
  • GoogleMock 1.6.0

GoogleMockをダウンロード

GoogleMockをダウンロードしてきて、適当な場所に置きます。

https://code.google.com/p/googlemock/

Visual StudioでGoogleMockをビルド

Visual StudioでGoogleMockをビルドします。

まずは、msvc/2010配下に移動してフォルダの読み取り専用属性を外します。右クリックから[プロパティ] > [読み取り専用]のチェックを外す。

次に、gmock.slnをVisual Studioで起動します。Visual Studioの変換ウィザードが立ち上がり2010が2012用のプロジェクトに変換されるはず。

上のツールバーから[ビルド] > [ソリューションのビルド]でビルドまたは、F7で。そうすると、エラーが出まくるはず(´・ω・`)

以下のサイトに、解説方法が載っている。感謝しながらヘッダファイルを修正しよう。gmockだけど、gtestのファイルを修正する。

ブログズミ: Visual Studio 11 Beta で Google Test を使う

gmock\gtest\include\gtest\internal\gtest-port.h

ただし、GoogleTestならばこれでうまく行くけれども、GoogleMockは軽薄な奴なので、そうはいかない。追加で修正。

ソリューションエクスプローラに並んでいるプロジェクト(gmock,gmock_main,gmock_test)のプロパティを右クリックで開き、[構成プロパティ] > [C/C++] > [プリプロセッサ] >[プリプロセッサの定義]を選択して、以下のdefineを追加する。

_VARIADIC_MAX=10

image

gmock\msvc\2010\Debug\gmock_test.exeができるので、とりあえず動かしてみて、テストが走ればコンパイル成功。以下のライブラリが作成されます。

  • gmock.lib
  • gmock_main.lib

GoogleMockを使いたいプロジェクトの設定

GoogleMockを使いたいプロジェクトをVisual Studioで起動します。プロジェクトのプロパティを以下のように修正。gtestはgmockに含まれるので、ライブラリは必要ないことに注意。

  • gmockとgtestのインクルードディレクトリを設定 
    • C/C++ -> 全般 -> 追加のインクルードディレクトリ
      • \gmock-1.6.0\include
      • \gmock-1.6.0\gtest\include
  • gmock ライブラリを追加(gtestライブラリの代わり)
    • リンカー -> 全般 -> 追加のライブラリディレクトリ
      • \gmock-1.6.0\msvc\2010\Debug
    • リンカー-> 入力 -> 追加のライブラリ
      • gmock.lib
      • gmock_main.lib
  • ランタイムライブラリの修正
  • C/C++ -> コード生成-> ランタイムライブラリ
  • マルチスレッド デバッグ (/MTd)

また、_VARIADIC_MAX=10もプリプロセッサに追加する。

Debug用のメイン関数に をインクルードする。また、main関数に、    ::testing::InitGoogleMock(&argc, argv);を追加。

#include "stdafx.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>

 
int _tmain(int argc, _TCHAR* argv[])
{
    ::testing::InitGoogleTest(&argc, argv);
 ::testing::InitGoogleMock(&argc, argv);
    return RUN_ALL_TESTS();
}

サンプルコードまで書こうとおもったけど、疲れたので今日は終わり。参考リンクを下にいろいろ貼っておきます。

参考

08 Sep 2012, 10:10

GoogleMockをC言語で使う方法をハックしてみた

今、仕事ではテスト工程なので、

C言語で書いたソースにたいしてGoogleTestを使ってウラウラとテストを書きまくっている。

このロジックに対してはどうやってテストを書けばいいかを考えるのが楽しい。

モック関数を書きまくっている結果微妙な工程遅延を起こし、

マニュアルテスト組から、したり顔をされていたりする。;(´o`

GoogleTestには兄貴分のGoogleMockがいるのだが、

こいつがC言語で使えないかどうか、調べてみた。

GoogleMockとは

GoogleMockとは、C++用のモックオブジェクト作成ツール。

モックオブジェクトを使うことによって、

関数からの戻り値を自分でかってに書き換えたり,

テストしたい関数に渡された引数の値をしらべることができる。

普通ならば、テスト用の関数を自作して、調べなければいけないが、

これをGoogleMockを使うことによって、自作する関数を少なくできる。

ダウンロードはココから:GoogleMock

C++でのつかいかたはここから:Google Mock ドキュメント日本語訳

テスト対象コード

数を取得するライブラリを呼び出して、その結果から分岐するコードをテストする。

sample.c

[c]

#include “sample.h”

int get_ret(void)

{

int num = 0;

int ret;

/**N 数をライブラリより取得 */

num = get_num();

/**N 結果によって分岐 */

if( num == 1 ) {

ret = 10;

}

else if( num == 2 ){

ret = 20;

}

else {

ret = 30;

}

return ret;

}

[/c]

numlib.c

[c]

#include “numlib.h”

#include

int get_num()

{

return rand();

}

[/c]

get_num()の戻り値をテストを実行するたびに変更できれ便利だなー。

そんなことが、GoogleMockを使えばできてしまう。

GoogleMock導入結果

まずは、導入結果から。sample.cには手を入れない。

numlib.c

[c]

#include “numlib.h”

#include

int get_numImpl()

{

return rand();

}

int (*get_num)(void) = get_numImpl;

[/c]

numlib.h

[c]

#ifndef NUMLIB_H_

#define NUMLIB_H_

#ifdef __cplusplus

extern “C” {

#endif

extern int (*get_num)(void);

#ifdef __cplusplus

}

#endif

#endif /* NUMLIB_H_ */

[/c]

gmock_test.cpp

[cpp]

extern “C”

{

#include “sample.h”

}

#include “gtest/gtest.h” /**N GoogleTest インクルード */

#include “gmock/gmock.h” /**N GoogleMock インクルード */

using ::testing::Return;

/**N モッククラス */

class MockNum

{

public:

MOCK_METHOD0(get_num, int() );

} mock;

int Mockget_num(void)

{

return mock.get_num();

}

/**N get_num 実体 */

int (*get_num)(void);

/**N テスト */

class MockNumTest : public ::testing::Test

{

int (*savedget_num)();

virtual void SetUp()

{

savedget_num = get_num;

get_num = Mockget_num;

}

virtual void TearDown()

{

get_num = savedget_num;

}

};

TEST_F(MockNumTest, return1)

{

EXPECT_CALL(mock, get_num() ).WillOnce(Return(1));

EXPECT_EQ(10, get_ret() );

}

TEST_F(MockNumTest, return2)

{

EXPECT_CALL(mock, get_num() ).WillOnce(Return(2));

EXPECT_EQ(20, get_ret() );

}

TEST_F(MockNumTest, return3)

{

EXPECT_CALL(mock, get_num() ).WillOnce(Return(3));

EXPECT_EQ(30, get_ret() );

}

[/cpp]

テストコードではget_numからの戻り値がそれぞれ1,2,3が帰ってきた場合の、

処理分岐をテストしている。

GoogleMock導入のポイント

GoogleMcokをC言語で導入するためのポイントは2つ。

extern “C” で CとC++のソースをつなぐ

numlib.hは、C言語(sample.c)とC++(gmock_test.cpp)の

異なる言語のファイルからインクルードされるため、

インクルードのためのおまじないをかく。

[c]

#ifdef __cplusplus

extern “C” {

#endif

extern int (*get_num)(void);

#ifdef __cplusplus

}

#endif

[/c]

__cplusplusマクロは、C++で開発しているときにだけ定義される。

C++でコンパイルをかけるときだけ、C++で定義されたget_numをC言語でも

コンパイルできる extern “C” が適応される。

関数ポインタでget_numをモック関数に置き換える

関数ポインタをつかうことで、プロダクトコードには手を加えなくても、

実際のインタフェースをモックオブジェクトのインタフェースを置換することができる。

テストを実行する際に、一時的に関数ポインタをテスト用の関数で上書きする。

[cpp]

class MockNumTest : public ::testing::Test

{

int (*savedget_num)();

virtual void SetUp()

{

savedget_num = get_num;

get_num = Mockget_num;

}

virtual void TearDown()

{

get_num = savedget_num;

}

};

[/cpp]

また、プロダクトコード、テストコードの両方でget_numをインクルードしたいため、

ヘッダファイルでextern宣言している。

最後に

実は、GoogleMockをGoogleTestで使う方法は1年以上前から調べていたのだが、

ようやくわかったので、記事にしてみた。参考にしたのは以下のQ&A。

https://groups.google.com/forum/?fromgroups=#!searchin/googlemock/C$20language/googlemock/CVdeuQ0e6OI/HOYyoNA7uIAJ

これで毎日が定時退社だ!!(^ ○ ^)!/

追記

Cygwin環境で、g++-4.exeを使ってコンパイルすると、コンパイルは通るが、

g++-3.exeだと、コンパイルエラーする。

3.xと4.xでなにが異なるのかわからないけれど、

とりあえずコンパイル通らなかったら、g++-3.exeをg++.exeに置換する必要あり。