SSブログ

Google Mockを使ってみる [Google Test]

いままで,Google Testを使ってきたが,さらにGoogle Mockも使ってみる.

Google Mockとは,まぁ日本語ドキュメントがあるので参照してください.って書いてしまうとさすがに寂しいのでもうちょっと書くというか引用すると,Google Mockは「モッククラスを作成して使用するためのライブラリ」ということになる.
で,モックというのは,「Expectation を利用して事前にプログラムされたオブジェクト」だ.呼び出されたら何を返すかとか,何を引数として呼び出されるのかとか,そもそも何回呼び出されるのかとかをあらかじめ指定しておけるオブジェクトだ.なので,テストをするときに「本物」の代わりに使うと何かと便利になる.

ではまず,Google Mockを使えるようにしてみる.Google Testを使えるようにしたときと同様に,まずは適当なディレクトリを作成し,Google Mockをダウンロード,展開してやる.
cd ~/
mkdir googlemock
cd googlemock/
wget http://googlemock.googlecode.com/files/gmock-1.6.0.zip
unzip gmock-1.6.0.zip

そしたらmakeする.
このとき,ubuntuの環境だとエラーが出てmakeできない.なので,1カ所変更する.
変更対象のファイルは,~/googlemock/gmock-1.6.0/make/Makefileだ.このファイルの最後の部分,-lpthreadのところを変更してやる.
変更前
gmock_test : gmock_test.o gmock_main.a
    $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@
変更後
gmock_test : gmock_test.o gmock_main.a
    $(CXX) $(CPPFLAGS) $(CXXFLAGS) $^ -lpthread -o $@

変更したらmakeして,テストする.
cd gmock-1.6.0/make/
make
./gmock_test

テストでエラーが出なければOKだ.

そしたらライブラリはコピーしておく.
cd ~/googlemock/gmock-1.6.0/
mkdir lib
cp make/gmock_main.a lib/libgmock_main.a


あと,Google Mockのパスを設定しておく.~/.bashrcとかに,
export GMOCK_DIR=~/googlemock/gmock-1.6.0

としておけばよい.

これでひとまず準備は完了.
そしたら,Google Mock ドキュメント日本語訳の超入門編に出てくるMock Turtlesの例で試してみる.

いつもと同じように適当なディレクトリにturtles_mock_projディレクトリを作って,ファイル構成は以下のようにしてやる.
turtles_mock_proj/
├── CMakeLists.txt
├── compiler_settings.cmake
├── mock_turtle.h
├── painter.cpp
├── painter.h
├── test
│   └── mock_unittest.cpp
└── turtle.h


それぞれのファイルは以下のようになる.ちょっと長くなるが全部示そう.

CMakeLists.txt
cmake_minimum_required(VERSION 2.6)
project(mock_test)

include (compiler_settings.cmake)

enable_testing()
set(CMAKE_INCLUDE_CURRENT_DIR ON)
message(STATUS GTEST_DIR=$ENV{GTEST_DIR})
message(STATUS GMOCK_DIR=$ENV{GTEST_DIR})

include_directories($ENV{GTEST_DIR}/include)
include_directories($ENV{GMOCK_DIR}/include)
link_directories($ENV{GTEST_DIR}/lib)
link_directories($ENV{GMOCK_DIR}/lib)

cxx_gmock_test(mock_test "" painter.cpp  test/mock_unittest.cpp)

compiler_settings.cmake
# compiler settings
find_package(Threads)

set(cxx_base_flags "${cxx_base_flags} -Wall -Wshadow")
set(cxx_base_flags "${cxx_base_flags} -Wextra")
set(cxx_base_flags "${cxx_base_flags} -Werror")

function(cxx_executable_with_flags name cxx_flags libs)
  add_executable(${name} ${ARGN})
  if (cxx_flags)
    set_target_properties(${name}
      PROPERTIES
      COMPILE_FLAGS "${cxx_flags}")
  endif()
  foreach (lib "${libs}")
    target_link_libraries(${name} ${lib})
  endforeach()
endfunction()

function(cxx_executable name libs)
  cxx_executable_with_flags(${name} "${cxx_base_flags}" "${libs}" ${ARGN})
endfunction()

function(cxx_test_with_flags name cxx_flags libs)
  cxx_executable_with_flags(${name} "${cxx_flags}" "${libs}" ${ARGN})
  target_link_libraries(${name} gtest;gtest_main)
  target_link_libraries(${name} ${CMAKE_THREAD_LIBS_INIT})
  add_test(${name} ${name})
endfunction()

function(cxx_test name libs)
  cxx_test_with_flags("${name}" "${cxx_base_flags}" "${libs}" ${ARGN})
endfunction()

function(cxx_gmock_test name libs)
  cxx_test_with_flags("${name}" "${cxx_base_flags}" "${libs};gmock_main" ${ARGN})
endfunction()

painter.h
#ifndef PAINTER_H
#define PAINTER_H

#include "turtle.h"

class Painter {
public:
    Painter(Turtle* turtle)
        : turtle_(turtle) {}
    ~Painter() {}

    bool DrawCircle(int x, int y, int r);

private:
    Turtle* turtle_;
};

#endif // PAINTER_H

painter.cpp
#include "painter.h"

bool Painter::DrawCircle(int x, int y, int r)
{
    (void)x;
    (void)y;
    (void)r;

    turtle_->PenDown();
    return true;
}

turtle.h
#ifndef TURTLE_H
#define TURTLE_H

class Turtle {
public:
    Turtle() {}
    virtual ~Turtle() {}
    virtual void PenUp() = 0;
    virtual void PenDown() = 0;
    virtual void Forward(int distance) = 0;
    virtual void Turn(int degrees) = 0;
    virtual void GoTo(int x, int y) = 0;
    virtual int GetX() const = 0;
    virtual int GetY() const = 0;
};

#endif // TURTLE_H

mock_turtle.h
#ifndef MOCK_TURTLE_H
#define MOCK_TURTLE_H

#include "gmock/gmock.h"
#include "turtle.h"

class MockTurtle : public Turtle {
public:
    MOCK_METHOD0(PenUp, void());
    MOCK_METHOD0(PenDown, void());
    MOCK_METHOD1(Forward, void(int distance));
    MOCK_METHOD1(Turn, void(int degrees));
    MOCK_METHOD2(GoTo, void(int x, int y));
    MOCK_CONST_METHOD0(GetX, int());
    MOCK_CONST_METHOD0(GetY, int());
};

#endif // MOCK_TURTLE_H

test/mock_unittest.cpp
#include "gtest/gtest.h"
#include "gmock/gmock.h"

#include "painter.h"
#include "mock_turtle.h"

TEST(PainterTest, CanDrawSomething)
{
    MockTurtle turtle;
    EXPECT_CALL(turtle, PenDown())
        .Times(::testing::AtLeast(1));
  
    Painter painter(&turtle);

    EXPECT_TRUE(painter.DrawCircle(0, 0, 10));
}


そしたらいつも通りビルドしてやればテストできる.
コードとテストの詳細は前述のGoogle Mock ドキュメント日本語訳の超入門編をみてもらうとして,ポイントだけ説明しておく.
今回の例だと,CanDrawSomethingのテストで,PenDown()が1回だけ呼ばれることをモックを使ってテストしていて,実際,DrawCircle()でPenDown()を呼び出しているので,テストは成功する.
試しに,DrawCircle()でのPenDown()の呼び出しをコメントアウトしたりするとテストが失敗するようになるはずだ.

今回はとりあえずメソッド呼び出しの回数テストだけしか試してないけど,Google Mockの雰囲気は分かると思う.
Google Mockは他にもいろいろできるんだけど,その辺についてはまた次回.
タグ:Google Mock
nice!(1)  トラックバック(0) 
共通テーマ:日記・雑感

nice! 1

トラックバック 0

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。