Google Mock


Why Mock?

Mock Object는 real object를 모방하여 대신하기 위해 만들어진 객체로서 real object와 같은 interface를 가지고 각각의 interface 호출시 유저가 정의한 값을 반환하도록 만들어진다.

이러한 Mock Object는 다음의 이유로 사용된다.

  • Object가 비결정적 결과를 return할 때 (현재 시간, 온도 등)
  • Object가 재현 또는 구현하기 힘든 상태를 가질 때 (network error)
  • Object의 동작이 느릴 때 (Test 전에 초기화 되어야 하는 DB)
  • Object를 아직 구현하지 않았거나 동작이 바뀌었을 때
  • Test를 위한 method나 information을 포함해야 할 때

여기서 Unit Test 시에는 마지막 두가지 이유로 Mock을 사용한다.

  • 아직 구현되지 않은 Object를 대신하여 Test를 진행할 수 없을 때 fake object의 목적으로 구현
  • Test하고자 하는 과정에서 대상 객체의 Interface가 다음과 같이 정상적으로 호출되었는지를 검사
    • 호출시 parameter 검사
    • 호출 횟수 검사

Google Mock 자체는 Test Framework가 아니라 앞서 말한 Mock Object를 쉽게 만들어주기 위한 도구이다. 결국 Test를 위해서는 Test Framework와 같이 사용되야 한다. Google Test bundel과 함께 제공되므로 Google Test를 사용해도 좋고 CppUnit등과 같은 다른 Test Framework 와 같이 사용할 수 도 있다.

System Requirement


  • gcc 4.0+ or Microsoft Visual C++ 8.0 SP1
    • Users reported that it also works with gcc 3.4, Microsoft Visual C++ 7.1, and Cygwin, although we haven't tested it there ourselves.

Example

(ref googlemock wiki)
예를 들어 어떤 drawing API를 사용한 graphic program을 개발한다고 할때, 이것이 올바로 동작하는지 어떻게 테스트 해야 할까?
Program에서 drawing API를 직접 호출하는 대신에, API를 Interface(Turtle이라고 하자) 로 wrapping한다.

Wrapping interface를 정의할때 주의할 점은 소멸자는 반드시 virtual로 선언해야 하는 것이다.
이유는 이것을 상속받은 넘의 소멸자가 호출되지 않아 memory leak같은 개발자가 의도하지 않는 문제가 발생할 수 있기 때문이다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#ifndef TURTLE_H_
#define TURTLE_H_

class Turtle {
public:
    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 구현

이 API가 올바로 호출되는지 확인하기 위한 Mock을 구현한다고 하면 Mock을 만드는 방법은 다음과 같다.
  1. Turtle을 상속받은 MockTurtle을 정의한다.
  2. Test할 member function의 Parameter 갯수를 확인한다.
  3. 자식 class인 MockTurtle의 public section에 Test할 function에 대한 Mock method를 다음과 같이 정의한다.
    • MOCK_METHOD[param갯수] ( [function명], [function type] );
    • (const function일 경우는 MOCK_CONST_METHOD[param갯수] ( [function명], [function type] ); ) 


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#ifndef MOCKTURTLE_H_
#define MOCKTURTLE_H_

#include "gmock/gmock.h" // Brings in Google Mock.
#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 /* MOCKTURTLE_H_ */

이것을 자동으로 해주는 script를 gmock에서 제공한다. (대상 class가 복잡하면 잘 안될수도 있다)
위치와 파일 명은 gmock-1.7.0/scripts/generator/gmock_gen.py 이고 argument로 대상 header file을 입력해주면 된다.

Mock을 Test에 사용

생성한 Mock class를 Test에 사용하는 방법은 다음과 같다.

  1. Test시에 제한없이 사용할수 있도록 Google Mock이름을 testing namespace로부터 Import 한다.
  2. Mock Object를 생성한다.
  3. 대상의 Expectations을 명시한다.
    • Method 호출 횟수
    • 예상되는 argument(parameter)값
    • 수행시 예상 동작(return)
  4. mock을 적용한 코드를 수행한다. 옵션으로 결과 체크는 google test assertion을 사용한다. mock이 더호출되거나 잘못된 argument로 호출될때 즉시 확인 가능하다.
  5. Mock이 소멸될때, 자동으로 모든 Expectations을 만족하였는지 확인한다.

소멸되지 않을 경우 Expectation check가 되지 않기 때문에 Mock을 "new"를 사용하여 heap 영역에 생성하였다면 Test가 끝난 후 반드시 소멸시켜주어야 함

Google Mock은 호출되기 이전에 expectation이 먼저 Set되어 있어야 한다.

Example code

Painter.h

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#ifndef PAINTER_H_
#define PAINTER_H_

#include "Turtle.h"

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

    bool DrawCircle(int x, int y, int r) {
        return true;
    }

protected:
    Turtle* turtle_;
};

#endif /* PAINTER_H_ */

TestMain.cpp


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include "MockTurtle.h"
#include "Painter.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"

// #1 Test시에 제한없이 사용할수 있도록 Google Mock이름을 testing namespace로부터 Import 한다.
using ::testing::AtLeast;

TEST(PainterTest, CanDrawSomething) {
    MockTurtle turtle; // #2 Mock Object를 생성한다

    EXPECT_CALL(turtle, PenDown()) // #3 대상의 Expectations을 명시한다
    .Times(AtLeast(1));

    Painter painter(&turtle); // #4 mock을 적용한 코드를 수행한다.

    EXPECT_TRUE(painter.DrawCircle(0, 0, 10)); // 옵션으로 결과 체크는 google test assertion을 사용한다.
} // #5 Mock이 소멸될때, 자동으로 모든 Expectations을 만족하였는지 확인한다.

int main(int argc, char** argv) {
    // The following line must be executed to initialize Google Mock
    // (and Google Test) before running the tests.
    ::testing::InitGoogleTest(&argc, argv);
    ::testing::InitGoogleMock(&argc, argv);
    return RUN_ALL_TESTS();
}


만약 PenDown이 한번도 불리지 않는 경우 다음과 같은 Message가 출력된다.

../UnitTest/Source/testMain.cpp:14: Failure
Actual function call count doesn't match EXPECT_CALL(turtle, PenDown())...
Expected: to be called at least once
Actual: never called - unsatisfied and active

Expectation 설정

General Syntax

EXPECT_CALL(mock_object, method(matchers))
.Times(cardinality)
.WillOnce(action)
.WillRepeatedly(action);

Mock method의 expectation을 명시할때는 EXPECT_CALL macro를 사용하고 방법은 위와 같다.

  • mock_object는 mock객체의 이름을 말하며
  • method는 mock의 Test할 대상 method의 이름이다.
  • matchers는 ","로 구분되는 예상 Argument값이다.
    • Primitive type은 예상 값을 다음과 같이 직접 입력할 수 있다.
      • EXPECT_CALL(mock_object, method(1, 0).Times(1);
    • argument의 condition을 check하기위해 Eq(), Ge(), IsNull(), Pointee() 등을 사용할 수 있다.
      • EXPECT_CALL(mock_object, method(Ge(1), Eq(0)).Times(1);
    • 객체일 경우나 사용자 정의 condition는 MATCHER_P macro를 이용하여 matcher를 정의하여 사용할 수 있다.
    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      MATCHER_P(StrIn, str, "") {
          std::string str_arg = arg;
          return strstr(str_arg.c_str(), str) == NULL ? false : true;
      }
      
      MATCHER_P(UuidEq, uuid, "") {
          Device* device = static_cast<Device*>(arg);
          return device->uuid == uuid;
      }
      
      
      
      MATCHER_P2(StrIn, str1, str2, "") {
      
          std::string str_arg = arg;
      return (strstr(str_arg.c_str(), str1) == NULL || strstr(str_arg.c_str(), str2) == NULL) ? false : true;
      }
  • EXPECT_CALL macro는 TypedExpectation& 객체를 return하도록 되어 있는데TypedExpectation에서 제공하는 대부분의 API는 TypedExpectation&를 return하도록 되어 있어서 위와 같이 expectation을 끝에 "."을 붙여 쉽게 확장 할 수 있다.
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    EXPECT_CALL(turtle, GetX())
    .WillOnce(Return(100))
    .WillOnce(Return(200))
    .WillOnce(Return(300));
    
    EXPECT_CALL(turtle, GetY())
    .WillOnce(Return(100))
    .WillOnce(Return(200))
    .WillRepeatedly(Return(300));
    

Mock을 Fake로 동작하도록 설정

::testing::Invoke의 ON_CALL macro를 사용한다.

대상 class를 상속받은 Fake sub class를 생성하여 동작을 구현한다.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#ifndef UNITTEST_FAKETURTLE_H_
#define UNITTEST_FAKETURTLE_H_

#include "Turtle.h"
#include 

class FakeTurtle: public Turtle {
public:
    virtual ~FakeTurtle() {    }

    virtual void PenUp() {
        printf("%s()\n", __FUNCTION__);
    }

    virtual void PenDown() {
        printf("%s()\n", __FUNCTION__);
    }

    virtual void Forward(int distance) {
        printf("%s(%d)\n", __FUNCTION__, distance);
    }

    virtual void Turn(int degrees) {
        printf("%s(%d)\n", __FUNCTION__, degrees);
    }

    virtual void GoTo(int x, int y) {
        printf("%s(%d, %d)\n", __FUNCTION__, x, y);
    }

    virtual int GetX() const {
        printf("%s()\n", __FUNCTION__);
    }

    virtual int GetY() const {
        printf("%s()\n", __FUNCTION__);
    }
};

#endif /* UNITTEST_FAKETURTLE_H_ */

Mock이 호출될때 Fake object의 함수가 불리도록 ON_CALL을 이용하여 다음과 같이 void DelegateToFake() 함수를 작성한다.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#ifndef MOCKTURTLE_H_
#define MOCKTURTLE_H_

#include "gmock/gmock.h" // Brings in Google Mock.
#include "Turtle.h"
#include "FakeTurtle.h"

using ::testing::Invoke;
using ::testing::_;

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());

    void DelegateToFake() {
        ON_CALL(*this, PenUp()).WillByDefault(Invoke(&fake_, &FakeTurtle::PenUp));
        ON_CALL(*this, PenDown()).WillByDefault(Invoke(&fake_, &FakeTurtle::PenDown));
        ON_CALL(*this, Forward(_)).WillByDefault(Invoke(&fake_, &FakeTurtle::Forward));
        ON_CALL(*this, Turn(_)).WillByDefault(Invoke(&fake_, &FakeTurtle::Turn));
        ON_CALL(*this, GoTo(_,_)).WillByDefault(Invoke(&fake_, &FakeTurtle::GoTo));
        ON_CALL(*this, GetX()).WillByDefault(Invoke(&fake_, &FakeTurtle::GetX));
        ON_CALL(*this, GetY()).WillByDefault(Invoke(&fake_, &FakeTurtle::GetY));
    }

private:
    FakeTurtle fake_;
};

#endif /* MOCKTURTLE_H_ */

Test Code에서 Fake로 동작하도록 하기 위해 Mock 객체 생성 후 DelegateToFake() 함수를 호출한다.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
TEST(PainterTest, CanDrawSomething_Fake) {
    MockTurtle turtle;

    turtle.DelegateToFake(); // DelegateToFake 호출

    EXPECT_CALL(turtle, PenDown())
    .Times(AtLeast(1));

    Painter painter(&turtle);

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

실행시 다음과 같이 Fake 객체가 실행됨을 확인 할 수 있다.

[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from PainterTest
[ RUN ] PainterTest.CanDrawSomething
PenDown()
PenUp()
GMOCK WARNING:
Uninteresting mock function call - taking default action specified at:
../UnitTest/mock-turtle.h:29:
Function call: PenUp()
Stack trace:
[ OK ] PainterTest.CanDrawSomething (0 ms)
[----------] 1 test from PainterTest (1 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (1 ms total)
[ PASSED ] 1 test.


댓글

댓글 쓰기

이 블로그의 인기 게시물

Raspberry pi 한글 설정 및 chromium 설치

Google Test를 이용한 Class의 Private/Protected Member Test