Add korean version docs

This commit is contained in:
hyuk.myeong
2019-08-13 11:00:10 +09:00
parent 90a443f9c2
commit 8117fd29e2
7 changed files with 7397 additions and 0 deletions

View File

@@ -0,0 +1,607 @@
## gMock Cheat Sheet
### Mock Class 정의하기
#### 일반 Class Mocking하기
아래에 `Foo`라는 interface가 있습니다. (소멸자 `~Foo()`는 반드시 `virtual`로 선언해야 합니다.)
```cpp
class Foo {
...
virtual ~Foo();
virtual int GetSize() const = 0;
virtual string Describe(const char* name) = 0;
virtual string Describe(int type) = 0;
virtual bool Process(Bar elem, int count) = 0;
};
```
`Foo` interface는 아래와 같이 mocking 할 수 있습니다.
```cpp
#include "gmock/gmock.h"
class MockFoo : public Foo {
...
MOCK_METHOD(int, GetSize, (), (const, override));
MOCK_METHOD(string, Describe, (const char* name), (override));
MOCK_METHOD(string, Describe, (int type), (override));
MOCK_METHOD(bool, Process, (Bar elem, int count), (override));
};
```
이 때, uninteresting call을 무시하려면 "nice" mock object로 생성하면 됩니다. 반대로 모든 uninteresting call을 failure로 처리하려면 "strict" mock object로 생성하면 됩니다.
```cpp
using ::testing::NiceMock;
using ::testing::NaggyMock;
using ::testing::StrictMock;
NiceMock<MockFoo> nice_foo; // The type is a subclass of MockFoo.
NaggyMock<MockFoo> naggy_foo; // The type is a subclass of MockFoo.
StrictMock<MockFoo> strict_foo; // The type is a subclass of MockFoo.
```
#### Class Template Mocking하기
아래에 `StackInterface`라는 interface가 있습니다. (소멸자 `~StackInterface()`는 반드시 `virtual`로 선언해야 합니다.)
```cpp
template <typename Elem>
class StackInterface {
...
virtual ~StackInterface();
virtual int GetSize() const = 0;
virtual void Push(const Elem& x) = 0;
};
```
`StackInterface`는 아래와 같이 mocking할 수 있습니다.
```cpp
template <typename Elem>
class MockStack : public StackInterface<Elem> {
...
MOCK_METHOD(int, GetSize, (), (const, override));
MOCK_METHOD(void, Push, (const Elem& x), (override));
};
```
#### Mock Function의 호출방식 명세하기
만약 mocking 대상이 기본적인 호출방식을 사용하지 않는다면, 이를 gMock에 알려줘야 합니다. 예를 들어 Windows의 `STDMETHODCALLTYPE`같은 경우에는 `ULONG STDMETHODCALLTYPE AddRef()`과 같은 형태로 function이 선언됩니다. 사용자는 gMock이 이러한 내용을 알 수 있도록 `MOCK_METHOD`의 4번째 parameter를 통해 관련내용을 전달할 수 있습니다.
```cpp
MOCK_METHOD(bool, Foo, (int n), (Calltype(STDMETHODCALLTYPE)));
MOCK_METHOD(int, Bar, (double x, double y),
(const, Calltype(STDMETHODCALLTYPE)));
```
예제로 사용한 `STDMETHODCALLTYPE`은 Windows의 `<objbase.h>`파일에 정의되어 있습니다.
### Mock 사용하기
이제 mock을 사용할 차례입니다. Mock을 사용하는 일반적인 순서는 아래와 같습니다.
1. `using`을 통해서 사용하려는 gMock 기능을 추가하세요. 예외적인 경우를 제외하면 거의 모든 기능은 `testing` namespace에 정의되어 있습니다.
2. Mock object를 생성합니다.
3. (선택사항) Mock object의 default action을 설정합니다.
4. Mock object에 대한 expectation을 지정합니다. (어떤 방식으로 호출 될 것인지, 무슨일을 할 것인지 등)
5. Mock object를 사용하는 실제 코드를 수행합니다. 필요한 경우 googletest assertion을 함께 사용합니다.
6. Mock object가 소멸될 때, 설정된 모든 expectation들이 만족되었는지 확인합니다.
위의 6단계 순서를 아래 예제코드에서 확인할 수 있습니다.
```cpp
using ::testing::Return; // #1
TEST(BarTest, DoesThis) {
MockFoo foo; // #2
ON_CALL(foo, GetSize()) // #3
.WillByDefault(Return(1));
// ... other default actions ...
EXPECT_CALL(foo, Describe(5)) // #4
.Times(3)
.WillRepeatedly(Return("Category 5"));
// ... other expectations ...
EXPECT_EQ("good", MyProductionFunction(&foo)); // #5
} // #6
```
### Default Action 설정하기
gMock은 `void`, `bool`, 숫자형, 포인터 등을 return type으로 하는 function에 대해서는 **built-in default action**을 기본적으로 제공하고 있습니다.
다음으로 임의의 타입 `T`에 대한 default action을 전역적으로 변경하려면 아래 기능을 사용하면 됩니다.
```cpp
using ::testing::DefaultValue;
// Sets the default value to be returned. T must be CopyConstructible.
DefaultValue<T>::Set(value);
// Sets a factory. Will be invoked on demand. T must be MoveConstructible.
// T MakeT();
DefaultValue<T>::SetFactory(&MakeT);
// ... use the mocks ...
// Resets the default value.
DefaultValue<T>::Clear();
```
위에서 설정한 default action을 사용하는 코드는 아래와 같습니다.
```c++
// Sets the default action for return type std::unique_ptr<Buzz> to
// creating a new Buzz every time.
DefaultValue<std::unique_ptr<Buzz>>::SetFactory(
[] { return MakeUnique<Buzz>(AccessLevel::kInternal); });
// When this fires, the default action of MakeBuzz() will run, which
// will return a new Buzz object.
EXPECT_CALL(mock_buzzer_, MakeBuzz("hello")).Times(AnyNumber());
auto buzz1 = mock_buzzer_.MakeBuzz("hello");
auto buzz2 = mock_buzzer_.MakeBuzz("hello");
EXPECT_NE(nullptr, buzz1);
EXPECT_NE(nullptr, buzz2);
EXPECT_NE(buzz1, buzz2);
// Resets the default action for return type std::unique_ptr<Buzz>,
// to avoid interfere with other tests.
DefaultValue<std::unique_ptr<Buzz>>::Clear();
```
특정 method에 대해서만 default action을 변경하려면 `ON_CALL()`을 사용하면 됩니다. `ON_CALL()`은`EXPECT_CALL()`과 사용방법이 비슷하며 default behavior를 변경하기 위한 목적으로 사용합니다. 더 자세한 정보는 [여기]()를 참조하세요.
```cpp
ON_CALL(mock-object, method(matchers))
.With(multi-argument-matcher) ?
.WillByDefault(action);
```
### Expectation 설정하기
Mock method에 **expectation**을 설정할때는 `EXPECT_CALL()`을 사용합니다. Expectation이란 해당 mock method가 어떤 방식으로 호출될 것인지 그리고 무슨일을 할 것인지 등을 지정하는 것입니다.
```cpp
EXPECT_CALL(mock-object, method (matchers)?)
.With(multi-argument-matcher) ?
.Times(cardinality) ?
.InSequence(sequences) *
.After(expectations) *
.WillOnce(action) *
.WillRepeatedly(action) ?
.RetiresOnSaturation(); ?
```
만약 `(matchers)` 부분이 없다면 어떤 argument가 와도 괜찮다는 의미의 `(_,_,_,_)`와 동일하게 동작합니다.
위에서 `Times()`를 설정하지 않은 경우에는 아래와 같이 호출횟수가 결정됩니다.
- `WillOnce()`와 `WillRepeatedly()` 둘 다 없는 경우, `Times(1)`로 간주됩니다.
- `WillOnce()`가 `n`개 있고 `WillRepeatedly()`가 없다면 `Times(n)`으로 간주됩니다. (`n` >=1 )
- `WillOnce()`가 `n`개 있고 `WillRepeatedly()`가 있다면 `Times(AtLeast(n))`으로 간주됩니다. (`n` >=0)
`EXPECT_CALL()`을 사용하지 않은 mock method는 *몇 번 호출되더라도 괜찮습니다.* 호출될 때마다 default action이 수행될 것입니다.
### Matchers
하나의 **Matcher**는 *하나의* argument를 비교하기 위한 목적으로 사용합니다. 주로 `ON_CALL()`이나 `EXPECT_CALL()`과 함께 사용하며, `EXPECT_THAT`을 통해 값을 직접 검증하는 것도 가능합니다.
| Matcher | Description |
|:--------|:------------|
| `EXPECT_THAT(actual_value, matcher)` | `matcher`를 통해 `actual_value`를 검증합니다. |
| `ASSERT_THAT(actual_value, matcher)` | **fatal** failure를 발생시킨다는 점 외에는 `EXPECT_THAT(actual_value, matcher)`과 동일합니다. |
아래부터는 built-in matcher를 여러가지로 분류한 내용들이 계속됩니다. 자주 등장하는 용어인 `argument`는 mock function으로 전달되는 argument를 의미함을 기억하세요.
#### Wildcard
| Matcher | Description |
|:--------|:------------|
|`_`|타입만 문제없다면 `argument`로 어느 값이 전달돼도 괜찮습니다.|
|`A<type>()` or `An<type>()`|기본적으로 `_`와 동일하지만, `type`을 통해 `argument`의 타입을 명시적으로 지정할 수 있습니다. |
#### Generic Comparison
| Matcher | Description |
|:---------------------|:------------------|
|`Eq(value)` or `value`|`argument == value`|
|`Ge(value)` |`argument >= value`|
|`Gt(value)` |`argument > value` |
|`Le(value)` |`argument <= value`|
|`Lt(value)` |`argument < value` |
|`Ne(value)` |`argument != value`|
|`IsNull()` |`argument`가 `NULL` 포인터이기를 기대합니다. (raw, smart 둘다가능)|
|`NotNull()` |`argument`가 `NULL` 포인터가 아니기를 기대합니다. (raw, smart 둘다가능)|
|`Optional(m)` |`argument`로 전달되는 `optional<>`을 matcher `m`을 통해 비교합니다.|
|`VariantWith<T>(m)` |`argument`로 전달되는 `variant<>`를 matcher `m`을 통해 비교합니다.|
|`Ref(variable)` |`argument`가 `variable`의 참조형태이길 기대합니다.|
|`TypedEq<type>(value)`|`argument`가 `type`이라는 타입을 가지며 그 값도 `value`와 같기를 기대합니다. 기본적으로 `Eq(value)`와 동일한 기능을 하지만, 타입지정이 가능하기 때문에 mock function이 overloaded 되었을 때 유용하게 사용할 수 있습니다.|
대부분의 matcher들은 자신이 실행되는 시점에 `value`에 대한 *복사본을 생성하고 내부적으로 보관합니다.* (`Ref()`는 제외) 이렇게 복사본을 만드는 이유는 `value`가 추후에 수정되거나 소멸될 것에 대비하기 위함입니다. 그러다가 실제로 mock function이 호출되면 위에서 만들어 놓은 복사본과 전달된 argument를 서로 비교하게 됩니다. 이런 안전장치가 있기 때문에 `EXPECT_CALL`이 수행된 이후에는 `value`가 수정되더라도 영향을 받지 않게 됩니다. 한가지 유의할 점은 copy constructor가 없는 `value`는 (당연하게도) compile error가 발생한다는 것입니다. 이 문제를 해결하기 위해서 `value`를 `ByRef()`로 감싸서 참조형식으로 전달하는 것도 가능합니다. 예를 들면 `Eq(ByRef(non_copyable_value))`와 같이 구현할 수 있습니다. 다만, 이것은 복사본을 만드는 것이 아니기 때문에 주의해서 사용하기 바랍니다. 참조형식을 사용하게 되면 `value`의 값에 따라 matcher의 동작도 달라진다는 점을 기억하세요.
#### Floating-Point Matchers
| Matcher | Description |
|:-------------------|:---------------------------------------------------------------------------------------------------------|
|`DoubleEq(a_double)`|`argument`는 `double` 타입이며, 값은 `a_double`과 *거의 같습니다.* 두 개의 NaN을 비교할 때는 같지 않다고 판단합니다. |
|`FloatEq(a_float)` |`argument`는 `float` 타입이며, 값은 `a_float`와 *거의 같습니다.* 두 개의 NaN을 비교할 때는 같지 않다고 판단합니다. |
|`NanSensitiveDoubleEq(a_double)`|`argument`는 `double` 타입이며, 값은 `a_double`과 *거의 같습니다.* 두 개의 NaN을 비교할 때는 같다고 판단합니다. |
|`NanSensitiveFloatEq(a_float)`|`argument`는 `float` 타입이며, 값은 `a_float`와 *거의 같습니다.* 두 개의 NaN을 비교할 때는 같다고 판단합니다. |
위의 floating-point matcher들은 ULP-based 비교를 수행합니다. googletest의 floating-point assertion에서도 동일한 방법을 사용하고 있습니다. 이 때의 오차범위는 expected value의 절대값을 기반으로 자동 계산됩니다. 다음으로 `DoubleEq`, `FloatEq`를 사용해서 2개의 NaN를 비교한다면 IEEE 표준에 따라 `false`를 반환하도록 되어 있습니다. 만약 NaN 2개를 비교했을 때 `true`가 반환되기를 원한다면 `NanSensitive*`로 시작하는 버전을 사용하시기 바랍니다.
| Matcher | Description |
|:--------|:------------|
|`DoubleNear(a_double, max_abs_error)`|`argument`는 `double` 타입이며, 값은 `a_double`과 *거의 같습니다.* (단, absolute error <= `max_abs_error`) 두 개의 NaN을 비교할 때는 같지 않다고 판단합니다.|
|`FloatNear(a_float, max_abs_error)`|`argument`는 `float` 타입이며, 값은 `a_float`와 *거의 같습니다.* (단, absolute error <= `max_abs_error`) 두 개의 NaN을 비교할 때는 같지 않다고 판단합니다.|
|`NanSensitiveDoubleNear(a_double, max_abs_error)`|`argument`는 `double` 타입이며, 값은 `a_double`과 *거의 같습니다.* (단, absolute error <= `max_abs_error`) 두 개의 NaN을 비교할 때는 같다고 판단합니다.|
|`NanSensitiveFloatNear(a_float, max_abs_error)`|`argument`는 `float` 타입이며, 값은 `a_float`와 *거의 같습니다.* (단, absolute error <= `max_abs_error`) 두 개의 NaN을 비교할 때는 같다고 판단합니다.|
#### String Matchers
String matcher로 전달가능한 `argument`는 C string이나 C++ string object 입니다.
| Matcher | Description |
| :---------------------- | :----------------------------------------------------------- |
| `ContainsRegex(string)` | `argument`가 주어진 정규식 `string`을 포함하는지 확인합니다. |
| `EndsWith(suffix)` | `argument`가 `suffix`로 끝나는지 확인합니다. |
| `HasSubstr(string)` | `argument`가 `string`을 포함하는지 확인합니다. |
| `MatchesRegex(string)` | `argument`가 주어진 정규식 `string`과 일치하는지 확인합니다. |
| `StartsWith(prefix)` | `argument`가 `prefix`로 시작하는지 확인합니다. |
| `StrCaseEq(string)` | `argument`가 `string`과 같은지 확인합니다. 대소문자는 무시합니다. |
| `StrCaseNe(string)` | `argument`가 `string`과 다른지 확인합니다. 대소문자는 무시합니다. |
| `StrEq(string)` | `argument`가 `string`과 같은지 확인합니다. |
| `StrNe(string)` | `argument`가 `string`과 다른지 확인합니다. |
`ContainsRegex()`, `MatchesRegex()`에 사용되는 정규식 문법은 [이곳](../../googletest/docs/advanced.md#regular-expression-syntax)에 정의되어 있으며 `StrCaseEq()`, `StrCaseNe()`, `StrEq()`, `StrNe()`은 wide string 타입을 사용해도 잘 동작합니다.
#### Container Matchers
STL-style container들은 대부분 `==` 연산자를 제공하고 있습니다. 따라서 `Eq(expected_container)` 또는 `foo(expected_container)`와 같이 구현하면 별다른 조치를 취하지 않더라도 잘 동작할 것입니다. 혹시나 보다 다양한 방법으로 container를 비교하고 싶은 사용자는 아래 표를 확인하기 바랍니다. 이렇게 추가적으로 제공되는 matcher를 통해서 사용자가 처한 상황에 맞게 좀 더 유연한 방법을 선택할 수 있을 것입니다.
| Matcher | Description |
|:--------|:------------|
| `BeginEndDistanceIs(m)` | argument로 전달된 container의 `begin()`, `end()`간의 차이(*즉, container의 size*)를 matcher `m`통해 비교합니다. 예를 들어`BegintEndDistanceIs(2)` 또는 `BeginEndDistanceIs(Lt(2))`와 같이 사용하면 됩니다. 만약, 해당 container가 `size()`를 정의하고 있다면 `SizeIs(m)`를 사용해도 됩니다. 기능자체는 동일합니다. |
| `ContainerEq(container)` | 기본적인 동작은 `Eq(container)`와 유사합니다. 단, failure message에 어떤 element가 서로 다른지와 같은 추가정보를 출력해줍니다. |
| `Contains(e)` | argument로 전달된 container에 `e`를 만족하는 element가 있는지 확인합니다. 여기서 `e`는 값일 수도 있고 matcher일 수도 있습니다. |
| `Each(e)` | argument로 전달된 container의 *모든* element가 `e`를 만족해야 합니다. 여기서 `e`는 값일 수도 있고 matcher일 수도 있습니다. |
| `IsEmpty()` | argument로 전달된 container에 element가 하나도 없기를 기대합니다. (`container.empty()`) |
| `IsTrue()` | argument로 전달된 container가 `true`이기를 기대합니다. 단, container가 boolean 타입으로 사용가능한 경우만 해당됩니다. |
| `IsFalse()` | argument로 전달된 container가 `false`이기를 기대합니다. 단, container가 boolean 타입으로 사용가능한 경우만 해당됩니다. |
| `SizeIs(m)` | argument로 전달된 container의 size가 matcher `m`을 만족하는지 확인합니다. `SizeIs(2)` 또는 `SizeIs(Lt(2))`와 같이 사용할 수 있습니다. |
| `ElementsAre(e0, e1, ..., en)` | argument로 전달된 container의 element들이 `e0`, `...`, `en`을 각각 만족하는지 비교합니다. 여기서 `e`는 값일 수도 있고 matcher일 수도 있습니다. 허용되는 element 개수는 0~10개까지입니다. |
| `ElementsAreArray({ e0, e1, ..., en })`, `ElementsAreArray(a_container)`, `ElementsAreArray(begin, end)`, `ElementsAreArray(array)`, `ElementsAreArray(array, count)` | 기본적인 동작은 `ElementsAre()`과 유사합니다. 단, `e0`, `e1`과 같은 expected element를 initializer_list, STL-style container, C-style array와 같은 다양한 방법으로 전달할 수 있습니다. |
| `UnorderedElementsAre(e0, e1, ..., en)` | 기본적인 동작은 `ElementsAre()`과 유사합니다. 단, `argument`와 parameter의 element들을 순서대로 비교하지 않습니다. |
| `UnorderedElementsAreArray({ e0, e1, ..., en })`, `UnorderedElementsAreArray(a_container)`, `UnorderedElementsAreArray(begin, end)`, `UnorderedElementsAreArray(array)`, `UnorderedElementsAreArray(array, count)` | 기본적인 동작은 `UnorderedElementsAre()`과 유사합니다. 단, `e0`, `e1`과 같은 expected element를 initializer_list, STL-style container, C-style array와 같은 다양한 방법으로 전달할 수 있습니다. |
| `IsSubsetOf({e0, e1, ..., en})`, `IsSubsetOf(a_container)`, `IsSubsetOf(begin, end)`, `IsSubsetOf(array)`, `IsSubsetOf(array, count)` | "argument container" ⊂ "expected container" |
| `IsSupersetOf({e0, e1, ..., en})`, `IsSupersetOf(a_container)`, `IsSupersetOf(begin, end)`, `IsSupersetOf(array)`, `IsSupersetOf(array, count)` | "argument container" ⊃ "expected container" |
| `Pointwise(m, container)` | argument로 전달된 container의 element 개수와 `container`의 element 개수가 같아야 합니다. 이 때 2개의 container에 속한 모든 element들이 matcher `m`을 통해 비교됩니다. 이 때, 양측의 element는 2-tuples 형태로 matcher `m`에 전달됩니다. 예를 들어 `Pointwise(Le(), upper_bounds)`라는 코드가 있다면 이는 argument로 전달된 container의 element가 `upper_bounds`에 속한 element보다 작은지를 확인 할 것입니다. `Pointwise`와 관련해서는 표 아랫부분에서도 좀 더 설명하고 있습니다. |
| `UnorderedPointwise(m, container)`, `UnorderedPointwise(m, {e0, e1, ..., en})` | 기본적인 동작은 `Pointwise(m, container)`와 유사합니다. 단, element들을 순서대로 비교하지 않습니다. 순서에 관계없이 matcher `m`을 만족하면 됩니다. |
| `WhenSorted(m)` | argument로 전달된 container의 element들을 `<`operator 를 통해서 정렬하고(오름차순) 그 결과를 matcher `m`을 통해 비교합니다. 이 때, 정렬은 gMock에서 자동으로 해줍니다. 예를 들어 `WhenSorted(ElementsAre(1,2,3))`라는 코드는 argument로 `1, 2, 3` 혹은 `2, 3, 1`과 같은 값들이 전달되어야 만족하게 됩니다. |
| `WhenSortedBy(comparator, m)` | 기본적인 동작은 `WhenSorted(m)`와 유사합니다. 단, 정렬방식을 변경할 수 있습니다. 예를 들어 `WhenSortedBy(std::greater<int>(), ElementsAre(3,2,1))`과 같이 사용하면 내림차순으로 정렬한 결과를 비교하게 됩니다. |
몇 가지 추가적인 내용을 공유합니다.
- Matcher들은 아래와 같은 상황에서도 사용 가능합니다.
1. 참조형식으로 전달되는 native array (예: `Foo(const int (&a)[5])`)
2. pointer + array size 형태로 array를 사용할 때 (예: `Bar(const T* buffer, int len)` -- 자세한 사용방법은 [multi-argument matchers](#multiargument-matchers)에서 참조하세요.)
- Multi-dimensional array에도 사용 가능합니다. (예: container의 element 각각이 array인 경우)
- `Pointwise(m, ...)`에 사용하기 위한 `m`을 구현할 때는 `::testing::tuple<T, U>`라는 tuple을 전달받아서 `T`, `U`를 비교하는 형태가 되어야 합니다. 여기서 `T`와 `U`는 각각 actual container(argument)의 element type과 expected container(parameter)의 element type을 의미합니다. 예를 들어, `Foo`라는 타입이 `operator==` 없이 `Equals()`이라는 method만 제공한다면 해당 타입 2개를 비교하는 matcher는 아래처럼 구현하면 됩니다.
```cpp
using ::std::get;
MATCHER(FooEq, "") {
return std::get<0>(arg).Equals(std::get<1>(arg));
}
...
EXPECT_THAT(actual_foos, Pointwise(FooEq(), expected_foos));
```
#### Member Matchers
| Matcher | Description |
|:--------|:------------|
|`Field(&class::field, m)`|`argument.field` 또는 `argument->field`를 matcher `m`을 통해 비교합니다. `argument`는 해당 *class*의 객체를 의미합니다.|
|`Key(e)`|`argument.first`는 `e`와 비교됩니다. `e`는 값 또는 matcher가 될 수 있습니다. 예를 들어 `Contatins(Key(Le(5)))`는 어떤 `map`에 `key <= 5`인 element가 있는지 확인합니다.|
|`Pair(m1, m2)`|`argument`는 `std::pair` 타입입니다. `first` 필드는 matcher `m1`을 통해 비교되며 `second` 필드는 matcher `m2`를 통해 비교됩니다.|
|`Property(&class::property, m)`|`argument.property()` 또는 `argument->property()`를 matcher `m`을 통해 비교합니다. `argument`는 해당 *class*의 object를 의미합니다.|
#### Matching the Result of a Function, Functor, or Callback
| Matcher | Description |
| :--------------- | :----------------------------------------------------------- |
| `ResultOf(f, m)` | `f`는 function 혹은 functor이며 `f(argument)`의 결과를 matcher `m`과 비교합니다. |
#### Pointer Matchers
| Matcher | Description |
| :------------------------ | :----------------------------------------------------------- |
| `Pointee(m)` | `argument`가 가리키는 값이 matcher `m`을 통해 비교됩니다. `argument`는 smart pointer 혹은 raw pointer 둘 다 가능합니다. |
| `WhenDynamicCastTo<T>(m)` | `argument`가 `dynamic_cast<T>()`를 통해 전달되었을 때, `m`을 사용해서 비교합니다. |
#### Multi-argument Matchers
기본적으로 하나의 matcher는 *하나의 값만 비교합니다.* 제목인 "multi-argument"라는 것도 사실은 *tuple*을 통해서 가능한 것이기 때문에 여전히 하나의 값을 비교한다고 주장해도 틀린말은 아닙니다. 어찌 됐든 아래 표에 있는 matcher들은 `(x, y)`형태의 tuple을 비교하는데에 사용할 수 있습니다. 물론 사용자가 tuple을 사용할 수 있는 matcher를 직접 구현해도 됩니다.
| Matcher | Description |
|:--------|:------------|
|`Eq()`|`x == y`|
|`Ge()`|`x >= y`|
|`Gt()`|`x > y` |
|`Le()`|`x <= y`|
|`Lt()`|`x < y` |
|`Ne()`|`x != y`|
아래표에 selector라고 불리는 기능들인데, tuple을 비교하는 matcher와 함께 사용하면 유용합니다.
| Matcher | Description |
|:--------|:------------|
|`AllArgs(m)`|사실 단순히 `m`이라고 구현하는 것과 동일합니다. 즉, `EXPECT_THAT(std::make_tuple(2,1), Gt());`과 `EXPECT_THAT(std::make_tuple(2,1), AllArgs(Gt());`은 동일한 의미입니다. `.With`와 함께 `.With(AllArgs(m))`와 같이 사용하는 것도 좋습니다.|
|`Args<N1, N2, ..., Nk>(m)`|전달된 tuple에서 몇 개만 선택하고 이들을 matcher `m`을 통해 비교합니다. argument는 0부터 시작합니다. 예를 들어 `EXPECT_THAT(std::make_tuple(1,2,3), Args<1, 2>(Eq())`이라는 코드는 값 `2`와 `3`이 같은지 비교합니다.|
#### Composite Matchers
여러개의 matcher를 묶어서 실행할 수도 있습니다.
| Matcher | Description |
| :----------------------- | :----------------------------------------------------------- |
| `AllOf(m1, m2, ..., mn)` | `argument`는 `m1`부터 `mn`까지 모든 matcher를 만족해야 합니다. |
| `AnyOf(m1, m2, ..., mn)` | `argument`는 `m1`부터 `mn`까지 중에서 하나의 matcher만 만족하면 됩니다. |
| `Not(m)` | `argument`는 matcher `m`을 만족하면 안됩니다. |
#### Adapters for Matchers ###
| Matcher | Description |
|:--------|:------------|
|`MatcherCast<T>(m)`|Matcher `m`을 `Matcher<T>`로 변환합니다.|
|`SafeMatcherCast<T>(m)`| Matcher `m`을 `Matcher<T>`로 [safely casts](cook_book.md#casting-matchers) 변환합니다. |
|`Truly(predicate)`|`predicate`는 function이나 functor을 의미합니다. 이 때, `predicate(argument)`는 `boolean` compatible한 값을 반환해야 합니다.|
#### Using Matchers as Predicates
| Matcher | Description |
|:--------|:------------|
|`Matches(m)(value)`|`value`가 matcher `m`을 만족하면 `true`입니다. unary functor를 사용하듯이 단독으로 사용할 수 있습니다.|
|`ExplainMatchResult(m, value, result_listener)`|`value`가 matcher `m`을 만족하면 `true`입니다. 그 결과를 `result+listener`를 통해 출력합니다.|
|`Value(value, m)`|`value`가 matcher `m`을 만족하면 `true`입니다.|
#### Defining Matchers
| Matcher | Description |
| :----------------------------------------------------------- | :----------------------------------------------------------- |
| `MATCHER(IsEven, "") { return (arg % 2) == 0; }` | argument가 짝수인지 확인하는 `IsEven()`이라는 matcher를 정의한 것입니다. |
| `MATCHER_P(IsDivisibleBy, n, "") { *result_listener << "where the remainder is " << (arg % n); return (arg % n) == 0; }` | argument가 `n`으로 나누어 떨어지는지 확인하는 `IsDivisibleBy(n)`이라는 matcher를 정의한 것입니다. |
| `MATCHER_P2(IsBetween, a, b, std::string(negation ? "isn't" : "is") + " between " + PrintToString(a) + " and " + PrintToString(b)) { return a <= arg && arg <= b; }` | argument가 a보다 크고, b보다 작은지 확인하는 `IsBetween(a, b)`이라는 matcher를 정의한 것입니다. |
**Notes:**
1. `MATCHER*`macro는 function이나 class 내부에서는 사용하면 안 됩니다.
1. Matcher의 body`{}`를 구현할때는 *purely functional*하게 구현해야 합니다. 어떤 side-effect도 가지면 안됩니다. 즉, 순수하게 matcher로 전달되는 argument와 parameter의 값을 읽고 비교해서 그 결과를 알려주는 동작만 수행해야 합니다. 또한, 프로그램의 다른 부분을 수정해서는 안 되며 argument, parameter를 제외한 외부의 다른 정보에 영향을 받아서도 안됩니다. 자신의 기능을 독립적으로 수행해야 합니다.
1. `PrintToString(x)`를 사용하면 `x`가 어떤 타입이라도 string으로 변환시킬 수 있습니다.
#### Matcher를 Test Assertion처럼 사용하기
| Matcher | Description |
|:--------|:------------|
|`ASSERT_THAT(expression, m)`|`expression`의 값이 matcher `m`을 만족하지 않는다면 [fatal failure](../../googletest/docs/primer.md#assertions)를 발생시킵니다.|
|`EXPECT_THAT(expression, m)`|`expression`의 값이 matcher `m`을 만족하지 않는다면 non-fatal failure를 발생시킵니다.|
### Actions
**Actions**은 mock function이 어떤 행동을 해야하는지를 명세합니다.
#### Returning a Value
| Matcher | Description |
|:--------|:------------|
|`Return()`|Mock function은 `void` 타입을 반환하면서 종료합니다.|
|`Return(value)`|Mock function은 `value`를 반환하면서 종료합니다. 만약, mock function의 본래 return type과 `value`의 타입이 서로 다르다면, value의 타입을 선택하게 됩니다.|
|`ReturnArg<N>()`|Mock function으로 전달된 `N`번째(0부터 시작) argument를 반환합니다.|
|`ReturnNew<T>(a1, ..., ak)`|`new T(a1, ..., ak)`를 반환합니다. Action이 수행될때마다 매번 새로 생성됩니다.|
|`ReturnNull()`|Null pointer를 반환합니다.|
|`ReturnPointee(ptr)`|`ptr`이 가리키는 값을 반환합니다.|
|`ReturnRef(variable)`|변수 `variable`의 참조를 반환합니다.|
|`ReturnRefOfCopy(value)`|`value`를 복사하고 복사본의 참조를 반환합니다. 복사본의 life-time은 action의 life-time과 동일합니다.|
#### Side Effects
| Matcher | Description |
|:--------|:------------|
|`Assign(&variable, value)`|`value`를 variable에 저장합니다.|
|`DeleteArg<N>()`| `N`번째(0부터 시작) argument를 delete합니다. 따라서 해당 argument는 pointer 형태입니다. |
|`SaveArg<N>(pointer)`| `N`번째(0부터 시작) argument를 `*pointer`에 저장합니다. |
|`SaveArgPointee<N>(pointer)`| `N`번째(0부터 시작) argument가 가리키는 값을 `*pointer`에 저장합니다. |
|`SetArgReferee<N>(value)` | `N`번째(0부터 시작) argument가 참조하는 변수에 `value`를 저장합니다. |
|`SetArgPointee<N>(value)` |`N`번째(0부터 시작) argument가 가리키는 변수에 `value`를 저장합니다.|
|`SetArgumentPointee<N>(value)`|`SetArgPointee<N>(value)`과 동일하지만 deprecated 되었습니다. gMock v1.7.0 이후에 삭제될 예정입니다.|
|`SetArrayArgument<N>(first, last)`|[`first`, `last`)에 저장되어 있는 값을 `N`번째(0부터 시작) argument가 가리키는 array에 저장합니다. Array는 pointer 혹은 iterator일수도 있습니다. 이 때, action은 [`first`, `last`) 가 가리키는 값을 직접 소유하지는 않습니다.|
|`SetErrnoAndReturn(error, value)`|`error`에 `errno`를 저장하고, `value`를 반환합니다.|
|`Throw(exception)`|`expection`을 던집니다. 이 때, `exception`은 복사가능해야 합니다. gMock v1.1.0 이후부터 적용 되었습니다.|
#### Callable(Function, Functor, Lambda, Callback)을 Action처럼 사용하기
| Matcher | Description |
|:--------|:------------|
|`Invoke(f)`|`f`를 호출합니다. mock function이 전달받은 argument를 `f`에 그대로 전달합니다.|
|`Invoke(object_pointer, &class::method)`|`class::method`를 호출합니다. mock function이 전달받은 argument를 `class:method`에 그대로 전달합니다.|
|`InvokeWithoutArgs(f)`|`f`를 호출합니다. 이 때 mock function이 전달받은 argument는 전달하지 않습니다.|
|`InvokeWithoutArgs(object_pointer, &class::method)`|`class::method`를 호출합니다. 이 때 mock function이 전달받은 argument는 전달하지 않습니다.|
|`InvokeArgument<N>(arg1, arg2, ..., argk)`|Mock function의 `N`번째 argument(0부터 시작)를 호출합니다. 이 때, k개의 argument도 함께 전달합니다.|
`Invoked*`를 통해서 호출된 callable의 반환값을 action 전체의 반환값으로도 사용할 수 있습니다. 즉, `Return*`과 동일한 역할을 수행하게 됩니다.
만약, `Invoked*`에 사용할 callabe을 구현해야 한다면 사용하지 않을 parameter는 `Unused`로 선언하는 것을 추천합니다.
```cpp
using ::testing::Invoke;
double Distance(Unused, double x, double y) { return sqrt(x*x + y*y); }
...
EXPECT_CALL(mock, Foo("Hi", _, _)).WillOnce(Invoke(Distance));
```
`Invoke(callback)`와 `InvokeWithoutArgs(callback)`과 같은 action은 전달받은 `callback`에 대한 소유권을 갖게 됩니다. 따라서 해당 action과 `callback`의 life-time은 동일합니다. 그리고 `callback`의 타입은 base callback이어야만 합니다. 아래 예제와 같이 dervied callback을 사용하면 compile error가 발생합니다.
```c++
BlockingClosure* done = new BlockingClosure;
... Invoke(done) ...; // This won't compile!
Closure* done2 = new BlockingClosure;
... Invoke(done2) ...; // This works.
```
`InvokeArgument<N>(...)`을 사용할 때, 참조형식으로 argument를 전달하고 싶다면 `ByRef()`를 사용하면 됩니다.
```cpp
using ::testing::ByRef;
using ::testing::InvokeArgument;
...
InvokeArgument<2>(5, string("Hi"), ByRef(foo))
```
위 코드는 mock function으로 전달된 2번째 argument를 호출합니다. 그러므로 해당 mock function의 2번째 argument가 기본적으로 callable일 것입니다. 또한, 해당 callable을 호출할 때 3개의 argument(숫자 `5`, 문자열 `"Hi"`, `foo`라는 변수의 참조)도 같이 전달하고 있습니다.
#### Default Action
| Matcher | Description |
|:--------|:------------|
|`DoDefault()`|Default action을 수행합니다. Built-in default action 또는 `ON_CALL()`을 통해 사용자가 정의한 default action을 실행합니다.|
**Note:** 기술적인 문제로 인해 `DoDefault()`를 composite action에 사용할 수는 없습니다. 그렇게 되면 runtime error가 발생합니다. Composite action이란 여러개의 action을 조합하는 사용하는 것을 의미하며 바로 아래에서 계속 설명합니다.
#### Composite Actions
| Matcher | Description |
| :----------------------------- | :----------------------------------------------------------- |
| `DoAll(a1, a2, ..., an)` | `a1`부터 `an`까지 모든 action을 수행합니다. 전체의 반환값은 `an`의 반환값을 사용합니다. 나머지 action들은 `void`를 반환해야만 합니다. |
| `IgnoreResult(a)` | Action `a`를 수행하고 그 반환값은 무시합니다. 단, `a`의 return type은 `void`가 되면 안 됩니다. |
| `WithArg<N>(a)` | Mock function의 `N`번째(0부터 시작) argument를 action `a`에 전달합니다. |
| `WithArgs<N1, N2, ..., Nk>(a)` | Mock function의 argument들 중에서 몇 개를 골라서 action `a`에 전달합니다. |
| `WithoutArgs(a)` | Action `a`를 argument 없이 수행합니다. |
#### Defining Actions
| Matcher | Description |
| :-------------------------------------------- | :----------------------------------------------------------- |
| `ACTION(Sum) { return arg0 + arg1; }` | mock function의 첫번째, 두번째 argument의 합을 반환하는 `Sum()`이라는 action을 정의한 것입니다. |
| `ACTION_P(Plus, n) { return arg0 + n; }` | mock function의 첫번째 argument에 `n`만큼 더한 값을 반환하는 `Plus(n)`이라는 action을 정의한 것입니다. |
| `ACTION_Pk(Foo, p1, ..., pk) { statements; }` | `statements` 코드를 수행하는 `Foo(p1, ... pk)`라는 parameterized action을 정의한 것입니다. |
`ACTION*`macro를 function이나 class 내부에서 사용하면 안 됩니다.
### Cardinalities
Cardinalities는 mock function의 호출횟수를 명세하는데 사용합니다. 주로 `Times()`와 함께 사용합니다.
| Matcher | Description |
|:--------|:------------|
|`AnyNumber()`|호출되는 횟수에 제한이 없습니다.|
|`AtLeast(n)`|적어도 `n`회 이상 호출되어야 합니다.|
|`AtMost(n)`|많아도 `n`회를 넘겨서는 안됩니다.|
|`Between(m, n)`|`m`회 이상 `n`회 이하로 호출되기를 원합니다.|
|`Exactly(n) or n`|정확히 `n`회만큼 호출되기를 원합니다. 만약, `n`이 `0`이라면 호출되지 않기를 기대한다는 의미입니다.|
### Expectation Order
기본설정에서는 expectation들간의 *호출순서를 부여하지 않고 있습니다.* 사용자가 expectation들간에 호출순서를 지정하고 싶다면 2가지 방법을 사용할 수 있습니다. 이 때 2가지 방법을 따로 사용해도 되고 같이 사용해도 됩니다.
#### The After Clause
```cpp
using ::testing::Expectation;
...
Expectation init_x = EXPECT_CALL(foo, InitX());
Expectation init_y = EXPECT_CALL(foo, InitY());
EXPECT_CALL(foo, Bar())
.After(init_x, init_y);
```
위 코드는 `InitX()`와 `InitY()` 호출된 이후에 `Bar()`가 호출하기를 기대한다는 의미합니다.
만약, 아직 `Bar()` 이전에 수행되어야 할 function들을 확정할 수 없는 상황이라면 `ExpectationSet`을 사용하면 됩니다.
```cpp
using ::testing::ExpectationSet;
...
ExpectationSet all_inits;
for (int i = 0; i < element_count; i++) {
all_inits += EXPECT_CALL(foo, InitElement(i));
}
EXPECT_CALL(foo, Bar())
.After(all_inits);
```
위 코드는 `Bar()`가 `all_inits`에 포함된 모든 expectation들이 수행된 후에 호출되기를 기대한다는 의미입니다. 그러나 `all_inits `에 포함된 expectaion들간의 호출순서는 신경쓰지 않습니다. 오직 `Bar()`가 그 후에 호출되기만 하면 됩니다.
이렇게 구현하면 나중에 `all_inits`에 expectation이 추가 혹은 삭제되더라도 `Bar()`가 그 다음에 호출되어야 한다는 점은 변함이 없습니다.
#### Sequences
Expectation들이 많다면 **sequences**를 사용하는 것도 좋습니다. 각 expectation들이 별도의 이름을 가질 필요는 없으며 같은 이름의 sequence를 사용하기만 하면 됩니다. 그렇게 동일한 sequence에 포함된 expectation들은 순서대로 호출되어야 합니다.
```cpp
using ::testing::Return;
using ::testing::Sequence;
Sequence s1, s2;
...
EXPECT_CALL(foo, Reset())
.InSequence(s1, s2)
.WillOnce(Return(true));
EXPECT_CALL(foo, GetSize())
.InSequence(s1)
.WillOnce(Return(1));
EXPECT_CALL(foo, Describe(A<const char*>()))
.InSequence(s2)
.WillOnce(Return("dummy"));
```
위의 코드는 `Reset()`이 다른 2개의 function인 `GetSize()`, `Describe()`보다 먼저 호출되기를 기대한다는 의미입니다. 그 외의 다른 호출순서는 없습니다. 예를 들면 `GetSize()`, `Describe()`라는 2개의 function은 누가 먼저 호출되든 상관이 없습니다. 왜냐하면 2개의 function은 동일한 sequence로 연결되지 않았기 때문입니다.
Sequence를 좀 더 편리하게 사용하는 방법은 아래와 같습니다.
```cpp
using ::testing::InSequence;
{
InSequence seq;
EXPECT_CALL(...)...;
EXPECT_CALL(...)...;
...
EXPECT_CALL(...)...;
}
```
위의 코드는 `seq`라는 scope에 속한 모든 expectation들이 순서대로 호출되기를 기대한다는 의미입니다. 여기서 `seq`라는 이름 자체에는 큰 의미가 없습니다.
### Verifying and Resetting a Mock
gMock은 어떤 mock object가 소멸될 때, 해당 mock object에 설정된 expectation들의 수행결과를 확인해줍니다. 만약 좀 더 일찍 확인하고 싶다면 아래처럼 하면 됩니다.
```cpp
using ::testing::Mock;
...
// Verifies and removes the expectations on mock_obj;
// returns true iff successful.
Mock::VerifyAndClearExpectations(&mock_obj);
...
// Verifies and removes the expectations on mock_obj;
// also removes the default actions set by ON_CALL();
// returns true iff successful.
Mock::VerifyAndClear(&mock_obj);
```
gMock이 mock object에서 발생하는 memory leak에 대해서는 점검하지 않도록 설정하는 것도 가능합니다.
```cpp
Mock::AllowLeak(&mock_obj);
```
### Mock Classes
gMock은 편리하게 사용할 수 있는 mock class template도 제공합니다.
```cpp
class MockFunction<R(A1, ..., An)> {
public:
MOCK_METHOD(R, Call, (A1, ..., An));
};
```
이에 대한 예제코드는 [여기](cook_book.md#using-check-points)를 참조하세요.
### Flags
| Flag | Description |
|:--------|:------------|
| `--gmock_catch_leaked_mocks=0` | Mock object에서 발생하는 memory leak에 대해서는 failure로 취급하지 않습니다. |
| `--gmock_verbose=LEVEL` | gMock에서 출력하는 정보의 양을 조절합니다.(`info`, `warning`, or `error`) |

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,452 @@
## gMock for Dummies
### gMock이란 무엇인가?
프로토타입 혹은 테스트 프로그램을 구현할 때, 관련된 모든 소프트웨어를 구현하고 실행하는 것은 사실상 불가능합니다. 이러한 경우, 아직 구현하지 못한 소프트웨어(real object)들을 대체하기 위해서 그와 동일한 interface를 갖는 mock object를 만들어서 적용한다면 runtime에 마치 real object가 있는 것처럼 동작시킬 수 있습니다. 예를 들어서 "어떤 function을 호출할지, 그러한 function은 어떤 argument를 통해 몇 번 호출되어야 하는지, return value는 어떻게 할지, function이 여러개라면 그들간의 호출순서는 어떠한지" 등을 사용자가 자유롭게 지정할 수 있게 됩니다.
**Note**: Mock object라는 용어와 *fake object*라는 용어는 혼동하기 쉽습니다. 그러나 TDD(Test-Driven Development) 관점에서 보면 fake와 mock의 의미는 매우 다릅니다.
- **Fake** object는 동작에 필요한 실제 구현부가 있고 그 결과도 real object와 동일해야 합니다. 다만, real object보다 간단하거나 빠르게 동작할 수 있도록 구현합니다. 대표적인 예로는 데이터베이스 혹은 파일시스템 처리를 메모리에서 수행하도록 구현하는 것이 있습니다.
- **Mock** object는 *expectation*을 통해 수행할 동작을 지정할 수 있습니다. 여기서 expectation이란 mock object(엄밀히는 mock object의 method)가 어떤 일을 하고 또 어떤 결과를 만들어내야 하는지 그 기대하는 바를 미리 지정하는 것을 의미합니다
지금 시점에서 위의 개념이 추상적이고 구별이 안될 수도 있지만 너무 걱정할 필요는 없습니다. 일단은 mock이 테스트 대상코드와 mock 간의 상호작용(interaction) 검증을 가능하도록 한다는 것만 기억하기 바랍니다. 앞으로 mock을 사용하다 보면 fake와 mock의 차이를 좀 더 명확히 알게 될 것입니다.
**gMock**이란 mock class를 만들고 사용하기 위한 function, macro 등을 제공하는 library 입니다. (때때로 "framework" 이라고 부르기도 합니다.) 이를 통해 Java의 jMock, EasyMock에서 제공하는 다양한 기능을 C++에서도 사용할 수 있게 됩니다.
gMock을 사용할 때는 기본적으로 아래 과정을 거치게 됩니다.
- 먼저, gMock에서 제공하는 macro를 이용해서 테스트대상(interface)을 mocking하고 해당 mock class에 필요한 내용을 추가합니다.
- 다음으로 mock object를 만들어서 expectation, behavior를 지정합니다. 이를 위한 매우 직관적인 문법의 기능들을 제공하고 있습니다.
- 이제 mock object를 사용하는 코드를 실행하면 됩니다. gMock은 위에서 지정한 expectation중에 만족하지 않는 부분을 찾아내서 알려줍니다.
### 왜 gMock 인가?
상술한 것처럼 mock object는 소프트웨어 간에 존재하는 의존성을 제거해주기 때문에 개발자가 테스트 프로그램 또는 프로토타입 소프트웨어를 개발할 때 더 빠르고 신뢰성있게 구현할 수 있도록 도와줍니다. 만약 gMock을 사용하지 않고서 mock object를 직접 구현하려 한다면 상당한 *어려움에 직면할 것입니다.* 그 이유는 아래와 같습니다.
- 먼저 mock도 소프트웨어이므로 누군가는 그것을 구현해야 합니다. 그런데 그러한 작업이 힘들고 시간이 많이 소모되는 작업이었기 때문에 C++ 개발자들이 mock을 멀리하는 주된 원인이 되었습니다.
- Mock을 구현하는데 성공했다고 하더라도 동작이 완벽하지 않을 수 있습니다. 물론 많은 시간을 투자하면 쓸만한 mock을 만들수도 있지만 긴 시간이 필요한 작업은 효율성 측면에서 좋지 않습니다.
- Mock을 개발함에 있어서 뚜렷한 규칙이 없다면 새로운 mock을 만들때마다 이전에 고생했던 만큼의 노력이 필요합니다.
Java, Python 같은 언어에서는 이미 mock을 자동으로 생성가능한 효율적인 mock framework([jMock](http://www.jmock.org/), [EasyMock](http://www.easymock.org/), [Mox](https://code.google.com/archive/p/pymox/wikis/MoxDocumentation.wiki))을 제공하고 있습니다. 그 결과 mocking이 효율적인 기술이라는 것이 증명되었으며 여러 커뮤니티를 통해서 관련 기술이 발전되고 있습니다. 그러나 C++ 에서는 mock을 위한 좋은 도구가 없었기 때문에 격차가 벌어지게 되었습니다.
gMock은 이렇게 어려움을 겪고 있는 C++ 프로그래머들을 돕기 위해 만들어졌습니다. jMock, EasyMock으로부터 많은 아이디어를 가져온 것은 맞지만, 거기에 더해 C++의 특징을 잘 살려서 설계했습니다. C++을 사용하면서 아래와 같은 문제들로 어려움을 겪고 있다면 이제부터는 gMock이 좋은 친구가 되어줄 것입니다.
- 설계가 문제없는지 프로토타입을 통해 확인하고 싶지만 프로토타입을 "빠르게" 만드는 것이 어려울 때
- 너무 많은 리소스(라이브러리, 데이터베이스 등)를 사용함으로 인해서 테스트의 속도가 느린 경우
- 네트워크와 같이 실패가능성이 있는 리소스를 사용함으로 인해서 테스트도 뜻하지 않게 실패하는 일이 발생할 떄
- 오류처리에 관한 테스트를 하고 싶지만, 해당 오류가 발생하는 빈도자체가 낮아서 테스트가 어려울 때 (예: file checksum 오류 등)
- 모듈간에 데이터를 주고받는 내부과정을 들여다보고 싶은데 잘 안되서 최종적인 수행결과만 확인하고 있는 경우
- 의존성 제거를 위해서 mock을 사용하고 싶지만, mock을 직접 구현하기가 싫은 경우
마지막으로 gMock을 아래와 같은 용도로 사용하기를 권장합니다.
- *설계도구* 로서 사용하기 바랍니다. 이를 통해 설계에 문제가 없는지 빠르게 확인하고 문제가 있다면 개선할 수 있습니다
- *테스트도구* 로서 사용하기 바랍니다. 외부와 연결되는 의존성을 제거하고 모듈간의 상호작용을 확인할 수 있습니다
### 시작하기
gMock은 googletest와 함께 제공되는 번들소프트웨어입니다.
### 예시: Mock Turtles
먼저 우리가 어떤 그래픽 관련 프로그램을 개발하고 있다고 가정합시다. 또한, 렌더링 API로는 [LOGO](https://en.wikipedia.org/wiki/Logo_(programming_language))라는 것을 사용하고 있다고 합시다. 과연 이 프로그램은 어떻게 테스트하면 좋을까요? 네, 가장 쉬운 방법은 역시 해당 API를 사용해서 그림을 그린 후, 그 결과를 golden image와 비교하는 것입니다. (golden image = "OK"라고 판단할 수 있는 비교대상) 그러나 이런 테스트는 시간이 많이 걸리고 깨지기도 쉬운 테스트 방법입니다. 만약, 그래픽카드를 변경했더니 anti-aliasing 동작도 바뀌었다고 생각해보십시오. 그렇게 되면 golden image도 변경되어야만 합니다. 왜냐하면 그려진 그림의 결과가 그래픽카드에 종속되어 있기 때문입니다. 테스트가 많으면 많을수록 golden image를 변경하는 작업도 매우 힘들고 귀찮은 일이 될 것입니다. 이제부터 mock을 사용해서 이러한 문제를 해결해 보겠습니다. 그럼 본격적으로 시작하기에 앞서 의존성 주입([Dependency Injection](https://en.wikipedia.org/wiki/Dependency_injection)) 패턴을 어느정도 이해하고 있다면 도움이 될 것입니다. 물론 완벽히는 몰라도 괜찮으며 앞으로 나오는 내용들을 따라가다 보면 그 의미를 자연스럽게 알게 될 것입니다. 그럼 이제 `Turtle`이라는 interface를 하나 정의하고 본격적인 설명을 시작하겠습니다.
```cpp
class 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;
};
```
(`Turtle`의 destructor는 virtual function으로 선언해야만 합니다. C++ 에서 base class의 destructor가 `virtual`로 선언되어야만 derived class들의 destructor를 자동으로 호출해주기 때문입니다. 그렇지 않으면 당연히 memory leak과 같은 문제가 발생할 수 있습니다.)
`PenUp()`, `PenDown()`는 turtle(거북이)이 움직일 때 흔적을 남길지 말지 결정합니다. `PenDown()`이 호출되면 곧 "펜"을 쓴다는 의미이므로 흔적을 남기게 됩니다. `Forward()`, `Turn()`, `GoTo()`는 turtle이 어느 방향으로 이동할지 지정합니다. 마지막으로 `GetX()`, `GetY()`는 turtle의 현재 위치를 알려줍니다.
이렇게 정의된 interface를 real class와 mock class로 나눠서 구현하게 됩니다. 이 때, real class를 구현하는 것은 실제로 많은 시간과 노력이 필요할 수도 있기 때문에 mock class를 먼저 구현함으로써 해당 interface의 동작을 미리 그리고 빠르게 검증해 볼 수 있습니다. 어떤 argument를 가지고 어떤 function을 호출할 것인가, 또 function 간의 호출순서는 어떠한가 등을 검증할 수 있게 됩니다. 이를 통해서 쉽고 빠르게 원하는 바를 확인할 수 있으며 그러면서도 여전히 강력하고 유지보수 측면에서도 더 좋은 코드를 구현할 수 있습니다. 또한, 위에서 언급한 그래픽카드 변경에 따른 golden image 교체작업도 할 필요가 없어집니다. 왜냐하면 mock class를 사용한 검증이란 어떤 코드의 수행결과나 바이너리를 확인하는 것이 아니며 그 수행과정 자체를 코드레벨에서 확인하는 것이기 때문입니다. 마지막으로 역시 제일 중요한 것은 mock class를 사용하면 이러한 일련의 작업이 *매우 매우 빠르다는 것입니다.*
### Mock Class 작성하기
물론 mock이 이미 구현되어 있다면 좋겠지만, 그렇지 않은 경우에는 직접 만들어야 합니다. gMock을 사용하면 이러한 작업도 (대부분의 경우에) 매우 쉽게 진행할 수 있습니다.
#### 정의하는 방법
`Turtle` interface를 계속 사용해 보겠습니다. 기본적으로 아래와 같은 단계를 거칩니다.
- `Turtle` interface를 상속받는 `MockTurtle` class를 생성합니다.
- `Turtle` interface에 정의한 *virtual* function의 이름과 해당 function이 몇 개의 argument를 가지고 있는지 확인합니다. (상속 대신 템플릿을 이용하는 것도 가능하지만 상속보다는 약간 어렵습니다, 관련내용은 [여기](cook_book.md#mocking-nonvirtual-methods)를 참조하세요.)
- Derived class의 `public:` 영역에 `MOCK_METHOD();` macro를 사용합니다.
- 다음으로는 `Turtle` interface의 function signature에서 *function name*, *return type*, *argument*들을 복사해서 `MOCK_METHOD();` 에 그대로 붙여 넣으세요 (아래 예제코드 참조)
- 만약, const method를 mocking하고 있다면 4번째 parameter에 `(const)`를 추가해주세요.
- Virtual method를 overriding하고 있는 것이므로 `override`키워드를 사용하기를 추천합니다. 그렇게 되면 4번째 parameter는 `(override)` 혹은 `(const, override)`가 될 것입니다.
- 위의 내용을 `Turtle` interface의 다른 모든 virtual function에 대해서도 수행하세요. 알고 계시겠지만 C++에서 interface(abstract class)를 상속받는 derived class는 interface의 pure virtual method를 전부 override해야만 object를 생성할 수 있습니다.
위의 과정을 거치면 최종적으로 아래와 같은 모양이 될 것입니다
```cpp
#include "gmock/gmock.h" // Brings in gMock.
class MockTurtle : public Turtle {
public:
...
MOCK_METHOD(void, PenUp, (), (override));
MOCK_METHOD(void, PenDown, (), (override));
MOCK_METHOD(void, Forward, (int distance), (override));
MOCK_METHOD(void, Turn, (int degrees), (override));
MOCK_METHOD(void, GoTo, (int x, int y), (override));
MOCK_METHOD(int, GetX, (), (const, override));
MOCK_METHOD(int, GetY, (), (const, override));
};
```
위와 같이 했다면 다른 추가작업은 필요 없습니다. 나머지 작업은 `MOCK_METHOD` macro가 알아서 해줍니다. 이렇듯 간단하게 mock class와 mock function을 정의할 수 있습니다. 조금만 익숙해지면 매우 빠르게 mock class를 생성할 수 있을 것입니다.
#### 어떤 파일에 저장해야 할까?
Mock class를 정의할 때, 해당 class를 실제로 어디에 저장해야 할지도 고민해 볼 부분입니다. 예를 들어 `Foo`라는 interface가 있다면 이것을 사용하는 모든 개발자가 본인만의 공간에 본인만의 테스트파일(예: `*_test.cc`)을 만들고 `MockFoo` class를 구현할 수 있습니다. 물론, 이렇게 해도 동작상에 문제는 없지만 유지보수 측면에서 어려움이 발생할 것입니다. 왜냐하면 `Foo` interface가 고쳐졌다고 가정하면, 독자적으로 이것을 상속받아 사용하던 모든 테스트가 실패하거나 영향을 받기 때문입니다.
따라서 되도록이면 interface와 mock class는 같은 패키지에 있는 것이 좋습니다. (이 경우에 production 빌드와 test 빌드를 확실히 구분해서 둘이 섞이지 않도록 주의해야 합니다.) 즉, `Foo`가 포함된 패키지안에 `mock_foo.h`라는 파일을 만들고 `MockFoo`를 구현하십시오. 그리고 모든 개발자가 `mock_foo.h`를 사용하게 하십시오. 이렇게 되면 `Foo`가 변경되더라도 1개의 `MockFoo`만 수정하면 되기 때문에 유지보수 측면에서 도움이 됩니다.
또 다른 방법으로 `Foo` interface보다 더 상위에 `FooAdaptor`라는 새로운 interface를 만드는 것입니다. 이러한 패턴은 외부로 노출되는 `FooAdaptor`는 쉽게 변하지 않도록 유지하면서도 내부에서는 자유롭게 변경작업이 진행될 수 있도록 도와줍니다. 물론 초반에 해야할 일이 많아지는 것은 사실이지만 코드의 가독성을 높이고 구현난이도를 낮춰주는 장점이 있기 때문에 장기적으로 볼 때는 좋은 방법이라고 할 수 있습니다.
### 테스트에서 Mock 이용하기
Mock class를 생성했다면, 이후에는 아래와 같은 방법으로 진행합니다.
- gMock 관련 기능을 사용하기 위해서 `testing` namespace를 포함시킵니다. (한 파일에 한 번씩만 해야 합니다.)
- Mock object를 생성합니다.
- Mock object에 expectation을 지정합니다. (어떤 method를 호출할지, 어떤 argument를 전달받을지, 어떤 동작을 할지 등)
- Mock object를 사용하는 실제코드를 수행합니다. 이 때 expectation으로 지정된 횟수 및 argument정보와 다르게 호출되는 부분이 있다면 즉시 에러로 검출됩니다. (결과확인을 위해 googletest assertion을 추가로 사용하는 것도 좋습니다.)
- Mock object가 소멸될 때, gMock은 설정한 *모든* expectation들이 만족되었는지 확인해 줍니다.
4단계는 각각의 expectation이 호출될 때마다 잘못된 부분을 검출해서 알려주는 것이고 5단계는 해당 mock object에 지정했던 모든 expectation들을 다시 한 번 확인해주는 것이기 때문에 다릅니다. 출력되는 내용도 조금 다른데 디버깅에 필요한 정보는 일반적으로 4단계에서 더 많이 출력됩니다.
아래에 예제코드가 있습니다.
```c++
#include "path/to/mock-turtle.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
using ::testing::AtLeast; // #1
TEST(PainterTest, CanDrawSomething) {
MockTurtle turtle; // #2
EXPECT_CALL(turtle, PenDown()) // #3
.Times(AtLeast(1));
Painter painter(&turtle); // #4
EXPECT_TRUE(painter.DrawCircle(0, 0, 10)); // #5
}
```
위 코드를 간단히 해석해보면 보면, `PenDown()`이 적어도(AtLeast) 한 번 이상 호출되도록 expectation이 설정되었습니다. 따라서 `painter` object의 method인 `DrawCircle()`이 내부에서 `PenDown()`을 호출하지 않는다면 아래와 같은 에러가 발생할 것입니다.
```bash
path/to/my_test.cc:119: Failure
Actual function call count doesn't match this expectation:
Actually: never called;
Expected: called at least once.
Stack trace:
...
```
**Tip 1:** Emacs buffer에서 테스트 프로그램을 실행하는 사람은 line number에서 바로 실패한 부분(expectation)으로 이동할 수 있습니다.
**Tip 2:** Mock object가 소멸되지 않았다면 최종결과도 출력되지 않습니다. 따라서 mock을 지역변수가 아닌 heap에 할당하는 경우에는 heap leak checker등을 사용해서 누수되는게 없는지 확인해볼 필요가 있습니다. `gtest_main` library를 사용하면 heap leak checker기능이 자동으로 포함됩니다.
**Important note:** 당연하겠지만 expectation은 mock function이 호출되기 **전에** 설정되어야 합니다. 그렇지 않은 경우의 동작은 **미정의**이기 때문에 문제가 발생할 것입니다. 그리고 `EXPECT_CALL()`을 여러개 사용할 때는 `EXPECT_CALL()`들의 사이에 mock function을 끼워넣으면 안됩니다. `EXPECT_CALL()`들을 먼저 지정하고 mock function들을 호출하기 바랍니다.
`EXPECT_CALL()` 은 *미래에 일어날 함수호출을* 미리 지정함을 의미합니다. 왜 gMock은 이렇게 동작할까요? 왜냐하면 expectation은 말 그대로 기대하는 바를 알려주는 것이기 때문입니다. 또한, 미리 지정해야만 잘못된 동작으로 인해 실행환경(stack 등)이 깨지기 전에 문제를 잡아내고 사용자에게 알려줄 수 있습니다. 예를 들어, stack이 이미 깨진 상태에서 제공되는 정보는 디버깅에 도움이 되지 않을 것입니다.
위의 코드는 단순한 예제이긴 합니다. 앞으로 gMock이 제공하는 보다 다양한 기능을 확인하게 될 것입니다.
### Expectation 설정하기
"mock object를 잘 쓰고 있다"라고 말하려면 역시 *expectation을 제대로* 사용해야 합니다. 이러한 expectation를 사용하는 것도 쉽지는 않습니다. 먼저 expectation을 너무 엄격하게 지정하면 테스트가 소소한 변화에도 영향을 받아서 실패하게 됩니다. 반대로 너무 느슨하게 지정하면 잡아내야 할 bug를 검출하지 못 하게 됩니다. 누구나 테스트 프로그램이 bug를 정확히 잡아내서 목적한 바를 이루길 원할텐데요. gMock은 이러한 목적달성을 위해 안성맞춤인 기능들을 제공하고 있습니다.
#### 일반적인 문법
gMock에서는 `EXPECT_CALL()` macro를 사용하여 mock method에 대한 expectation을 지정합니다. 기본적인 사용법은 아래와 같습니다.
```cpp
EXPECT_CALL(mock_object, method(matchers))
.Times(cardinality)
.WillOnce(action)
.WillRepeatedly(action);
```
`EXPECT_CALL()`는 두 가지 argument를 전달받습니다. 첫 번째는 mock object이며 두 번째는 mock method(+ its argument)입니다. 여기서 두 가지가 점(`.`)이 아니라 콤마(`,`)로 구분된다는 것도 주의하기 바랍니다. (기술적인 이유로 그렇게 구현되었습니다.)
만약, 해당 mock method가 overloaded method가 아니라면 matcher 없이 바로 사용할 수도 있습니다.
```c++
EXPECT_CALL(mock_object, non-overloaded-method)
.Times(cardinality)
.WillOnce(action)
.WillRepeatedly(action);
```
`EXPECT_CALL()`을 matcher없이 사용하게 되면 "called with any arguments"를 가능하게 해줍니다. 즉, argument의 타입이나 개수를 굳이 쓰지 않아도 됩니다. (overloaded method는 이름이 같은 method가 여러개 존재할 수 있기 때문에 matcher없이는 사용할 수 없습니다.)
`EXPECT_CALL()`에는 매우 다양한 clause를 붙일 수 있습니다. 각각에 대해서는 다음 섹션에서 계속 설명합니다.
이처럼 expectation을 지정하는 문법은 마치 영어를 읽는 것처럼 자연스럽게 읽을 수 있도록 설계되었습니다. 아래 예제를 보면,
```cpp
using ::testing::Return;
...
EXPECT_CALL(turtle, GetX())
.Times(5)
.WillOnce(Return(100))
.WillOnce(Return(150))
.WillRepeatedly(Return(200));
```
이것은 곧 `turtle` object의 `GetX()`는 총 5번 호출되는데, 첫 번째 호출에서는 100을 반환하고, 두 번째 호출에서는 150을 반환하고, 그 다음부터는 계속해서 200을 반환한다는 expectation을 의미합니다. 어떤 사람들은 이러한 스타일을 Domain Specific Language(DSL)라고 부르기도 합니다.
**Note:** `EXPECT_CALL`은 왜 macro 형태로 구현되었을까요? 여기에는 두 가지 이유가 있습니다. 첫 번째로 검색하기가 편리합니다. 원하는 `EXPECT_CALL`을 찾을 때, `gsearch`와 같은 도구와 연동해서 쉽게 찾아낼 수 있습니다. 더불어 사람의 눈으로 직접 확인할 때도 macro 형태의 코드가 더 알아보기 쉬울 것입니다. 두 번째로 테스트가 실패했을 때, gMock이 해당 expectation의 위치를 알려주기가 용이합니다. 또한, 그 위치를 참고하는 사용자 입장에서도 더 편리할 것입니다. 물론, 소소한 이유들이긴 하지만 이런 부분들이 모이면 테스트가 실패해서 디버깅을 해야할 때 도움이 될 것입니다.
#### Matchers: 전달되기를 바라는 argument 지정하기
먼저, gMock에서는 mock function으로 전달되는 argument의 기대값을 지정하는 것이 가능합니다.
```c++
// Expects the turtle to move forward by 100 units.
EXPECT_CALL(turtle, Forward(100));
```
위 코드는 `Forward()`가 호출될 때, argument로 `100`이 전달되기를 기대한다는 의미입니다. 적법한 코드입니다. 다만, 상황에 따라서 이처럼 구체적인 값까지는 필요하지 않을 수도 있습니다. 앞에서도 언급한 것처럼 너무 엄격한 테스트는 깨지기 쉽고 때때로 테스트의 가독성을 떨어트리기도 하기때문에 꼭 필요한 만큼만 지정하는 것이 중요합니다. 테스트가 쉽게 깨지지 않으면서도 bug를 잘 찾아낼 수 있도록 계속해서 고민해야 합니다. 다시 본론으로 돌아오면 `Forward()`로 전달되는 argument가 테스트의 목표에 큰 영향을 끼치지 않는다면 `_`라고 표시하는 것도 좋은 방법입니다. 여기서 `_`는 어떤 값이라도 괜찮다는 것을 의미하며 관련 예제가 아래에 있습니다.
```c++
using ::testing::_;
...
// Expects that the turtle jumps to somewhere on the x=50 line.
EXPECT_CALL(turtle, GoTo(50, _));
```
위 코드는 "`GoTo()`라는 mock function의 첫번째 argument에는 정확히 `50`이 전달되어야 하고, 두번째 argument에는 어떤 값이라도 올 수 있다"를 의미합니다. 여기서 두번째 argument에 사용한 `_`이 바로 **matcher**의 한 종류입니다. 이렇듯 matcher는 어떤 값이 전달되기를 기대하는지에 대해 좀 더 세밀한 조작이 가능하도록 도와주는 기능이며 `EXPECT_CALL()`과 함께 사용하면 매우 유용합니다. (꼭 `EXPECT_CALL()`이 아니더라도 사용할 수는 있습니다.)
사실 `50`, `100`처럼 값으로 구현한 코드들도 내부적으로는 `Eq(50)`, `Eq(100)`과 같이 matcher를 사용하는 코드로 변환됩니다. (`Eq()`는 값이 같은지 확인하는 matcher입니다.) 한 가지 중요한 점은 `Eq()`를 사용하든 값을 사용하든 해당 타입은 `operator==`를 지원해야 한다는 점입니다. 그래야만 gMock이 비교를 수행할 수 있기 때문에 `operator==`를 지원하지 않는 타입에 `Eq()`를 사용하려면 해당 연산자를 직접 구현해서 제공해야 합니다. 마지막으로 gMock은 `_`나 `Eq()` 외에도 다양한 built-in matcher들을 제공하고 있으며 이들을 확인하려면 [built-in matcher]()를 참고하시기 바랍니다. 더불어 새로운 matcher를 직접 구현하고자 한다면 [custom matcher]()를 참조하시기 바랍니다.
아래 코드는 `Ge()`라는 또 다른 built-in matcher를 사용한 예제이며 그 의미는 "`turtle.Foward()`로 전달되는 argument가 100보다 크거나 같아야 한다"를 의미입니다.
```cpp
using ::testing::Ge;
...
// Expects the turtle moves forward by at least 100.
EXPECT_CALL(turtle, Forward(Ge(100)));
```
위 코드도 마찬가지로 `Forward(_)`와 같이 구현하게 되면 어떤 argument가 전달돼도 괜찮음을 의미합니다. `_`는 사실 좀 더 간결하게 구현하는 것도 가능한데요, 아래처럼 argument 부분을 아예 구현하지 않으면 `_`를 사용한 것과 동일한 의미로 동작합니다. 단, 이렇게 argument 부분을 생략하는 방법은 대상 mock function이 non-overloaded일 때만 사용할 수 있습니다.
```c++
// Expects the turtle to move forward.
EXPECT_CALL(turtle, Forward);
// Expects the turtle to jump somewhere.
EXPECT_CALL(turtle, GoTo);
```
#### Cardinalities: 몇 번 호출되어야 하는지 그 횟수를 지정하기
위에서 `EXPECT_CALL()`과 함께 사용되는 `Times()`라는 clause를 본 적이 있습니다. 이 `Times()`가 전달받는 argument를 바로 **cardinality**라고 부르며 그 의미는 "*mock method가 n번 호출되기를 기대한다*"입니다. 이러한 cardinality를 사용하면 동일한 expectation을 중복해서 구현하지 않고도 어떤 method가 여러번 호출되어야 한다는 것을 쉽게 표현할 수 있습니다. 또한, matcher의 `_`와 같이 cardinality도 "fuzzy"하게 지정할 수 있기 때문에 사용자가 테스트의 목적을 보다 쉽게 표현할 수 있습니다.
`Times(0)`은 좀 특이한 형태입니다. 코드를 보고 예측하셨다시피 이것은 "mock method가 지정된 argument와 함께 호출되서는 안된다"를 의미합니다. 따라서 gMock은 `Times(0)`으로 지정된 mock method가 호출되면 테스트실패로 판단합니다.
`AtLeast(n)`도 본 적이 있을 것입니다. `AtLeast(n)`은 fuzzy cardinalities 중 하나이며 "최소한 `n`번 이상 호출되기를 기대한다"라는 의미로 사용됩니다. gMock이 제공하는 다양한 built-in cardinalities는 [cheat_sheet]()을 계속해서 확인할 수 있습니다.
마지막으로 사용자가 `Times()`를 사용하지 않은 경우에는 **gMock이 cardiniality를 추론**하게 되는데, 이 때 적용되는 추론규칙은 아래와 같습니다.
- `WillOnce()`와 `WillRepeatedly()` 가 모두 없다면, cardinality → `Times(1)`
- `WillOnce()`가 `n`개(>=1) 있고 WillRepeatedly() 는 없다면, cardinality → `Times(n)`
- `WillOnce()`가 `n`개(>=0) 있고 WillRepeatedly() 가 있다면, cardinality → `Times(AtLeast(n))`
**Quick quiz**: 어떤 function이 2회 호출될 것으로 expectation 설정되었지만, 실제로는 4번 호출되었다면 어떤일이 발생할까요?
#### Actions: 수행해야 할 동작 지정하기
위에서 `MockTurtle` class를 생성할 때, 각각의 mock method를 실제로 구현하지는 않았습니다. 왜냐하면 `MOCK_METHOD` macro를 사용하면 gMock이 기본적인 구현을 해주기 떄문입니다. 그러나 테스트의 목적을 명확히 표현하려면 mock method가 호출되었을 때 수행해야 할 동작을 직접 명세해주는 것이 좋습니다. 이 때, mock method가 수행해야 할 동작들을 action이라는 명칭으로 부르게 되며 gMock은 이를 위한 다양한 기능을 제공하고 있습니다.
먼저, mock function의 return type이 기본자료형 혹은 포인터라면 사용자가 별도의 action을 지정하지 않더라도 default action을 수행해줍니다. 예를 들어 `void` 타입은 그냥 `return`, `bool` 타입은 `return false`, 다른 타입들은 `return 0`을 수행해줍니다. 게다가 C++11 이후 버전부터는 mock function의 return type이 꼭 기본자료형이 아니더라도 default constructor를 가지고 있는 타입이라면 해당 default constructor를 자동으로 호출해 줍니다. 이처럼 gMock은 사용자가 아무런 action을 지정하지 않아도 가능한 최대한의 default action을 알아서 수행해줍니다.
그렇더라도 해당 mock method에 대한 default action이 없는 경우는 여전히 존재하며 default action만으로는 원하는 목적을 달성할 수 없는 경우도 많이 있습니다. 이런 경우에 사용자가 직접 action을 지정하는 것도 어렵지 않습니다. `WillOnce()` 혹은 `WillRepeatedly()`를 통해 action을 지정할 수 있으며 아래에 간단한 예제가 있습니다.
```cpp
using ::testing::Return;
...
EXPECT_CALL(turtle, GetX())
.WillOnce(Return(100))
.WillOnce(Return(200))
.WillOnce(Return(300));
```
위 코드는 `turtle.GetX()`가 3번 호출되기를 기대한다는 의미입니다. (`WillOnce()`가 `Times(1)`이라는 의미로 추론되기 때문입니다.) 따라서 `GetX()`가 3번 호출되면 호출될 때마다 `100`, `200`, `300`을 순서대로 반환합니다.
```cpp
using ::testing::Return;
...
EXPECT_CALL(turtle, GetY())
.WillOnce(Return(100))
.WillOnce(Return(200))
.WillRepeatedly(Return(300));
```
위 코드는 `turtle.GetY()`가 적어도 2번 호출되기를 기대한다는 의미입니다. (`WillOnce()`가 2번 사용됐기 때문에 기본적으로 2번은 호출되어야 합니다.) 따라서 `GetY()`가 호출되면 처음에는 `100`을 반환하고 다음에는 `200`을 반환하며 세번째부터는 `300`을 반환합니다.
물론, `Times()`를 통해서 호출횟수를 명시적으로 지정해도 됩니다. 그렇게 되면 gMock은 `WillOnce()`로부터 호출횟수를 추론하지 않습니다. `Time()`에 지정된 값이 `WillOnce()`보다 많으면 어떻게 될까요? 그런 경우에는 default action이 수행됩니다. 만약, 지정된 횟수를 초과했을 때 default action이 사용되는게 싫다면 `WillRepeatedly()`를 사용해서 default action을 대체하면 됩니다.
`Return()`말고도 다양한 action이 존재합니다. 예를 들면, `ReturnRef(variable)`을 사용해서 참조타입을 반환할 수도 있고 미리 구현해놓은 다른 함수를 연계적으로 호출할 수도 있습니다. 더 자세한 내용은 [여기](CheatSheet.md#actions)를 참고하세요.
**Important note**: `EXPECT_CALL()`에 action을 지정할 때, 값이 아니라 변수(또는 참조, 포인터)를 사용하면 어떻게 될까요? 과연 사용자가 원하는 대로 잘 동작할까요? 아래 예제와 함께 설명하겠습니다.
```cpp
using ::testing::Return;
...
int n = 100;
EXPECT_CALL(turtle, GetX())
.Times(4)
.WillRepeatedly(Return(n++));
```
위의 mock function이 100, 101, 102... 를 순서대로 반환할까요? 그렇지 않습니다. 결과만 놓고 보면 항상 100을 반환합니다. 왜냐하면 `n++`이라는 코드가 수행되는 시점의 값인 `100`이 gMock 내부에 저장되고 또 사용되기 때문입니다. 다시 말해서 `GetX()`가 실제로 호출되는 시점의 `n` 값을 사용하는 것이 아니라 `EXPECT_CALL()` 코드가 수행되는 시점의 `n` 값을 저장해 놨다가 `GetX()`가 호출될 때에는 그걸 그대로 반환해주는 것입니다. 이와 유사하게 `EXPECT_CALL()`에 `Return(new Foo)` 라는 expectation을 지정하면 호출될때마다 새로운 객체를 반환하는 것이 아니라 항상 같은 주소값(pointer 값)을 반환하게 됩니다. 만약, 사용자가 원했던 동작이 이렇게 고정된 값을 반환하는 것이 아니라 `n`에 저장된 값을 동적으로 반환하고 싶었던 것이라면 [CookBook](cook_book.md)에서 이에 대한 해결방법을 확인하시기 바랍니다.
이제 또 퀴즈를 풀어 볼 시간입니다. 아래코드는 무엇을 의미할까요?
```cpp
using ::testing::Return;
...
EXPECT_CALL(turtle, GetY())
.Times(4)
.WillOnce(Return(100));
```
이것은 명확하게 `turtle.GetY()`가 4번 호출될 것임을 의미합니다. 그러나 4번 모두 `100`을 반환한다고 생각하면 안 됩니다. `WillOnce()`는 맨 처음의 action에만 영향을 주게 됩니다. 즉, `GetY()`에 대한 2번째 호출부터는 default action에 의해 `0`이 반환될 것입니다. 왜냐하면 `int`타입을 반환하는 function의 default action은 `return 0`이기 때문입니다.
#### 여러개의 Expectations 사용하기
지금까지는 mock method 1개에 expectation 1개를 사용한 예제만 다뤘습니다. 그러나 실제 개발환경에서는 다양한 expectation들을 복합적으로 사용하게 될 것입니다. 여기서는 mock method 1개에 expectation 여러개를 지정하는 방법에 대해 살펴보겠습니다.
사용자가 여러개의 expectation을 지정하게 되면 gMock은 가장 밑에 있는 expectation부터 시작해서 조건을 만족하는 expectation이 있는지 차례로 탐색합니다. 즉, **reverse order**로 expectation을 탐색합니다. 이렇게 탐색순서가 있다는 부분을 제외하면 개별 expectation을 사용하는 방법은 사실 기존과 크게 다르지는 않습니다. 한가지 유의할 점은 expectation 탐색순서를 잘 고려하지 않으면 의도한 대로 동작하지 않을 수도 있다는 것입니다. 왜냐하면 expectation을 여러개 사용했다고 하더라도 어떤 expectation에 지정된 cardinality가 초과되면 upper-bound-violated failure가 발생한다는 점은 동일하기 때문입니다. 아래 예제를 보겠습니다.
```cpp
using ::testing::_;
...
EXPECT_CALL(turtle, Forward(_)); // #1
EXPECT_CALL(turtle, Forward(10)) // #2
.Times(2);
```
위의 코드와 같이 `Forward()`라는 function에 2개의 expectation을 지정한 상태에서 `Foward(10)`이 3번 연속으로 호출된다면 upper-bound-violated failure가 발생하게 됩니다. 왜냐하면 #2번 expectation이 기대하고 있는 2회 호출을 초과하기 때문입니다. 그런데 신기하게도 `Foward(20)`으로 3번 호출한다면 upper-bound-violated failure는 발생하지 않을 것입니다. 왜 그럴까요? 그 이유는 `Foward(20)`은 #1번 expectation을 사용하기 때문입니다. 사용자의 세심한 주의가 필요한 부분으로서 이에 대한 해결방법은 이 문서 하단에서 계속해서 설명하겠습니다.
**Note**: gMock은 왜 reverse order로 expectation을 탐색할까요? 먼저, test fixture를 떠올려보기 바랍니다. 우리가 test fixture를 사용하는 목적은 test fixture 레벨에서 공통적으로 적용해야 할 내용들을 `SetUp()` 또는 constructor에 구현해서 중복된 코드를 없애기 위함이었습니다. 그런 후에 개별 test case에는 좀 더 특화된 내용을 구현했습니다. 이 개념을 그대로 가져오면 `SetUp()` 또는 constructor에는 default expectation과 같은 일반적인 expectation을 먼저 구현하고 각각의 test case에는 특화된 expectation들을 구현하는 구조가 됩니다. 즉, 소스코드 측면에서 보면 나중에 수행되는 expectation일수록 더 특화된 expectation이라는 규칙이 만들어집니다. 이러한 규칙은 개별 test case 내부에서도 그대로 적용되어야 하기 때문에 어떤 method에 여러개의 expectation들이 존재한다면 아래쪽에 있는 expectation부터 탐색하게 되는 것입니다.
**Tip:** 테스트코드 구현을 처음 시작하는 시점에는 expectation을 최대한 느슨하게 설정하는 것이 좋습니다. 쉽게 말해서 cardinality는 `Times(AnyNumber())`으로 하고 matcher는 `_`로 해놓고 시작하는 것입니다. 그런 후에 개발을 진행하면서 점점 특화된 경우를 늘려가는 것이 유연성 측면에서 좋은 방법입니다. 물론, "uninteresting call" 에 대해서는 굳이 이렇게 할 필요가 없겠지만, 관심대상이 되는 mock method에 대해서는 상당히 유용할 것입니다. "uninteresting call"이 궁금한 분들은 [Understanding Uninteresting vs Unexpected Calls](https://github.com/google/googletest/blob/master/googlemock/docs/for_dummies.md#uninteresting-vs-unexpected)를 참조하세요.
#### Ordered vs Unordered Calls
위에서 얘기한 것처럼 **특정(1개)** mock method에 여러개의 expectation이 지정되어 있는 상태에서 해당 mock method가 호출되면 reverse order로 expectation을 탐색하게 됩니다. 그런데 여기서 주의해야 할 점은 reverse order라는 것은 1개의 mock method에 지정된 expectation들을 탐색하는 순서일 뿐이라는 것입니다. 우리는 아직 그들이 호출되어야 하는 순서를 지정한 적은 없습니다. 이제 여기서는 mock method의 호출순서를 지정하는 방법에 대해 설명합니다. 이 때는 1개의 mock method에 지정된 expectation들간의 호출순서뿐만 아니라 여러 mock method간의 호출순서를 지정하는 것도 포함합니다.
gMock에서는 expectation들의 호출순서를 지정하는 방법도 아래처럼 간단하게 구현할 수 있습니다.
```c++
using ::testing::InSequence;
...
TEST(FooTest, DrawsLineSegment) {
...
{
InSequence seq;
EXPECT_CALL(turtle, PenDown());
EXPECT_CALL(turtle, Forward(100));
EXPECT_CALL(turtle, PenUp());
}
Foo();
}
```
위 예제처럼 어떤 scope(block)를 만들고 scope 내부에 `InSequence` object를 생성하게 되면 해당 scope 내에 구현된 모든 expectation들이 *순서대로 호출되기를 기대한다*는 의미가 됩니다. 또한, 이 때의 호출순서는 reverse order가 아니며 위에서 아래 방향입니다. 사용자 입장에서 중요한 부부은 아니지만 gMock은 내부적으로 `InSequence` object의 constructor와 destructor를 통해서 이러한 호출순서를 검증하고 있습니다. (`InSequence` object의 이름이 무엇인지는 중요하지 않습니다.)
결과적으로 위 코드는 `Foo()`라는 function이 내부에서 `turtle` object의 mock method 3개(`PenDown()`, `Forward(100)`, `PenUp()`)를 순서대로 호출하기를 기대한다고 해석할 수 있습니다. 따라서 3개 function이 모두 호출되었다고 하더라도 `PenDown()` -> `Forward(100)` -> `PenUp()`이라는 순서를 지키지 않았다면 테스트는 실패하게 됩니다.
만약, 관심대상인 function들을 단 하나의 흐름으로 묶는것이 아니라 여러 흐름으로 나눠서 호출순서를 지정하고 싶다면 어떻게 해야할까요? gMock은 그러한 방법도 제공하고 있습니다. 자세한 내용은 [여기](cook_book.md#expecting-partially-ordered-calls)에서 확인하세요.
#### 모든 Expectation은 연결되어 있습니다. (Sticky Expectations)
간단한 퀴즈를 풀어봅시다. `Turtle`이 원점에 도착한 횟수가 2번인지만을 확인하는 테스트를 구현하려면 어떻게 해야 할까요? 즉, 테스트의 목적은 원점이동이 2회인지를 검증하는 것 외에는 관심이 없다고 가정합니다.
먼저 스스로 풀어보고 아래 코드와 비교해보시기 바랍니다.
```cpp
using ::testing::_;
using ::testing::AnyNumber;
...
EXPECT_CALL(turtle, GoTo(_, _)) // #1
.Times(AnyNumber());
EXPECT_CALL(turtle, GoTo(0, 0)) // #2
.Times(2);
```
위 코드와 같이 expectation을 설정한 다음에 원점으로 이동하라는 명령인 `GoTo(0, 0)`를 3번 호출하면 어떻게 될까요? 일단, gMock은 reverse order로 expectation을 탐색하기 때문에 #2번 expectation을 먼저 확인합니다. 그런데 #2번에서는 `GoTo()`가 정확히 2번 호출될 것이라고 기대하고 있기 때문에 3번째로 호출되면 테스트는 실패하게 될 것입니다. (여기서 실패하는 이유는 [Using Multiple Expectation]()을 참고하세요.)
이 예제는 사실 gMock의 **expectation들이 "sticky" 모드로 설정되어 있음**을 보여주고 있습니다. "sticky" 모드는 호출횟수가 초과된 expectation들도 active 상태로 남아있는 모드를 의미합니다. 이것은 매우 중요한 규칙입니다. 왜냐하면 다른 mocking framework들과 **구별되는** gMock의 특징이기 때문입니다. 그럼 gMock에서는 왜 그렇게 했을까요? 그 이유는 우리의 방식이 테스트를 구현하고 이해하는데 있어 더 유리하다고 판단했기 때문입니다.
이유가 너무 간단한가요? 예제를 통해서 sticky 모드가 무엇인지 그리고 왜 유리한지에 대해 자세히 알아보겠습니다.
```cpp
using ::testing::Return;
...
for (int i = n; i > 0; i--) {
EXPECT_CALL(turtle, GetX())
.WillOnce(Return(10*i));
}
```
위 예제 코드는 `GetX()`를 호출하면 10, 20, 30, ... 같은 순서로 반환하기를 기대하고 구현한 코드입니다. 코드가 조금 헷갈리나요? 반복문(`for`)의 초기값이 `n`이기 때문에 `EXPECT_CALL().WillOnce(Return(10*n))`, `EXPECT_CALL().WillOnce(Return(30))`, `EXPECT_CALL().WillOnce(Return(20))`, `EXPECT_CALL().WillOnce(Return(10))`과 같은 순서로 코드가 수행됩니다. 그리고 expectation은 reverse order로 탐색되기 때문에 `GetX()`가 호출되면 10, 20, 30, ... 을 반환해 줄 것입니다. 일단 처음 의도는 그렇습니다.
이제 문제점을 분석해 보겠습니다. 과연 `GetX()`가 10, 20, 30, ... 을 순서대로 잘 반환할까요? 그렇지 않습니다. 먼저 expectation들은 기본적으로 sticky 모드로 설정되어 있습니다. 따라서 `GetX()`가 처음 호출되면 `EXPECT_CALL().WillOnce(Return(10))`을 사용하여 원하는 대로 10을 반환하지만 두번째로 호출되었을 때는 원하던 값인 20을 반환하지 못 합니다. 왜냐하면 `EXPECT_CALL().WillOnce(Return(10))`이 여전히 active상태이기 때문에 두 번째 호출시점에도 `EXPECT_CALL().WillOnce(Return(10))`을 사용하려고 시도하기 때문입니다. 더군다나 gMock의 cardinality 추론규칙에 의하면 `WillOnce()`는 곧 `Times(1)`을 의미하기 때문에 해당 expectation은 upper bound violated failure를 발생시키게 될 것입니다.
그럼 `GetX()`가 10, 20, 30, ... 을 반환하게 하려면 어떻게 해야할까요? 이를 위해서는 명시적으로 expectation이 sticky하지 않다고 지정해야 합니다. 즉, 지정한 호출횟수를 만족한 expectation은 retire시켜줘야 합니다.
```cpp
using ::testing::Return;
...
for (int i = n; i > 0; i--) {
EXPECT_CALL(turtle, GetX())
.WillOnce(Return(10*i))
.RetiresOnSaturation();
}
```
위 코드와 같이 `RetiresOnSaturation()`을 사용하면 expectation을 retire 처리할 수 있습니다.
`InSequence`를 사용하는 경우에는 상황이 좀 다릅니다. 위에서 설명한 것처럼 `InSequence`에 포함된 expectation들은 위에서 아래 방향으로 호출순서가 지정됩니다. 따라서 `EXPECT_CALL()`을 지정하는 반복문의 초기값이 `1`부터 시작해야만 원하는 대로 10, 20, 30, ... 순서로 반환될 것입니다.
```cpp
using ::testing::InSequence;
using ::testing::Return;
...
{
InSequence s;
for (int i = 1; i <= n; i++) {
EXPECT_CALL(turtle, GetX())
.WillOnce(Return(10*i))
.RetiresOnSaturation();
}
}
```
만약, `InSequence` 내부에 `RetiresOnSaturation()`이 없다면 될까요? 이 때에는 바로 다음순서의 expectation으로 자동으로 넘어가기 때문에 문제가 없습니다. 즉, `InSequence`를 사용하면 기대되는 횟수가 만족된 expectation이 자동으로 retire되기 때문에 `RetiresOnSaturation()`를 사용하지 않더라도 괜찮습니다. (물론, 사용해도 됩니다.)
#### Uninteresting Calls
Mock class에 여러개의 mock method가 있을 때, 사용자가 모든 mock method를 사용하진 않을 수도 있습니다. 만약에 `GetX()`, `GetY()`라는 mock method가 얼마나 호출되든 관심이 없다면 어떻게 해야 할까요?
이렇게 관심대상이 아닌 mock method에 대해서는 그냥 아무런 조치도 안 하면 됩니다. 물론 gMock이 아무런 조치도 안 한(expectation이 없는) mock method를 확인해서 warning을 출력하긴 하지만 이것이 테스트의 성공이나 실패에는 영향을 주지 않습니다. 이렇게 관심대상이 아닌 mock method에 대해서 warning을 출력해주는 동작방식을 "naggy"라고 하는데 이러한 방식도 변경이 가능합니다. 보다 자세한 내용은 [The Nice, the Strict, and the Naggy]()를 참조하세요.

View File

@@ -0,0 +1,287 @@
## Legacy gMock FAQ
### Mock object의 method를 하나 호출했는데 real object의 method가 호출되어 버렸습니다. 왜 그런건가요? ###
Class method를 mocking하기 위해서는 기본적으로 *virtual*로 선언해야 합니다. 그게 어려운 경우라면 [high-perf dependency injection technique](cook_book.md#mocking-nonvirtual-methods)을 사용해도 됩니다.
### 가변함수(variadic function)도 mocking 할 수 있나요? ###
흔히 (`...`) 형태의 argument로 대변되는 variadic function을 gMock에서 직접 mocking할 수는 없습니다.
1차적인 문제는 mock object는 variadic function이 몇 개의 argument가 전달할지 *알 수 없다는 것입니다.* 물론 argument type도 마찬가지 알 수 없습니다. 이것은 *base class를 직접 구현하는 사람만이 알고 있을 것입니다.*
이런 variadic function을 mocking하려면 mock object에게 몇 개의 argument가 어떤 타입으로 전달될지를 알려줘야 한다는 것입니다. 이를 위한 한 가지 방법은 overloaded function을 제공하는 것입니다.
사실(`...`) 형태의 argument는 C언어의 기능이며 C++에서 새로 생겨난 것은 아닙니다. 그렇기 때문에 C++의 기능들과 함께 사용할 때 100% 안전하지는 않습니다. 되도록이면 variadic function은 지양하기를 권장합니다.
### MSVC에서 const parameter를 전달받는 mock method를 compile하면 C4301, C4373과 같은 warning이 발생합니다. 왜 그런건가요?
만약, Microsoft Visual C++ 2005 SP1을 통해 아래 코드를 compile한다면,
```c++
class Foo {
...
virtual void Bar(const int i) = 0;
};
class MockFoo : public Foo {
...
MOCK_METHOD(void, Bar, (const int i), (override));
};
```
아래와 같은 warning이 발생할 것입니다. 그러나 이것은 MSVC의 bug입니다. 동일한 코드가 gcc에서는 문제없이 잘 compile될 것입니다.
```bash
warning C4301: 'MockFoo::Bar': overriding virtual function only differs from 'Foo::Bar' by const/volatile qualifier
```
다음으로 Visual C++ 2008 SP1을 사용한다면 아래와 같은 warning이 발생할 것입니다.
```bash
warning C4373: 'MockFoo::Bar': virtual function overrides 'Foo::Bar', previous versions of the compiler did not override when parameters only differed by const/volatile qualifiers
```
C++에서 `const` parameter를 사용할 때 `const`는 무시됩니다. 즉, compiler가 `Bar(int)`와 `Bar(const int)`를 동일하게 취급하기 때문에 처음에 구현했던 코드는 아래 코드와 동일합니다.
```c++
class Foo {
...
virtual void Bar(int i) = 0; // int or const int? Makes no difference.
};
```
이렇게 method parameter에 `const`를 사용하는 것이 의미가 없기 때문에 MSVC에서 warning을 보기 싫다면 `Foo`와 `MockFoo`에서 `const`를 아예 제거하는 것도 괜찮습니다.
단, 현재 얘기하고 있는 `const`는 *top-level* `const`만 해당한다는 것을 주의하기 바랍니다. (쉽게 생각해서 value type 변수만) 만약, method parameter가 pointer나 reference라면 `const`는 여전히 의미가 있습니다. 예를 들어 아래코드의 `Bar()` 들을 서로 다릅니다.
```c++
void Bar(int* p); // Neither p nor *p is const.
void Bar(const int* p); // p is not const, but *p is.
```
### Expectation을 만족하지 못해서 테스트가 실패했는데, 뭐가 문제인지 잘 모르겠습니다. 어떻게 해야 할까요?
`--gmock_verbose=info`라는 flag와 함께 테스트를 수행하면, mock function의 trace까지 포함해서 최대한 많은 정보를 출력해 줍니다. 이러한 trace를 확인해보면 expectation이 만족되지 않은 이유를 밝히는데 도움이 될 것입니다.
혹시, "The mock function has no default action set, and its return type has no default value set." 이라는 message가 출력되었다면 [adding a default action]()을 적용해보기 바랍니다. 내부적인 이슈로 인해서 default action이 없는 상태에서 발생한 unexpected call에 대해서는 자세한 정보(actual argument와 expected argument 비교 등)는 출력하지 않고 있으며 위와 같은 message만 출력하고 있습니다.
### Program이 crash가 발생한 후에, `ScopedMockLog`가 너무 많은 내용을 출력합니다. 혹시 gMock의 bug가 아닌가요?
gMock과 `ScopedMockLog`는 정상적으로 동작한 것으로 보입니다.
테스트가 실패하면 failure signal handler는 최대한 많은 정보를 수집하려고 합니다. (stack trace, address map 등을 포함해서) 복잡한 환경일수록 정보도 많습니다. 예를 들어 여러 thread와 그들의 stack으로부터도 정보가 계속 수집됩니다. 그러다가 문제가 발생하면 `ScopeMockLog`가 관련 정보를 출력해 주는 것입니다.
물론, 이러한 error를 출력하지 않는 방법도 제공을 하고 있습니다. 아니면 아래 코드와 같이 구현해서 관심대상이 아닌 내용을 구분하도록 해도 됩니다.
```c++
using ::testing::AnyNumber;
using ::testing::Not;
...
// Ignores any log not done by us.
EXPECT_CALL(log, Log(_, Not(EndsWith("/my_file.cc")), _))
.Times(AnyNumber());
```
### Mock function이 호출되지 않기를 바란다면 어떻게 구현해야 하나요?
```c++
using ::testing::_;
...
EXPECT_CALL(foo, Bar(_))
.Times(0);
```
### 테스트가 실패했는데 gMock이 어떤 expectation에 대해 같은 내용을 두 번 출력했습니다. 중복으로 출력된 건가요? ###
gMock은 failure를 발견했을 때, 사용자를 위해 다양한 디버깅 정보들을 출력해줍니다. 예를 들면 mock function으로 전달된 argument 나 expectation의 상태 등을 알려줍니다. 이것은 failure가 발견될 때마다 같은 방식으로 동작합니다.
만약, 2개의 expectation이 있고 기대하는 것들이 같다고 가정해봅시다. Expectation 2개를 테스트하는 동안에도 기대하는 바를 충족하지 못한다면 당연히 failure도 동일한 정보를 출력할 것입니다. 이런 경우는 *중복이 아니라 서로 다른 시점에 동일한 문제가 발견 된 것입니다.* 비록 그 내용이 같을지라도 필요한 정보를 출력했다고 봐야합니다.
### Real object를 사용하면 괜찮은데 Mock object를 사용하면 heap check failure가 발생합니다. 뭐가 잘못된 걸까요? ###
지금 mocking하고 있는 class(pure interface이면 좋습니다.)가 virtual destructor를 가지고 있나요?
C++에서 base class의 destructor는 언제나 virtual로 선언되어야 합니다. 그렇지 않으면 derived class의 destructor들이 호출되지 않기 때문에 문제가 됩니다. 아래 코드를 보겠습니다.
```cpp
class Base {
public:
// Not virtual, but should be.
~Base() { ... }
...
};
class Derived : public Base {
public:
...
private:
std::string value_;
};
...
Base* p = new Derived;
...
delete p; // Surprise! ~Base() will be called, but ~Derived() will not
// - value_ is leaked.
```
주석에 설명한 것처럼 위 코드에서는 `delete p`를 해도 `Derived` class의 destructor가 호출되지 않습니다. 해결방법은`~Base()`를 `virtual ~Base()`로 변경하는 것입니다. 그러면 heap checker의 결과도 문제가 없을 것입니다.
### "newer expectations override older ones"라는 규칙이 이상해 보입니다. gMock은 왜 이런 정책을 선택한건가요? ###
아래 예제를 먼저 보겠습니다.
```cpp
using ::testing::Return;
...
// foo.Bar() should be called twice, return 1 the first time, and return
// 2 the second time. However, I have to write the expectations in the
// reverse order. This sucks big time!!!
EXPECT_CALL(foo, Bar())
.WillOnce(Return(2))
.RetiresOnSaturation();
EXPECT_CALL(foo, Bar())
.WillOnce(Return(1))
.RetiresOnSaturation();
```
위의 코드를 구현하면서 (주석에 쓰여있는) 불평을 하고 있다면 과연 적절한 방법으로 구현한 것인지 고민해 볼 필요가 있습니다.
gMock의 기본적으로 expectation간에 *어떠한 호출순서도 부여하지 않습니다.* (탐색순서만 있습니다.) 이것은 gMock 그리고 jMock의 기본철학입니다. 이러한 규칙은 사용자가 테스트를 너무 과하게 지정하는 실수를 줄이기 위한 것입니다. 만약, expectation간에 호출순서를 부여하고 싶다면 사용자가 직접 명시적으로 표현해야 합니다.
호출순서를 부여하기 위해서는 expectation들을 sequence에 포함시키면 됩니다.
```cpp
using ::testing::Return;
...
// foo.Bar() should be called twice, return 1 the first time, and return
// 2 the second time. Using a sequence, we can write the expectations
// in their natural order.
{
InSequence s;
EXPECT_CALL(foo, Bar())
.WillOnce(Return(1))
.RetiresOnSaturation();
EXPECT_CALL(foo, Bar())
.WillOnce(Return(2))
.RetiresOnSaturation();
}
```
아니면 expectation에 여러개의 action을 지정해도 됩니다. action의 호출순서는 위에서 아래입니다.
```cpp
using ::testing::Return;
...
// foo.Bar() should be called twice, return 1 the first time, and return
// 2 the second time.
EXPECT_CALL(foo, Bar())
.WillOnce(Return(1))
.WillOnce(Return(2))
.RetiresOnSaturation();
```
그럼 왜 gMock은 expectation들의 호출순서는 강제하지 않으면서 탐색할 때는 밑에서부터 탐색할까요? 왜냐하면 이렇게 해야 mock의 동작을 일반적인 내용부터 시작해서 상세한 동작으로 점진적으로 지정할 수 있기 때문입니다. 즉, mock의 set-up 시점에 일반적인 expectation을 지정하고, 각각의 test case에서는 좀 더 상세한 설정을 지정하는 것입니다. 만약, gMock이 위에서부터 아래 방향으로 expectation을 검색하면 위와 같은 패턴을 적용할 수 없게 됩니다.
### ON_CALL을 통해 default action을 지정했음에도 불구하고 EXPECT_CALL을 설정하지 않은 function이 호출되면 warning을 출력합니다. 이런 경우에 warning을 출력하는게 맞나요? ###
간편함과 안전함 중에 선택하라고 할 때, gMock은 후자를 선택합니다. 그러한 이유로 여기서도 warning을 출력해주는게 더 좋다고 생각합니다.
사람들은 mock object의 constructor나 `SetUp()`에 `ON_CALL`을 구현합니다. 왜냐하면 어떤 mock method의 default behavior를 지정할때는 여러 test case에 사용될 수 있고 잘 변하지 않는 일반적인 내용을 주로 지정하기 때문입니다. 그 다음에 각각의 test case를 구현할 때 해당 test case에 특화된 expectation을 지정할 것입니다. 즉, set-up 부분에서 지정하는 default behavior는 말 그대로 기본동작만 지정한 것이지 실제로 호출되길 바라는 expectation은 아닌 것입니다. 따라서 개별 test case에 `EXPECT_CALL `이 없는데도 mock method가 호출되었다면 문제가 있다고 판단하게 됩니다. 또 이러한 내용을 사용자에게 알려줘야 잠재적인 bug를 예방하는데 유리합니다. 다만, 실제로 bug일지 아닐지는 모르기 떄문에 error가 아닌 warning을 통해 알려주는 것입니다.
만약, 발생한 호출이 문제가 없다면 아래처럼 구현하기만 하면 됩니다.
```cpp
using ::testing::_;
...
EXPECT_CALL(foo, Bar(_))
.WillRepeatedly(...);
```
아래처럼 `ON_CALL`만 사용했다면 default behavior를 변경할 수는 있지만 warning 발생을 막을 수는 없습니다.
```cpp
using ::testing::_;
...
ON_CALL(foo, Bar(_))
.WillByDefault(...);
```
이제 gMock은 해당 method가 호출되어도 warning을 출력하지 않을 것입니다.
더불어 gMock이 출력하는 내용을 조절하는 것도 가능합니다. 디버깅을 해야하는데 너무 많은 정보가 출력되어 어려움을 겪고 있다면 `--gmock_verbose` flag의 레벨을 조절하시기 바랍니다.
### Action에서 mock function으로 전달되는 argument를 삭제(delete)하려면 어떻게 해야 하나요? ###
Mock function이 전달받는 pointer argument를 삭제하려고 하는 건가요? 그런 경우라면 `testing::DeleteArg()`를 사용해서 N번째(0부터 시작) argument를 삭제 할 수 잇습니다.
```c++
using ::testing::_;
...
MOCK_METHOD(void, Bar, (X* x, const Y& y));
...
EXPECT_CALL(mock_foo_, Bar(_, _))
.WillOnce(testing::DeleteArg<0>()));
```
### 새로운 action을 만들 수 있나요?
gMock에서 지원하지 않는 새로운 action을 구현하고 싶다면 [MakeAction()](https://github.com/ant35rookie/googletest_docs/blob/master/cook_book.md#writing-new-actions-quickly), [MakePolymorphicAction() ](https://github.com/ant35rookie/googletest_docs/blob/master/cook_book.md#writing-new-polymorphic-actions)를 사용하면 됩니다. 또한 stub function을 구현하고 [Invoke()](https://github.com/ant35rookie/googletest_docs/blob/master/cook_book.md#using-functionsmethodsfunctors-as-actions)를 사용해서 호출하는 것도 가능합니다.
```c++
using ::testing::_;
using ::testing::Invoke;
...
MOCK_METHOD(void, Bar, (X* p));
...
EXPECT_CALL(mock_foo_, Bar(_))
.WillOnce(Invoke(MyAction(...)));
```
### static/global function도 mocking 가능한가요? ###
물론 가능합니다, 그 전에 약간의 확인 및 수정작업은 필요합니다.
먼저, static/global function에 대한 mocking이 필요하다는 것은 소스코드가 너무 tightly coupled (혹은 less flexible, less resuable, less testable)되어 있음을 의미하기도 합니다. 이런 경우에는 작은 interface를 하나 정의하고 해당 interface를 통해서 static/global function을 호출하도록 변경하는 것이 좋습니다. 이러한 구조가 되면 mocking을 하기에도 유리해집니다. 물론 초반에 해야할 일이 좀 더 생기겠지만, 나중에는 도움이 될 것입니다.
관련 내용들이 [여기](https://testing.googleblog.com/2008/06/defeat-static-cling.html)에 매우 잘 설명되어 있습니다.
### Mock object를 통해 여러가지 복잡한 일을 해보고 싶습니다. 그러나 gMock에서 action을 명세하는 작업이 너무 힘듭니다. ###
엄밀히 말해서 질문이라고 보기는 어렵지만, 최대한 답을 드려보겠습니다.
gMock이라는 도구를 통해 C++에서도 간단하게 mock을 사용할 수 있게 되었지만 여전히 gMock조차도 어려워하는 사람들이 많이 있습니다. 무엇이 문제일까요?
먼저, mock이 없이 테스트코드를 구현한다고 가정해 봅시다. 이 때에는 어떤 변수나 시스템의 상태(state)를 검증하게 됩니다. 여기서 상태는 어떤 동작이 완료된 후에 변화된 결과를 의미하며 이렇게 상태를 기반으로 검증하는 방법을 "state-based testing" 이라고 부르기도 합니다.
반면에 mock을 사용하면 "interaction-based testing"이라는 방법을 사용하게 됩니다. 즉, 시스템의 상태를 검증하는 것이 아니라 상태를 변화시키기 위한 일련의 동작들이 원하는 방향으로 진행되고 있는지 확인하고 그 결과를 바로 알려줍니다. 그 과정중에 발생한 문제가 있다면 해당상황에 대한 정보도 제공해줍니다. 많은 경우에 "interaction-based testing" 접근방법이 "state-based testing" 보다 효과적이고 경제적으로 여겨집니다.
물론, mock이 아닌 다른 test double(fake 등) + "state-based testing"로도 충분하고 그렇게 하는 것이 적절한 상황이라면 당연히 그렇게 구현해야 합니다. Mock을 적용하는 것도 노력이 필요한 일이기 때문에 꼭 필요한 곳에만 적용하기 바랍니다. 즉, 사용자의 환경에 mock을 적용하기가 어렵다면 그 상황에 더 알맞은 다른 도구를 검토해 볼 필요도 있다는 것입니다. 아니면 gMock을 사용하면서 어려웠던 부분을 저희와 함께 개선해보면 어떨까요?
### "Uninteresting function call encountered - default action taken.." 이라는 warning이 발생했습니다. 큰 문제인가요? ###
당연히 아닙니다, 단순히 정보를 전달하기 위한 목적입니다.
해당 warning은 어떤 mock function에 아무런 expectation을 지정하지 않았는데도 호출되었음을 의미합니다. 이러한 경우에 gMock은 expectation이 없기 때문에 몇 번 호출되든 상관없다고 판단합니다. "호출되면 안 된다"라고 지정한 것이 아니기 때문에 warning외에 다른 문제는 없을 것입니다.
gMock은 warning을 통해서 잠재적인 문제에 대한 정보를 사용자에게 알려줍니다. 즉, warning이 발생했다면 관련 내용을 검토해 볼 필요가 있습니다. 예를 들어 원래는 "호출되면 안 된다"라고 하고 싶었는데 깜박했을 수도 있습니다. 즉, `EXPECT_CALL(foo, Bar()).Times(0)`이라는 구현을 빼먹은 경우입니다.
이제 질문과 같은 warning이 출력되면 이것이 실제로 문제가 될지 안될지를 판단하기를 바랍니다. 사용자가 실수를 했을지도 모른다는 가정으로 알려주는 정보인 것입니다. Function name, argument들도 출력해주기 때문에 상당한 도움이 될 것입니다.
### Action을 직접 정의하고 싶습니다. Invoke()를 사용하는게 좋은가요 아니면 action interface를 구현하는게 좋은가요? ###
어느 것을 사용해도 괜찮습니다. 상황에 맞는 것을 고르면 됩니다.
보통, action이 특정타입에 대해서만 수행된다면 `Invoke()`를 사용해서 구현하는게 쉽습니다. 반대로 `Return(value)`의 동작방식 처럼 같이 여러가지 타입에 두루두루 사용할 수 있는 action을 원한다면 `MakePolymorphicAction()`를 사용하는 것이 좋습니다. 후자의 경우에는 `ActionInteface`를 통해서 구조를 좀 더 간결하게 할 수도 있습니다. 관련 예제로 `include/gmock/gmck-actions.h` 파일의 `Return()` 구현부를 보면 도움이 될 것입니다.
### WillOnce()와 함께 SetArgPointee()를 사용하는데 "conflicting return type specified"라는 compile error가 발생했습니다. 무슨 뜻인가요? ###
Mock method가 호출되었지만 어떤 값을 반환해야 할지 모를 때, 위와 같은 에러가 발생합니다. `SetArgPointee()`는 argument에 값을 저장하는 동작만 수행하는 action입니다. 이런 문제가 발생하는 이유는 mock method에 대한 `Return()`동작을 지정하지 않았기 때문입니다. `DoAll()`을 사용해서 `SetArgPointee()`와 `Return()`을 둘 다 수행할 수 있도록 연결해주시기 바랍니다.
[여기](cook_book.md#mocking-side-effects)에 자세한 설명과 예제코드가 있습니다.
### Microsoft Visual C++에서 out out memory라고 하며 compile에 실패합니다. 어떻게 해야하나요? ###
Visual C++ 을 사용할 때, `/clr`이라는 compiler flag를 확인해보기 바랍니다. `/clr`이 사용되면 mock class를 compile하기 위해 기존보다 5~6배 많은 메모리를 사용하게 됩니다. 해당 flag를 제거하고 다시 시도해보기 바랍니다.