Cっぽいコードでgoogle testとgoogle mockを使ってみる
Cっぽいコードでgtestとgmockを使ってみる
クラスとか使っていないコードでgtest, ついでにgmockを使うことができないか試してみたのでメモ。
目次[非表示]
参考にした情報
以下の情報を参考にさせていただきました。ありがとうございます。
元のコード
我ながらCだかC++だか分からないコードですが・・・
capsule.h
#include <iostream>
#include <stdint.h>
#ifndef CAPSULE_H_
#define CAPSULE_H_
// Prototype for public function
void publicMethod(const uint8_t input, uint8_t &output);
#endif // CAPSULE_H_
capsule.cpp
#include "capsule.h"
// Prototype for private function
static void privateMethod01(const uint8_t input, uint8_t &output);
// Implement for public function
void publicMethod(const uint8_t input, uint8_t &output) {
std::cout << "publicMethod called." << std::endl;
// call private function
privateMethod01(input, output);
return;
}
// Implement for private function
static void privateMethod01(const uint8_t input, uint8_t &output) {
std::cout << "privateMethod called." << std::endl;
// TODO Implement here!!
return;
}
privateMethod01
の実装は大変そうなので、とりあえずprivateMethod01
はニセモノでいいからPublicMethod
のテストを書いておきたい、という状況を想定しています(あるのか?)。
gmockを使わずgtestをする場合
いきなり状況を無視しますが、ニセモノを使わずprivateMethod01
から正直に実装を進めるのであれば、テストコード側からcapsule.cpp**
をincludeすることで、privateMethod01
まで含めて試験を書くことができます。
capsule_gtest.cpp
#include <gtest/gtest.h>
// To avoid multiply declaration
namespace UNITTEST {
#include "capsule.cpp"
class SampleTest : public ::testing::Test {
protected:
virtual void SetUp() {
}
virtual void TearDown() {
}
};
TEST_F(SampleTest, public_method_test) {
std::cout << "public method test start!" << std::endl;
uint8_t input = 3;
uint8_t output = 0;
publicMethod(input, output);
EXPECT_EQ(5, output);
}
TEST_F(SampleTest, private_method_test) {
std::cout << "private method test start!" << std::endl;
uint8_t input = 3;
uint8_t output = 0;
privateMethod01(input, output);
EXPECT_EQ(3, output);
}
} // namespace UNITTEST
リンク時の名前衝突を防ぐため、capsule.cpp
のincludeは適当なnamespace内で行うようにしています(参考:モダンC言語プログラミング §5.3.3)。
gmockを使えるように修正
gmock本来の使い方では、
- 試験対象のクラスを継承したモッククラスを作り
- メンバ関数をモックでoverrideする
ようですが、クラスとか無い今回は
- 関数呼び出し先のポインタをモックに置き換えてやる
必要があるようです(参考:sioaji2012のブログ)。
モック化したい関数呼び出しを関数ポインタに変更
テストのときにだけprivateMethod01
の呼び出しを関数ポインタにしたいので、(ちょっと気持ち悪いですが)マクロを使ってみます。
capsule.cpp
#include "capsule.h"
#ifdef GTEST
// add "_impl" suffix to private function
#define FUNC(func_name) func_name ## _impl
#else
// not to change
#define FUNC(func_name) func_name
#endif // GTEST
// Prototype for private function
static void FUNC(privateMethod01)(const uint8_t input, uint8_t &output);
#ifdef GTEST
// To use mock, replace function call to pointer
void (*privateMethod01)(const uint8_t input, uint8_t &output) = FUNC(privateMethod01);
#endif
// Implement for public function
void publicMethod(const uint8_t input, uint8_t &output) {
std::cout << "publicMethod called." << std::endl;
// call private function
privateMethod01(input, output);
return;
}
// Implement for private function
static void FUNC(privateMethod01)(const uint8_t input, uint8_t &output) {
std::cout << "privateMethod called." << std::endl;
// TODO Implement here!!
return;
}
テストのときにだけ
privateMethod01
のプロトタイプ宣言と実装をprivateMethod01_impl
という名前に変更privateMethod01
という関数ポインタを宣言してprivateMethod01_impl
を格納
となるようにしていて、テストでない場合には元と同じになります。
モックを使った試験を実装
gmockを使った試験になるよう、capsule_gtest.cpp
も修正します。
#include <gtest/gtest.h>
#include <gmock/gmock.h>
// To avoid multiply declaration
namespace UNITTEST {
#include "capsule.cpp"
using ::testing::AtLeast;
using ::testing::SetArgReferee;
using ::testing::_;
// Mock class
class MockPrivate {
public:
MOCK_METHOD2(privateMethod01, void(const uint8_t input, uint8_t &output));
};
// Create instance
MockPrivate mock;
// Mock function
void mockPrivateMethod01(const uint8_t input, uint8_t &output) {
return mock.privateMethod01(input, output);
}
class SampleTest : public ::testing::Test {
protected:
// Mock target (private function)
void (*saved_privateMethod01)(const uint8_t input, uint8_t &output);
virtual void SetUp() {
// Save original function pointer
saved_privateMethod01 = privateMethod01;
// Overwrite function pointer by Mock
privateMethod01 = mockPrivateMethod01;
}
virtual void TearDown() {
// Restore original function pointer
privateMethod01 = saved_privateMethod01;
}
};
TEST_F(SampleTest, public_method_test) {
std::cout << "public method test start!" << std::endl;
uint8_t input = 3;
uint8_t output = 0;
// Set behavior for Mock
EXPECT_CALL(mock, privateMethod01(_, _))
.Times(2)
.WillOnce(SetArgReferee<1>(input))
.WillOnce(SetArgReferee<1>(5));
// evaluation
// No.1
publicMethod(input, output);
EXPECT_EQ(3, output);
// No.2
publicMethod(input, output);
EXPECT_EQ(5, output);
}
int main(int argc, char** argv) {
::testing::InitGoogleMock(&argc, argv);
return RUN_ALL_TESTS();
}
} // namespace UNITTEST
- モッククラスとモック関数を定義
- テストフィクスチャ内で
privateMethod01
の関数ポインタをモック関数に置き換え(テストごとに戻す)
これで、モックを使った試験ができるようになりました。
まとめ
Cっぽいコードでgmockを使ったgtestができるようにしてみました(誰得?)。
ただ、元コードにかなり手を入れてしまっているので、場合によってはこの方法を取ることができないこともあると思います。
というか、もっとスマートにできる方法があるに違いない・・・思いつきませんが。
Written with StackEdit.
コメント
コメントを投稿