diff --git a/googlemock/docs/kr/cheat_sheet.md b/googlemock/docs/kr/cheat_sheet.md new file mode 100644 index 00000000..5afa4508 --- /dev/null +++ b/googlemock/docs/kr/cheat_sheet.md @@ -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 nice_foo; // The type is a subclass of MockFoo. +NaggyMock naggy_foo; // The type is a subclass of MockFoo. +StrictMock strict_foo; // The type is a subclass of MockFoo. +``` + +#### Class Template Mocking하기 + +아래에 `StackInterface`라는 interface가 있습니다. (소멸자 `~StackInterface()`는 반드시 `virtual`로 선언해야 합니다.) +```cpp +template +class StackInterface { + ... + virtual ~StackInterface(); + virtual int GetSize() const = 0; + virtual void Push(const Elem& x) = 0; +}; +``` +`StackInterface`는 아래와 같이 mocking할 수 있습니다. + +```cpp +template +class MockStack : public StackInterface { + ... + 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의 ``파일에 정의되어 있습니다. + +### 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::Set(value); +// Sets a factory. Will be invoked on demand. T must be MoveConstructible. +// T MakeT(); +DefaultValue::SetFactory(&MakeT); +// ... use the mocks ... +// Resets the default value. +DefaultValue::Clear(); +``` + +위에서 설정한 default action을 사용하는 코드는 아래와 같습니다. + +```c++ + // Sets the default action for return type std::unique_ptr to + // creating a new Buzz every time. + DefaultValue>::SetFactory( + [] { return MakeUnique(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, + // to avoid interfere with other tests. + DefaultValue>::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()` or `An()`|기본적으로 `_`와 동일하지만, `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(m)` |`argument`로 전달되는 `variant<>`를 matcher `m`을 통해 비교합니다.| +|`Ref(variable)` |`argument`가 `variable`의 참조형태이길 기대합니다.| +|`TypedEq(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(), 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`라는 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(m)` | `argument`가 `dynamic_cast()`를 통해 전달되었을 때, `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(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(m)`|Matcher `m`을 `Matcher`로 변환합니다.| +|`SafeMatcherCast(m)`| Matcher `m`을 `Matcher`로 [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()`|Mock function으로 전달된 `N`번째(0부터 시작) argument를 반환합니다.| +|`ReturnNew(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`번째(0부터 시작) argument를 delete합니다. 따라서 해당 argument는 pointer 형태입니다. | +|`SaveArg(pointer)`| `N`번째(0부터 시작) argument를 `*pointer`에 저장합니다. | +|`SaveArgPointee(pointer)`| `N`번째(0부터 시작) argument가 가리키는 값을 `*pointer`에 저장합니다. | +|`SetArgReferee(value)` | `N`번째(0부터 시작) argument가 참조하는 변수에 `value`를 저장합니다. | +|`SetArgPointee(value)` |`N`번째(0부터 시작) argument가 가리키는 변수에 `value`를 저장합니다.| +|`SetArgumentPointee(value)`|`SetArgPointee(value)`과 동일하지만 deprecated 되었습니다. gMock v1.7.0 이후에 삭제될 예정입니다.| +|`SetArrayArgument(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(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(...)`을 사용할 때, 참조형식으로 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(a)` | Mock function의 `N`번째(0부터 시작) argument를 action `a`에 전달합니다. | +| `WithArgs(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())) + .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 { + 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`) | diff --git a/googlemock/docs/kr/cook_book.md b/googlemock/docs/kr/cook_book.md new file mode 100644 index 00000000..82af3f22 --- /dev/null +++ b/googlemock/docs/kr/cook_book.md @@ -0,0 +1,3238 @@ +## gMock Cookbook + +이 문서에서는 gMock의 심화된 사용방법을 확인할 수 있습니다. 아직 gMock 사용경험이 없는 분은 [ForDummies](ForDummies.md) 문서를 먼저 보는 것이 좋습니다. + +**Note:** gMock은 소스코드상에 `testing` 이라는 namespace에 구현되어 있습니다. 따라서 gMock에 구현된 `foo`라는 기능을 사용하고자 한다면 `using testing:Foo` 와 같이 namespace를 명시해서 사용해야 합니다. 이 문서에서는 예제코드를 간단하게 하기위한 목적으로 `using`을 사용하지 않기도 했지만 실제로 사용할 때에는 `using`을 꼭 사용하기 바랍니다. + + +### Mock Class 만들기 ### + +#### Comma(`,`)를 문제없이 사용하는 방법 + +만약 Comma를 괄호없이 사용하게 되면 `MOCK_METHOD` macro를 파싱할 때 문제가 발생합니다. + +```c++ +class MockFoo { + public: + MOCK_METHOD(std::pair, GetPair, ()); // Won't compile! + MOCK_METHOD(bool, CheckMap, (std::map, bool)); // Won't compile! +}; +``` + +위의 코드는 compile error가 발생할 것입니다. Return type, argument type의 구분이 모호하기 때문입니다. + +해결방법 1 - 괄호로 감싸기 + +```c++ +class MockFoo { + public: + MOCK_METHOD((std::pair), GetPair, ()); + MOCK_METHOD(bool, CheckMap, ((std::map), bool)); +}; +``` + +이 해결방법은 return type, argument type을 구분하기 위해 괄호를 사용했습니다. 물론 C++에서 return type, argument type을 괄호로 감싸면 안되지만 `MOCK_METHOD` macro는 파싱을 위해서만 괄호를 사용한 후에 괄호를 제거하기 때문에 괜찮습니다. + +해결방법 2 - alias 사용하기 + +```c++ +class MockFoo { + public: + using BoolAndInt = std::pair; + MOCK_METHOD(BoolAndInt, GetPair, ()); + using MapIntDouble = std::map; + MOCK_METHOD(bool, CheckMap, (MapIntDouble, bool)); +}; +``` + +#### Private, Protected 영역에 정의된 Method를 Mocking하기 #### + +Base class의 method가 `public`, `protected`, `private` 등 어느 영역에 정의되어 있는지에 관계없이 mock method를 정의할 때 사용하는 `MOCK_METHOD` macro는 항상 `public:` 영역에서 사용해야 합니다. (C++에서는 base class의 virtual function이 어느 영역에 선언되었는지에 관계없이 drived class에서는 원하는 영역에 자유롭게 overriding 할 수 있습니다.) 이렇게 해야만 `ON_CALL`, `EXPECT_CALL`과 같은 매크로가 mock class 또는 mock function에 접근할 수 있습니다. 아래 예제를 확인하세요. + +```cpp +class Foo { + public: + ... + virtual bool Transform(Gadget* g) = 0; + + protected: + virtual void Resume(); + + private: + virtual int GetTimeOut(); +}; + +class MockFoo : public Foo { + public: + ... + MOCK_METHOD(bool, Transform, (Gadget* g), (override)); + + // The following must be in the public section, even though the + // methods are protected or private in the base class. + MOCK_METHOD(void, Resume, (), (override)); + MOCK_METHOD(int, GetTimeOut, (), (override)); +}; +``` + +#### Overloaded Method를 Mocking 하기 #### + +Overloaded function을 mocking하는 것도 기존의 방법과 다르지 않습니다. + +```cpp +class Foo { + ... + + // Must be virtual as we'll inherit from Foo. + virtual ~Foo(); + + // Overloaded on the types and/or numbers of arguments. + virtual int Add(Element x); + virtual int Add(int times, Element x); + + // Overloaded on the const-ness of this object. + virtual Bar& GetBar(); + virtual const Bar& GetBar() const; +}; + +class MockFoo : public Foo { + ... + MOCK_METHOD(int, Add, (Element x), (override)); + MOCK_METHOD(int, Add, (int times, Element x), (override)); + + MOCK_METHOD(Bar&, GetBar, (), (override)); + MOCK_METHOD(const Bar&, GetBar, (), (const, override)); +}; +``` + +**Note:** 만약 overloaded method가 여러개 있음에도 그 중 일부만 mocking한다면 compiler가 warning message를 출력할 것입니다. 그 내용은 base class의 몇몇 함수가 숨겨져 있다고 알려주는 것입니다. 만약 이러한 warning message를 없애려면 `using`을 이용해서 해당함수를 drived class가 참조할 수 있게 하면 됩니다. + +```cpp +class MockFoo : public Foo { + ... + using Foo::Add; + MOCK_METHOD(int, Add, (Element x), (override)); + // We don't want to mock int Add(int times, Element x); + ... +}; +``` + +#### Class Template Mocking 하기 #### + +Class template을 mocking하는 것도 기존의 방법과 다르지 않습니다. + +```cpp +template +class StackInterface { + ... + // Must be virtual as we'll inherit from StackInterface. + virtual ~StackInterface(); + + virtual int GetSize() const = 0; + virtual void Push(const Elem& x) = 0; +}; + +template +class MockStack : public StackInterface { + ... + MOCK_METHOD(int, GetSize, (), (override)); + MOCK_METHOD(void, Push, (const Elem& x), (override)); +}; +``` + +#### Nonvirtual Method Mocking 하기 #### + +gMock을 통해서 non-virtual function도 간단하게 mocking 할 수 있습니다. 더불어 이러한 방법을 Hi-perf dependency injection라고 부르고 있습니다. Virtual function table을 사용하지 않기 때문에 속도가 빠르다는 의미입니다. + +또한, 이 경우에는 상속관계를 사용하지 않습니다. Dependency injection을 위한 방법으로 template을 이용하기 때문에 mock class가 interface 혹은 base class를 상속받을 필요가 없습니다. 대신 real class의 method 중에서 관심있는 method를 찾은 후에, mock class에 해당 method와 동일한 signature를 갖는 method를 정의하기만 하면 됩니다. 정의하는 방법은 *기존과 동일*하지만 `MOCK_METHOD` macro의 4번째 argument로 전달되던 `override`를 제거해야 합니다. + +```cpp +// A simple packet stream class. None of its members is virtual. +class ConcretePacketStream { + public: + void AppendPacket(Packet* new_packet); + const Packet* GetPacket(size_t packet_number) const; + size_t NumberOfPackets() const; + ... +}; + +// A mock packet stream class. It inherits from no other, but defines +// GetPacket() and NumberOfPackets(). +class MockPacketStream { + public: + MOCK_METHOD(const Packet*, GetPacket, (size_t packet_number), (const)); + MOCK_METHOD(size_t, NumberOfPackets, (), (const)); + ... +}; +``` + +코드에서 보이는 것처럼 mock class인 `MockPacketStream` 에서는 `AppendPacket()`을 정의하지 않았습니다. 이처럼 테스트에서 사용하지 않는 함수는 굳이 정의하지 않아도 문제가 없습니다. + +이제 2개의 class를 상황에 따라 구분해서 선택하도록 구현하면 됩니다. `ConcretePacketStream`은 제품코드에 사용하고 `MockPacketStream`은 테스트코드에 사용할 것입니다. 단, 2개의 class간에는 상속관계가 없고 따라서 virtual function도 없기 때문에 이러한 다형성이 compile time에 이뤄질 수 있도록 구현해야 합니다. + +몇 차례 언급한 것처럼 이런 경우에는 template을 사용하면 됩니다. 즉, 위에서 선언한 2개의 class를 사용하는 코드가 이들을 template type으로 취급하도록 구현하면 됩니다. 이를 통해 제품코드에 대해서는 `ConcretePacketStream` object가 사용되고 테스트코드에 대해서는 `MockPacketStream` object가 사용될 것입니다. 이러한 내용이 구현된 예제입니다. + +```cpp +template +void CreateConnection(PacketStream* stream) { ... } + +template +class PacketReader { + public: + void ReadPackets(PacketStream* stream, size_t packet_num); +}; +``` + +2개 class를 일반적으로 표현하기 위해서 template parameter의 이름을 `PacketStream`으로 지었습니다. 이제 사용하기만 하면됩니다. 즉, 제품코드에서는 `CreateConnection()`, `PacketReader` 과 같이 구현하면 되고 테스트코드에서는`CreateConnection()`, `PacketReader`으로 구현하면 됩니다. 아래 이렇게 구현된 테스트코드의 예제입니다. + +```cpp + MockPacketStream mock_stream; + EXPECT_CALL(mock_stream, ...)...; + .. set more expectations on mock_stream ... + PacketReader reader(&mock_stream); + ... exercise reader ... +``` + +#### Free Function Mocking 하기 #### + +gMock에서는 free function을 mocking하는것도 가능합니다. 여기서 free function이란 C-style function 또는 static method 등을 의미합니다. 잘 생각해보면 지금까지는 non-static class method 만 다뤘음을 알 수 있습니다. gMock에서는 free function을 mocking하기위해 interface(abstract class)를 사용합니다. + +즉, free function을 직접 호출하지 않고 wrapper class를 만드는 것입니다. 아래 코드를 보면 새로운 interface인 `FileInterface`를 만들고 `Open()`이라는 pure abstract method로 선언했습니다. 이제 dervied class를 생성하고 `Open()`을 정의합니다. Derived class `File`에서는 `Open`을 override 하면서 우리의 free function인 `OpenFile()`을 호출하도록 했습니다. + +```cpp +class FileInterface { + public: + ... + virtual bool Open(const char* path, const char* mode) = 0; +}; + +class File : public FileInterface { + public: + ... + virtual bool Open(const char* path, const char* mode) { + return OpenFile(path, mode); + } +}; +``` + +이제 `OpenFile`이라는 free function을 직접 호출하던 기존의 코드들을 `FileInterface`의 `Open()`을 호출하도록 변경합니다. 이렇게 free function들이 wrapper class를 갖게 되면 다음 순서는 non-static class method를 mocking하는 방법과 동일하게 진행하면 됩니다. + +이러한 작업이 귀찮게 느껴질 수도 있지만, 처음에 한번만 추상화 계층을 잘 설계해 놓으면 연관된 free function들을 같은 interface로 묶을 수 있습니다. 해야할 일이 거의 없을 뿐더러 소스코드는 깔끔해지고 mocking은 간편해집니다. + +혹시나 virtual function을 호출하기 때문에 발생하는 성능저하가 우려된다면 [mocking non-virtual methods](#mocking-nonvirtual-methods)와 같이 사용하면 성능저하도 크게 문제가 되지 않습니다. + +#### 이전방식의 macro인 `MOCK_METHODn` 간단소개 + +현재와 같은 `MOCK_METHOD` macro가 구현되기 전에는 `MOCK_METHODn`이라는 macro family를 통해 mock method를 정의했습니다. 이전방식 macro에서 `n`은 argument의 개수를 의미하기 때문에 argument 개수에 따라 여러개의 macro가 있었습니다. 물론, gMock은 `MOCK_METHODn`계열을 여전히 지원하고 있지만 점차 새로운 방식으로 변경하시길 추천합니다. + +아래는 `MOCK_METHOD`와 `MOCK_METHODn`의 차이점을 보여줍니다. + +- 기본구조가 `MOCK_METHOD(ReturnType, MethodName, (Args))`에서 `MOCK_METHODn(MethodName, ReturnType(Args))`으로 변경되었습니다. +- Argument의 개수를 의미하던 `n`이 제거되었습니다. +- const method를 위해서 존재하던 `MOCK_CONST_METHODn`가 제거되고 const 정보를 argument로 전달하도록 변경되었습니다. +- class tempalte을 위해서 존재하던 `T`라는 suffix가 제거되었습니다. +- 함수 호출방식을 알려주는 `_WITH_CALLTYPE`이라는 suffix가 제거되고 역시 argument로 전달하도록 변경되었습니다. + +아래 표에서 이전방식에서 사용하던 기능이 어떻게 대체되었는지 자세히 확인할 수 있습니다. + +| **Purpose** | Ver. | Macro | +| :-------------------------------------------------- | :--- | :----------------------------------------------------------- | +| **Simple** | Old | `MOCK_METHOD1(Foo, bool(int))` | +| | New | `MOCK_METHOD(bool, Foo, (int))` | +| **Const Method** | Old | `MOCK_CONST_METHOD1(Foo, bool(int))` | +| | New | `MOCK_METHOD(bool, Foo, (int), (const))` | +| **Method in a Class Template** | Old | `MOCK_METHOD1_T(Foo, bool(int))` | +| | New | `MOCK_METHOD(bool, Foo, (int))` | +| **Const Method in a Class Template** | Old | `MOCK_CONST_METHOD1_T(Foo, bool(int))` | +| | New | `MOCK_METHOD(bool, Foo, (int), (const))` | +| **Method with Call Type** | Old | `MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, Foo, bool(int))` | +| | New | `MOCK_METHOD(bool, Foo, (int), (Calltype(STDMETHODCALLTYPE)))` | +| **Const Method with Call Type** | Old | `MOCK_CONST_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, Foo, bool(int))` | +| | New | `MOCK_METHOD(bool, Foo, (int), (const, Calltype(STDMETHODCALLTYPE)))` | +| **Method with Call Type in a Class Template** | Old | `MOCK_METHOD1_T_WITH_CALLTYPE(STDMETHODCALLTYPE, Foo, bool(int))` | +| | New | `MOCK_METHOD(bool, Foo, (int), (Calltype(STDMETHODCALLTYPE)))` | +| **Const Method with Call Type in a Class Template** | Old | `MOCK_CONST_METHOD1_T_WITH_CALLTYPE(STDMETHODCALLTYPE, Foo, bool(int))` | +| | New | `MOCK_METHOD(bool, Foo, (int), (const, Calltype(STDMETHODCALLTYPE)))` | + +#### Nice 모드, Strict 모드, Naggy 모드 (Naggy: 잔소리가 심한) #### + +`EXPECT_CALL` 없이 호출된 mock method가 있다면, gMock은 "uninteresting call"이라는 warning을 출력한 후에 default action을 수행합니다. 이 때, warning을 출력해주는 이유는 아래와 같습니다. + +- 테스트코드가 이미 작성된 상태라고 해도 시간이 지남에 따라 mocking 대상 interface(혹은 base class)에 새로운 method가 추가될 수 있습니다. 또 이렇게 새로 추가된 method를 위한 mock method를 추가하는 것도 자연스러운 과정입니다. 따라서 gMock에서는 이렇게 새로 추가된 mock method가 `EXPECT_CALL`없이 호출되었다고 해서 기존의 테스트를 실패 처리하지는 않습니다. +- 다만, 이렇게 예측하지 못했던 mock method가 호출되는 상황은 추후에 문제가 될 수도 있기 때문에 테스트 실패까지는 아니더라도 warning을 통해 알려주는 것입니다. 따라서 이러한 warning이 발생하면 해당 mock method가 호출되어야 할 상황인지 아닌지 확인하기 바랍니다. 만약 호출되는 것이 맞다면 해당 mock method에 대해 `EXPECT_CALL()`만 추가하면 됩니다. 그럼 더 이상 warning은 발생하지 않을 것입니다. + +"uninteresting call"이라는 warning을 출력해주는 것은 말 그대로 경고의 의미입니다. 다만, 어떤 사용자는 이러한 warning을 보고 싶지 않은 사람도 있고 또는 모든 warning을 error로 처리하고 싶은 사람도 있을 것입니다. gMock에서는 이러한 처리방법을 사용자가 선택할 수 있게 했습니다. 또한, mock object별로 이러한 설정을 다르게 할 수도 있습니다. + +테스트코드에서 `MockFoo`라는 mock class를 사용하는 예제를 보겠습니다. + +```cpp +TEST(...) { + MockFoo mock_foo; + EXPECT_CALL(mock_foo, DoThis()); + ... code that uses mock_foo ... +} +``` + +먼저, `MockFoo`에는 여러개의 mock method가 정의되어 있다고 가정하겠습니다. 이런 상황에서 위의 테스트코드를 실행했더니 `EXPECT_CALL`을 명시적으로 사용한 `DoThis()` 외에 다른 mock method의 호출도 발생했습니다. 그럼 gMock은 warning을 출력할텐데 만약 이러한 warning을 보고 싶지 않다면 mock object를 생성할 때 `NiceMock`으로 사용하면 됩니다. 결론적으로 아래 코드는 더 이상warning을 출력하지 않습니다. + +```cpp +using ::testing::NiceMock; + +TEST(...) { + NiceMock mock_foo; + EXPECT_CALL(mock_foo, DoThis()); + ... code that uses mock_foo ... +} +``` + +gMock은 내부적으로 `NiceMock`를 `MockFoo`의 derived class로 생성하기 때문에 `MockFoo`가 사용가능한 곳에는 `NiceMock`도 사용할 수 있습니다. + +또한, `NiceMock`는 `MockFoo`의 constructor도 상속받기 때문에 `MockFoo`를 생성하던 방식도 그대로 사용할 수 있습니다. + +```cpp +using ::testing::NiceMock; + +TEST(...) { + NiceMock mock_foo(5, "hi"); // Calls MockFoo(5, "hi"). + EXPECT_CALL(mock_foo, DoThis()); + ... code that uses mock_foo ... +} +``` + +다음으로 설명할 내용은 `StrictMock`인데 이름에서 유추되듯이 `StrictMock`은 uninteresting call이 발생하면 테스트 실패로 간주합니다. 비록 목적은 다르지만 사용방법은 `NiceMock`과 동일합니다. + +```cpp +using ::testing::StrictMock; + +TEST(...) { + StrictMock mock_foo; + EXPECT_CALL(mock_foo, DoThis()); + ... code that uses mock_foo ... + + // The test will fail if a method of mock_foo other than DoThis() + // is called. +} +``` + +NOTE: `NiceMock`과 `StrictMock`의 대상은 *uninteresting call*이며 *unexpected call*은 대상이 아니라는 것을 기억하세요. 이에 대한 자세한 내용은 [Understanding Uninteresting vs Unexpected Calls](https://github.com/google/googletest/blob/master/googlemock/docs/cook_book.md#uninteresting-vs-unexpected)를 참조하세요. + +지금까지 설명한 기능들을 사용하기 위해서 지켜야 할 규칙이 몇 가지 있습니다. (주로 C++ 언어의 특성으로 인해 만들어진 것들입니다.) + +1. `NiceMock`, `StrictMock`는 `MockFoo` 내부에 **직접** 정의된(`MOCK_METHOD` 사용) mock method에만 적용됩니다. 만약, `MockFoo`의 **base class**에 mock method가 정의되어 있다면 동작하지 않을 확률이 높습니다. Compiler에 따라 다를 수는 있지만 대부분의 경우에 동작하지 않습니다. 더불어 `NiceMock>`와 같이 `NiceMock`, `StrictMock`을 nesting하는 것은 **지원하지 않습니다**. +2. `MockFoo`의 destructor가 virtual이 아니라면 `NiceMock`, `StrictMock`가 정상적으로 동작하지 않을 수 있습니다. 이 이슈는 http://b/28934720를 통해 수정 및 확인 중에 있습니다. +3. `MockkFoo`의 constructor나 destructor가 수행되는 동안에는 nice 모드, strict 모드가 적용되지 *않습니다*. 따라서 constructor나 destructor에서 `this`를 이용해 mock method를 호출하는 경우에는 의도한 것과 다르게 동작할 수 있습니다. 이러한 동작방식은 C++ 언어자체의 제약사항에 기반하며 constructor나 destructor에서 `this`를 이용해 virtual method를 호출하는 경우에는 해당 method를 non-virtual로 취급하게 됩니다. 다시 말해서 derived class의 constructor나 destructor가 호출되면 자연스럽게 base class의 constructor나 destructor도 호출될텐데 그런 경우에 base class의 constructor나 destructor에서 `this`를 사용했다면 이는 base class 자신을 가리킨다는 의미입니다. 이런 동작방식은 안정성을 보장하기 위한 C++언어 특징 중 하나입니다. 만약, 이렇게 동작하지 않는다면 base class의 constructor는 아직 초기화되지 않은 derived class의 정보를 사용하려고 시도할 것이며 마찬가지로 destructor도 이미 삭제된 derived class의 정보를 참조하게 되므로 심각한 문제를 초래할 수 있습니다. + +마지막으로 naggy 모드, strict 모드는 테스트가 자주 실패하게 하고 유지보수를 어렵게 만들기도 하기 때문에 **주의**해서 사용해야 합니다. 예를 들어, 코드의 외부는 그대로 두고 내부적인 동작에 대해서만 refactoring하는 경우라면 테스트코드는 수정하지 않는 것이 이상적입니다. 그러나 naggy 모드로 설정되어 있다면 이러한 내부적인 수정에 대해서도 많은 warning들을 발생시킬 것입니다. 게다가 strict 모드라면 아예 테스트가 실패하기 때문에 테스트코드의 유지보수 비용이 증가될 것입니다. 이를 위해 추천드리는 방법은 일반적인 상황에서는 nice 모드을 주로 사용하고 테스트코드를 개발할 때는 naggy 모드를 사용하는 것입니다. (다만, 현재 gMock은 naggy모드가 기본설정이긴 합니다.) 그리고 strict 모드는 필요한 경우에 한번씩 점검용으로 사용하기 바랍니다. + +#### 기존코드에 영향을 주지 않고, interface를 단순하게 만들기 #### + +아래처럼 argument의 개수가 아주 많은 method를 mocking해야할 때가 있습니다. 이런 method는 테스트코드도 매우 복잡하게 구현될 것입니다. 이런 경우에는 테스트의 목적을 달성하는 과정에서 크게 중요하지 않은 argument들은 걸러냄으로써 interface를 보다 단순하게 만들 수 있습니다. + +```cpp +class LogSink { + public: + ... + virtual void send(LogSeverity severity, const char* full_filename, + const char* base_filename, int line, + const struct tm* tm_time, + const char* message, size_t message_len) = 0; +}; +``` + +이렇게 argument가 많아지면 사용하는데에 어려움을 겪게 됩니다. 위의 `send()`는 argument도 많고 게다가 `message`로 전달되는 문자열이 길어지면 소스코드 해석이 매우 어려워 질 것입니다. 이러한 method에는 mock을 적용하는 것도 어렵습니다. 어떻게 해야할까요? + +방법은 간단한 mock method를 하나 만들고 이것을 `send()`의 내부에서 호출하도록 구현하는 것입니다. + +```cpp +class ScopedMockLog : public LogSink { + public: + ... + virtual void send(LogSeverity severity, const char* full_filename, + const char* base_filename, int line, const tm* tm_time, + const char* message, size_t message_len) { + // We are only interested in the log severity, full file name, and + // log message. + Log(severity, full_filename, std::string(message, message_len)); + } + + // Implements the mock method: + // + // void Log(LogSeverity severity, + // const string& file_path, + // const string& message); + MOCK_METHOD(void, Log, + (LogSeverity severity, const string& file_path, + const string& message)); +}; +``` + +위의 코드는 `send()`의 argument가 너무 많고 복잡하기 때문에 `Log()`라는 mock method를 만들어서 `send()`로부터 관심대상이 되는 3개의 argument만 전달받도록 구현한 것입니다. 물론, 어디까지나 `Log()`가 전달받는 3개의 argument를 제외한 나머지는 관심대상이 아니기에 가능한 일입니다. 마지막으로 `Log()`에 expectation을 설정하면 됩니다. `send()`를 mocking하는 것보다 간결하게 테스트코드를 구현할 수 있습니다. + +동일한 방법을 overloaded function을 mocking할 때에도 적용할 수 있습니다. + +```c++ +class MockTurtleFactory : public TurtleFactory { + public: + Turtle* MakeTurtle(int length, int weight) override { ... } + Turtle* MakeTurtle(int length, int weight, int speed) override { ... } + + // the above methods delegate to this one: + MOCK_METHOD(Turtle*, DoMakeTurtle, ()); +}; +``` + +위의 코드는 `MakeTurtle()` 중 어느것이 호출되더라도 내부에서 `DoMakeTurtle()`을 호출하도록 구현한 것입니다. (`MakrTurtle()`의 내부구현은 생략되어 있지만) 그런 후에는 `DoMakeTurtle()`을 mocking하면 됩니다. 즉, overloaded `MakeTurtle()` 중에서 어떤 것이 호출되더라도 사용자가 원하는 방식으로 `Turtle` class의 object를 생성하도록 지정할 수 있습니다. 아래는 `DoMakeTurtle()`이 원하는 action을 수행하도록 지정하는 코드입니다. `MakeMockTurtle()`의 구현부가 생략되어 있지만 이름에서 알 수 있듯이 `Turtle` class의 object를 생성해주는 action을 의미합니다. + +```c++ +ON_CALL(factory, DoMakeTurtle) + .WillByDefault(MakeMockTurtle()); +``` + +#### Concrete Classe Mocking 하기, 그리고 대안 #### + +상위 interface가 없는 class를 바로 mocking 해야하는 상황도 당연히 발생할 수 있습니다. 이러한 class들을 `Concrete`라고 부르겠습니다. 이러한 `Concrete` class는 어떻게 mocking해야 할까요? 지금까지 공부한 내용을 기반으로 하면 먼저 `Concrete` class에 포함된 method를 virtual function으로 만들고 다음으로 `Concrete` class를 상속받는 mock class를 만들면 될 것 같습니다. + +그러나 그렇게 하면 안됩니다. + +왜냐하면 non-virtual function을 virtual function으로 만드는 것은 설계상의 큰 변화이며 class의 동작도 달라지기 때문입니다. Class invariants를 제어하기가 힘들어질 것입니다. 단순히 mocking을 위한 목적으로 그렇게 수정하는 것은 좋지 않습니다. Non-virtual function을 virtual function으로 바꾸고 derived class에서 override하는 구조로 변경할 때에는 타당한 이유가 있어야 합니다. + +그럼 어떻게 할까요? `Concrete` class를 직접 mocking하는 것도 좋지 않습니다. 왜냐하면 제품코드와 테스트코드간의 coupling이 증가되기 때문입니다. 예를 들어 `Concrete` class에 작은 수정이 발생할 때마다 mock class가 제대로 동작하는지 확인해야 하기 때문에 유지보수 비용이 상당히 증가할 것입니다. + +사실 이러한 coupling 때문에 많은 개발자들이 interface를 선호합니다. 지금처럼 테스트코드를 구현하기 위한 경우가 아니더라도 `Concrete` class를 사용하는 것은 coupling 문제를 야기하곤 합니다. 이제 답을 드리면 `Concrete` class를 직접 사용하기보다는 interface를 하나 만들어서 `Concrete`의 adapter처럼 사용하기를 추천합니다. Interface는 항상 virtual function만 정의되어 있기 때문에 mocking 측면에서 유리하며 상술한 것처럼 꼭 테스트코드를 구현하기 위한 목적이 아니더라도 좋은 설계가 될 것입니다. + +물론, interface를 사용하는 것은 몇 가지 overhead를 발생시키긴 합니다. + +- Virtual function call 에 대한 비용이 추가됩니다. (대부분의 경우에 문제가 안됩니다.) +- 개발자들이 공부해야할 내용(추상화 계층)이 추가됩니다. + +그러나 그와 동시에 testability 관점에서는 많은 장점이 있습니다. + +- 먼저, `Concrete` class의 API들이 사용자가 원하는 방향과는 딱 맞지 않을수도 있습니다. 왜냐하면 대부분의 경우에 특정한 사용자만을 위해 API를 설계하지는 않기 때문입니다. 이런 경우에는 `Concrete` class를 위한 interface를 내부적으로 만들어서 사용하면 어떨까요? 단순히 조금 변경하는 것을 넘어 상위수준의 기능을 추가할 수도 있고, 아예 이름을 변경할 수도 있을 것입니다. 이렇듯 interface를 사용하게 되면 코드의 가독성, 유지보수성, 생산성이 향상됩니다. +- `Concete` class의 내부구현을 수정할때 연관된 곳들을 수정할 필요가 없어집니다. Inteface만 그대로 유지한다면 기존의 코드들은 이러한 변화로부터 안전하게 보호될 것입니다. + +누군가는 이러한 방법이 중복된 코드를 만드는 부분에 대해 우려하기도 합니다. 물론, 이러한 걱정도 충분히 이해가 갑니다. 다만, 아래 2가지 이유를 읽어보면 걱정은 사라질 것입니다. + +- 다양한 프로젝트에서 `Concrete` class를 가져다가 사용한다고 해봅시다. 각각의 프로젝트는 `Concrete` class를 사용하기 위해 기존 소스코드를 필연적으로 수정하게 됩니다. 이렇게 `Concrete` class를 사용하기 위해 원래 코드를 수정하는 것은 domain-specific한 interface를 정의하는 것과 다르지 않습니다. +- 여러개의 프로젝트가 동일한 interface를 사용하고자 할 때는 언제든 공유할 수 있습니다. `Concrete` class를 공유하는 것과 하나도 다르지 않습니다. Interface나 adatptor를 만들어서 `Concrete` class 근처에 저장하고 여러 프로젝트들이 사용하도록 하기만 하면 됩니다. + +어떤 방법이든 찬성과 반대의견을 잘 따져보는 것은 중요합니다. 다만, 여기서 설명한 interface를 사용하는 기술은 다양한 상황에서 적용가능하며 Java community에서 긴 시간에 걸쳐 효율성이 입증된 방법임을 말씀드립니다. + +#### Fake class에 위임하기 #### + +어떤 interface를 위한 fake class를 구현해서 테스트를 비롯한 다양한 용도로 사용하는 개발자들이 많이 있습니다. 이렇게 이미 fake class를 잘 사용하고 있는 경우에 굳이 mock class가 필요할까요? 어디까지나 상황에 따라 사용자가 선택해야 합니다. 다만, gMock의 좋은 점은 mock class를 사용하더라도 기존에 사용하던 fake class 역시 그대로 사용할 수 있다는 것입니다. 즉, 양자간에 자유롭게 선택할 수 있습니다. + +```cpp +class Foo { + public: + virtual ~Foo() {} + virtual char DoThis(int n) = 0; + virtual void DoThat(const char* s, int* p) = 0; +}; + +class FakeFoo : public Foo { + public: + char DoThis(int n) override { + return (n > 0) ? '+' : + (n < 0) ? '-' : '0'; + } + + void DoThat(const char* s, int* p) override { + *p = strlen(s); + } +}; +``` + +`Foo`라는 interface에 대해 mock class를 만들고 expectation을 설정하려 합니다. 이 때, 이미 사용하고 있던 `FakeFoo` class를 재사용하고 싶다면 어떻게 하면 될까요? 바로 mock function의 기본 동작으로 fake function을 지정하면 됩니다. 이를 통해 fake function과 동일한 내용을 mock class에 다시 구현하게 되는 중복작업를 피할 수 있습니다. 결과적으로 mock function의 동작은 이미 구현되어 있는 fake function을 그대로 사용하고 expectation만 지정하면 됩니다. + +이렇게 gMock을 사용해 fake function을 default action으로 지정하고 싶을 경우, 아래 패턴을 사용하기 바랍니다. + +```cpp +class MockFoo : public Foo { + public: + // Normal mock method definitions using gMock. + MOCK_METHOD(char, DoThis, (int n), (override)); + MOCK_METHOD(void, DoThat, (const char* s, int* p), (override)); + + // Delegates the default actions of the methods to a FakeFoo object. + // This must be called *before* the custom ON_CALL() statements. + void DelegateToFake() { + ON_CALL(*this, DoThis).WillByDefault([this](int n) { + return fake_.DoThis(n); + }); + ON_CALL(*this, DoThat).WillByDefault([this](const char* s, int* p) { + fake_.DoThat(s, p); + }); + } + + private: + FakeFoo fake_; // Keeps an instance of the fake in the mock. +}; +``` + +위의 코드를 보면 `MockFoo` class를 만드는 방법은 일반적인 mock class 생성방법과 다르지 않습니다. 다만, `DoThis()`, `DoThat()` 함수의 기본동작을 `FakeFoo` object로 위임하는 부분이 추가되었습니다. + +`MockFoo` class를 실제로 사용하는 예제는 아래에 있습니다. `TEST()`의 시작 시점에 `DelegateToFake()`를 호출함으로서 `action`을 다시 지정하지 않는 한, `FooFake`에 정의된 function들이 실행되도록 했습니다. + +```cpp +using ::testing::_; + +TEST(AbcTest, Xyz) { + MockFoo foo; + + foo.DelegateToFake(); // Enables the fake for delegation. + + // Put your ON_CALL(foo, ...)s here, if any. + + // No action specified, meaning to use the default action. + EXPECT_CALL(foo, DoThis(5)); + EXPECT_CALL(foo, DoThat(_, _)); + + int n = 0; + EXPECT_EQ('+', foo.DoThis(5)); // FakeFoo::DoThis() is invoked. + foo.DoThat("Hi", &n); // FakeFoo::DoThat() is invoked. + EXPECT_EQ(2, n); +} +``` + +**팁 공유:** + + * 원한다면 얼마든지 mock function의 동작을 다시 변경할 수 있습니다. 즉, 시작부분에서 `DelegateToFake()` 함수를 통해서 default action을 지정했지만 개별 `TEST()`에서 `ON_CALL()` 또는 `EXPECT_CALL()`를 사용하면 자유롭게 변경할 수 있습니다. + * `DelegateToFake()` 함수에서 굳이 모든 fake 함수를 지정할 필요는 없고 원하는 것만 선택적으로 지정하면 됩니다. + * 여기서 논의된 방법들은 overloaded method에도 적용될 수 있습니다. 물론 compiler에 어떤 것을 사용할지는 알려줘야 합니다. 먼저, mock function에 대한 모호성 해결방법은 이 문서의 [Selecting Between Overloaded Functions]() 부분을 참고하세요. 다음으로 fake function에 대한 모호성 해결방법은 `static_cast`를 사용하면 됩니다. 예를 들어 `Foo` class에 `char DoThis(int n)`, `bool DoThis(double x) const` 라는 2개의 overloaded function이 있다고 합시다. 이 상황에서 후자인 `bool DoThis(double x) const`를 invoke 대상으로 지정하고 싶다면 `Invoke(&fake_, static_cast(&FakeFoo::DoThis)`와 같이 사용하면 됩니다. `static_cast` 를 사용해 fuction pointer type을 명확하게 지정한 것입니다. + * 지금까지 mock, fake를 함께 사용하기 위한 방법을 설명했습니다. 그러나 사실 mock, fake를 함께 사용하는 경우가 발생했다면 해당 소프트웨어의 설계가 뭔가 잘못되었음을 의미하는 것이기도 합니다. 아마도 사용자가 interaction-based testing 방법론에 아직 익숙하지 않은 경우일 수 있습니다. 또는 대상 interface가 너무 많은 역할을 수행하고 있기 때문에 분리될 필요가 있을 수도 있습니다. 다시 말하면, **이 방법을 남용하지 않기를 바랍니다**. 어디까지나 refactoring에 필요한 하나의 과정 정도로 생각하면 좋을 것 같습니다. + +Mock, fake가 함께 나타날 때, bad sign을 찾아내는 예제를 하나 공유하겠습니다. 예를 들어 `System` class라는 low-level system operation을 수행하는 class가 하나 있다고 합시다. 이 class는 파일처리와 I/O라는 2가지 기능에 특화되었다고 하겠습니다. 이 때, `System` class의 I/O기능만 테스트하고자 한다면 파일처리는 문제 없이 기본적인 동작만 해주면 될 것입니다. 그런데 I/O기능을 테스트하기 위해서 `System` class를 mock class로 만들게 되면 파일처리 기능까지 mocking이 되어 버립니다. 이렇게 되면 개발자가 fake class를 제공하던지 해서 파일처리 기능과 동일한 역할을 구현해줘야 하는 어려움이 발생합니다. + +위의 상황은 `System` class가 너무 많은 역할을 가지고 있음을 보여주는 예입니다. 이런 경우에는 `System` class의 역할을 분리해서 `FileOps`, `IOOps`라는 interface 2개로 분리하는 것도 좋은 방법입니다. 그렇게 되면 분리된 `IOOps` interface만 mocking하여 테스트할 수 있습니다. + +#### Real Class 위임하기 #### + +테스트를 위해 testing double(mock, fake, stub, spy 등)을 사용할 떄의 문제는 동작이 real object와 달라질 수 있다는 것입니다. 물론 negative test를 위해서 일부러 다르게 만들기도 하지만, 때로는 실수로 인해 동작이 달라지기도 합니다. 후자의 경우라면 bug를 잡아내지 못하고 제품을 출시하는 것과 같은 문제가 발생하기도 합니다. + +이런 경우를 위해 *delegating-to-real* 이라고 불리는 기술을 사용할 수 있습니다. 즉, mock object가 real object와 동일하게 동작하게 만들어 주는 방법입니다. 사용법 자체는 바로 위에서 다룬 [delegating-to-fake]() 와 매우 유사합니다. 다른 점이라면 fake object 대신 real object로 변경된 것 뿐입니다. 아래 예제를 확인하세요. + +```cpp +using ::testing::AtLeast; + +class MockFoo : public Foo { + public: + MockFoo() { + // By default, all calls are delegated to the real object. + ON_CALL(*this, DoThis).WillByDefault([this](int n) { + return real_.DoThis(n); + }); + ON_CALL(*this, DoThat).WillByDefault([this](const char* s, int* p) { + real_.DoThat(s, p); + }); + ... + } + MOCK_METHOD(char, DoThis, ...); + MOCK_METHOD(void, DoThat, ...); + ... + private: + Foo real_; +}; + +... + MockFoo mock; + EXPECT_CALL(mock, DoThis()) + .Times(3); + EXPECT_CALL(mock, DoThat("Hi")) + .Times(AtLeast(1)); + ... use mock in test ... +``` + +이제 gMock을 통해 테스트코드에서 real object를 사용한 검증도 가능하게 되었습니다. 이러한 방법을 사용하면 테스트코드를 구현하는 입장에서도 제품코드의 품질을 높이는 입장에서도 매우 도움이 됩니다. + +#### Base Class에 위임하기 #### + +이상적인 경우 interface(base class)는 pure virtual method만 가지는 것이 좋습니다. 다만, C++에서는 interface의 method가 실제 구현체를 가지고 것도 적법합니다. 아래 예제를 보겠습니다. + +```cpp +class Foo { + public: + virtual ~Foo(); + + virtual void Pure(int n) = 0; + virtual int Concrete(const char* str) { ... } +}; + +class MockFoo : public Foo { + public: + // Mocking a pure method. + MOCK_METHOD(void, Pure, (int n), (override)); + // Mocking a concrete method. Foo::Concrete() is shadowed. + MOCK_METHOD(int, Concrete, (const char* str), (override)); +}; +``` + +사용자가 `MockFoo`라는 mock class를 만들기는 했지만, interface에 있는 구현을 그대로 사용하고 싶은 method가 있을 수도 있습니다. 또는, 원래부터 특정 method만 mocking하려고 했고 나머지 method는 관심대상이 아니었을 수도 있습니다. 예제를 보면 `Foo::Concrete()`가 실제 구현부를 가지고 있으며 mocking이 필요한 대상은 아니라고 가정하겠습니다. 다만, 원래대로 동작하게 하려고 interface의 구현을 `MockFoo::Concrete`로 복사/붙여넣기 하는 것은 중복코드를 만들게 되므로 좋은 방법이 아닙니다. 이런 경우에는 interface(base class)로 위임하는 것이 좋습니다. + +C++에서 dervied class가 base class에 접근하기 위한 방법은 `Foo::Concrete()`와 같이 범위지정 연산자( `::` )를 사용하는 것입니다. 전체 예제는 아래와 같습니다. + +```cpp +class MockFoo : public Foo { + public: + // Mocking a pure method. + MOCK_METHOD(void, Pure, (int n), (override)); + // Mocking a concrete method. Foo::Concrete() is shadowed. + MOCK_METHOD(int, Concrete, (const char* str), (override)); + + // Use this to call Concrete() defined in Foo. + int FooConcrete(const char* str) { return Foo::Concrete(str); } +}; +``` + +이제 `EXPECT_CALL`을 사용해서 `Concrete()`에 대한 호출이 `FooConcrete()`를 호출하도록 지정하면 끝입니다. + +```cpp +... + EXPECT_CALL(foo, Concrete).WillOnce([&foo](const char* str) { + return foo.FooConcrete(str); + }); +``` + +아니면 `ON_CALL`을 사용해서 default action으로 지정하는 것도 괜찮습니다. + +```cpp +... + ON_CALL(foo, Concrete).WillByDefault([&foo](const char* str) { + return foo.FooConcrete(str); + }); +``` + +그런데 왜 action을 지정할 때 `{ return foo.Concrete(str); }`과 같이 구현해서 base class의 method를 직접 호출하지 않고 한 단계를 거치도록 구현해야 할까요? 왜냐하면 base class의 `Concrete()`가 virtual function이기 때문에 이것을 직접 호출하면 derived class인 MockFoo의 `Concrete()`가 호출될 것이기 때문입니다. 게다가 derived class의 `MockFoo:Concrete`는 다시 base class의 `Foo::Concrete` 를 호출하고 있기 때문에 이러한 과정이 무한반복되는 문제가 발생할 것입니다. + +### Matcher 사용하기 ### + +#### 전달된 argument가 기대한 값과 일치하는지 확인하기 #### + +Matcher를 사용하면 mock method로 전달되는 argument가 정확히 어떤 값이기를 기대한다고 명세할 수 있습니다. 예를 들면, 아래에서 `DoThis()`로는 `5`가 전달되어야 하고, `DoThat()`은 `"Hello", bar`가 전달되기를 기대합니다. + +```cpp +using ::testing::Return; +... + EXPECT_CALL(foo, DoThis(5)) + .WillOnce(Return('a')); + EXPECT_CALL(foo, DoThat("Hello", bar)); +``` + +#### 간단한 Matcher 사용하기 #### + +Matcher를 이용해서 전달되는 argument가 어떤 범위에 포함되길 원하는지 명세하는 것도 가능합니다. 아래에서 `DoThis()`는 `5`보다 큰 값이 전달되기를 기대하고, `DoThat()`은 첫번째 argument로 `"Hello"`, 그리고 두번째 argument로는 `Null`이 아닌 값을 기대한다고 명세하고 있습니다. + +```cpp +using ::testing::Ge; +using ::testing::NotNull; +using ::testing::Return; +... + EXPECT_CALL(foo, DoThis(Ge(5))) // The argument must be >= 5. + .WillOnce(Return('a')); + EXPECT_CALL(foo, DoThat("Hello", NotNull())); + // The second argument must not be NULL. +``` + +또한, 어떤 값이라도 허용한다는 의미를 가지는 `_` 는 자주 사용하는 matcher 중에 하나 입니다. + +```cpp +EXPECT_CALL(foo, DoThat(_, NotNull())); +``` + +#### Matcher 조합하기 #### + +`AllOf()`, `AnyOf()`, `Not()`, `AnyOfArray()`, `Not()` 을 이용하면 여러가지 matcher들을 조합할 수 있습니다. 각각의 역할은 이름과 동일합니다. + +```cpp +using ::testing::AllOf; +using ::testing::Gt; +using ::testing::HasSubstr; +using ::testing::Ne; +using ::testing::Not; +... + // The argument must be > 5 and != 10. + EXPECT_CALL(foo, DoThis(AllOf(Gt(5), + Ne(10)))); + + // The first argument must not contain sub-string "blah". + EXPECT_CALL(foo, DoThat(Not(HasSubstr("blah")), + NULL)); +``` + +#### Casting Matchers #### + +gMock의 matcher는 정적으로 타입을 결정합니다. 즉, compile time에 타입을 검사해서 잘못된 부분을 알려준다는 의미입니다. 예를 들어 `Eq(5)`인 곳에 `string`을 전달하면 compile error가 발생하게 됩니다. + +단, 이러한 타입검사가 완벽하게 철저하다고는 할 수 없습니다. 만약에 `long`을 위한 matcher에 `int`가 전달되면 어떻게 될까요? 네, 잘 동작합니다. 왜냐하면 `int`가 `Matcher`을 만나면 `int`가 `long`으로 먼저 변환된 후에 matcher에 전달되기 때문입니다. 즉, 암시적으로 타입변환이 일어납니다. + +이러한 암시적 변환을 원하지 않는 경우에는, gMock의 `SafeMacherCast(m)`을 사용할 수 있습니다. 이 기능은 `m`이라는 matcher를 받아서 `Matcher` 타입으로 변환해 주는데, 이 때 안전한 타입변환을 보장하기 위해서 gMock은 아래 내용들을 검사합니다. (matcher가 전달받은 타입을 `U`라고 가정합니다) + +1. 타입 `T`가 타입 `U`로 암시적으로 변환 가능한지 +2. `T`와 `U`가 모두 산술타입(`bool`, `int`, `float` 등)일 때, `T` 에서 `U`로 변하는 과정에 손실이 없는지 +3. `U`가 참조타입일 때, `T`도 참조타입인지 + +위 조건들 중 하나라도 만족하지 않으면 compile error가 발생합니다. + +아래 예제를 확인하세요. + +```cpp +using ::testing::SafeMatcherCast; + +// A base class and a child class. +class Base { ... }; +class Derived : public Base { ... }; + +class MockFoo : public Foo { + public: + MOCK_METHOD(void, DoThis, (Derived* derived), (override)); +}; + +... + MockFoo foo; + // m is a Matcher we got from somewhere. + EXPECT_CALL(foo, DoThis(SafeMatcherCast(m))); +``` + +`SafeMatcherCast(m)`의 타입검사가 너무 엄격해서 사용하기 어려운 경우에는 이와 유사한 `MatcherCast(m)`을 사용할 수도 있습니다. 차이점이라면 `MatcherCast`는 `T`에서 `U`로의 `static_cast`가 가능한 경우에 한해서 matcher의 타입변환을 허용해준다는 점입니다. 즉, 타입검사가 좀 더 느슨하다고 할 수 있습니다. + +`MatcherCast`는 자동적으로 C++의 타입검사 기능(`static_cast` 등)를 사용하게 합니다. 다만, `static_cast` 과 같은 C++ 타입검사는 변환과정에서 정보를 없애버리기도 하기 때문에 남용/오용하지 않도록 주의해야 합니다. + +#### 여러개의 Overloaded Function 중에서 선택하기 #### + +Overloaded function 중에서 하나를 선택하고 싶은 경우가 있을 것입니다. 이 때에는 모호성을 없애기 위해서 compiler에게 관련 내용을 알려줄 필요가 있습니다. + +먼저, const-ness overloading에 대한 모호성 제거방법은 `Const()` 를 사용하는 것입니다. gMock에서 `Const(x)`는 `x`의 `const`참조를 반환하게 되어 있습니다. 사용법은 아래 예제를 참조하십시오. + +```cpp +using ::testing::ReturnRef; + +class MockFoo : public Foo { + ... + MOCK_METHOD(Bar&, GetBar, (), (override)); + MOCK_METHOD(const Bar&, GetBar, (), (const, override)); +}; + +... + MockFoo foo; + Bar bar1, bar2; + EXPECT_CALL(foo, GetBar()) // The non-const GetBar(). + .WillOnce(ReturnRef(bar1)); + EXPECT_CALL(Const(foo), GetBar()) // The const GetBar(). + .WillOnce(ReturnRef(bar2)); +``` + +다음으로 argument의 개수는 같고 타입만 다를 때의 모호성 제거 방법입니다. 이를 위해서는 matcher에 정확한 타입을 기입하는 것이 필요합니다. `Matcher()`을 사용해서 matcher를 감싸거나 `TypeEq`, `An()`과 같이 타입을 미리 지정하는 matcher를 사용하면 됩니다. + +```cpp +using ::testing::An; +using ::testing::Lt; +using ::testing::Matcher; +using ::testing::TypedEq; + +class MockPrinter : public Printer { + public: + MOCK_METHOD(void, Print, (int n), (override)); + MOCK_METHOD(void, Print, (char c), (override)); +}; + +TEST(PrinterTest, Print) { + MockPrinter printer; + + EXPECT_CALL(printer, Print(An())); // void Print(int); + EXPECT_CALL(printer, Print(Matcher(Lt(5)))); // void Print(int); + EXPECT_CALL(printer, Print(TypedEq('a'))); // void Print(char); + + printer.Print(3); + printer.Print(6); + printer.Print('a'); +} +``` + +#### Argument에 따라 다른 Action을 실행하기 #### + +Mock method가 호출되면, 소스코드 상에서 제일 *밑에 있는* expectation부터 탐색합니다. (물론 해당 expectation은 active 상태여야 합니다.) 이러한 동작방식은 [newer overrides older]() 에서 설명된 적이 있습니다. 이러한 규칙을 이용하면 argument로 전달되는 값에 따라 다른 action이 수행되도록 지정하는 것도 가능합니다. + +```cpp +using ::testing::_; +using ::testing::Lt; +using ::testing::Return; +... + // The default case. + EXPECT_CALL(foo, DoThis(_)) + .WillRepeatedly(Return('b')); + // The more specific case. + EXPECT_CALL(foo, DoThis(Lt(5))) + .WillRepeatedly(Return('a')); +``` + +위 코드의 `foo.DoThis()`는 전달된 argument가 `5` 미만이라면 `a`를 반환하고, 그 외의 경우에는 `b`를 반환할 것입니다. + +#### Argument 간의 관계 비교하기 #### + +지금까지는 argument 하나하나에 대한 matcher사용법을 다뤘습니다. 그러나 "첫번째 argument가 두번째 argument보다 작아야 된다." 와 같이 argument 간의 관계를 비교하고 싶은 경우도 있을 것입니다. 이런 경우에는 `With()` 를 사용하면 원하는 조건을 비교할 수 있습니다. + +```cpp +using ::testing::_; +using ::testing::Ne; +using ::testing::Lt; +... + EXPECT_CALL(foo, InRange(Ne(0), _)) + .With(Lt()); +``` + +위의 코드는 `InRange()`의 첫번째 argument가 `0`이 아니면서 두번째 argument보다 작아야함을 의미합니다. + +`With(m)` 내부의 `m`은 `Matcher<::testing::tuple>`타입이며 여기서 `A1`, ..., `An`은 function argument 들을 의미합니다. + +`With()` 내부에 `m`대신에 `AllArgs(m)`이라고 구현하는 것도 가능합니다. 물론, 동작자체는 동일하지만 `.With(Lt())`보다 `.With(AllArgs(Lt()))`이 더 명시적으로 표현하는 방법입니다. + +`Args(m)` 을 사용하면 비교하고 싶은 argument만 골라서(tuple형태로) 비교할 수도 있습니다. 아래 예제를 보겠습니다. + +```cpp +using ::testing::_; +using ::testing::AllOf; +using ::testing::Args; +using ::testing::Lt; +... + EXPECT_CALL(foo, Blah) + .With(AllOf(Args<0, 1>(Lt()), Args<1, 2>(Lt()))); +``` + +위 코드는 `Blah()`가 argument 3개(`x`, `y`, `z`)를 전달받았을 때, `x < y < z` 를 만족해야 함을 의미합니다. + +`With`를 쉽게 사용하기 위해서 gMock의 일부 matcher들은 2-tuples 타입을 지원하고 있습니다. 위 예제의 `Lt()`도 그 중 하나입니다. 2-tuples를 지원하는 matcher의 전체목록은 [여기]()를 참조하시기 바랍니다. + +만약 `.With(Args<0, 1>(Truly(&MyPredicate)))`와 같이 직접 만든 predicate를 사용하고 싶은 경우에는 해당 predicate의 argument가 `::testing::tuple` 타입으로 선언되어 있어야만 합니다. 왜냐하면 gMock이 `Args<>`를 통해 선택된 argument들을 1개의 tuple형태로 변환해서 predicate으로 전달하기 때문입니다. + +#### Matcher를 Predicate처럼 사용하기 #### + +이미 눈치챘겠지만 사실 matcher는 predicate의 확장판(출력문이 잘 정의된)이라고 볼 수 있습니다. 현재 C++ STL의 ` 을 포함해서 많은 algorithm들이 predicate를 argument로 받을 수 있게 구현되어 있습니다. 따라서 predicate의 확장판인 matcher도 그러한 내용을 지원하는 것이 맞을 것입니다. + +다행히도 matcher로 unary predicate functor를 대체할 수 있습니다. 아래 예제와 같이 `Matches()`를 이용해 matcher를 감싸주기만 하면 됩니다. + +```cpp +#include +#include + +using ::testing::Matches; +using ::testing::Ge; + +vector v; +... +// How many elements in v are >= 10? +const int count = count_if(v.begin(), v.end(), Matches(Ge(10))); +``` + +gMock에서는 matcher들을 여러개 조합하는 것이 쉽게 가능했습니다. 이것은 여기서도 그대로 적용됩니다. 예를 들어 `x>=0, x<=100, x!=50`를 조합하는 방법도 아래와 같이 간단합니다. 동일한 작업을 C++ STL의 ``을 통해 구현하려면 상당히 어려운 작업이 될 것입니다. + +```cpp +using testing::AllOf; +using testing::Ge; +using testing::Le; +using testing::Matches; +using testing::Ne; +... +Matches(AllOf(Ge(0), Le(100), Ne(50))) +``` + +#### Matcher를 googletest Assertion처럼 사용하기 #### + +Matcher는 기본적으로 predicate와 동일하고 상응하는 출력문도 잘 정의되어 있습니다. 따라서 matcher를 googletest assertion처럼 사용할 수 있다면 매우 유용할 것입니다. gMock의 `ASSERT_THAT`, `EXPECT_THAT`은 이러한 기능을 제공합니다. + +```cpp + ASSERT_THAT(value, matcher); // Asserts that value matches matcher. + EXPECT_THAT(value, matcher); // The non-fatal version. +``` + +예를 들어, googletest에서 아래와 같이 사용할 수 있습니다. + +```cpp +#include "gmock/gmock.h" + +using ::testing::AllOf; +using ::testing::Ge; +using ::testing::Le; +using ::testing::MatchesRegex; +using ::testing::StartsWith; + +... + EXPECT_THAT(Foo(), StartsWith("Hello")); + EXPECT_THAT(Bar(), MatchesRegex("Line \\d+")); + ASSERT_THAT(Baz(), AllOf(Ge(5), Le(10))); +``` + +코드에서 예측가능한 것처럼 `Foo()`, `Bar()`, `Baz()`는 아래 내용들을 검증하게 됩니다. + +- `Foo()` 가 `"Hello"`로 시작하는 문자열을 반환하는지 검증합니다. +- `Bar()` 가 정규식 `"Line \\d+"`과 매칭되는 값을 반환하는지 검증합니다. +- `Baz()` 가 [5,10] 범위내의 값을 반환하는지 검증합니다. + +이러한 macro의 좋은 점은 소스코드가 *말(영어)처럼 자연스럽게 읽혀진다*는 것입니다. 또한, 동일한 방법으로 failure message도 이해하기 쉽게 출력됩니다. 예를 들어 `Foo()`의 `EXPECT_THAT()`이 실패하면 아래와 같은 message가 출력됩니다. + +```bash +Value of: Foo() + Actual: "Hi, world!" +Expected: starts with "Hello" +``` + +**Credit:** `(ASSERT|EXPECT)_THAT`의 아이디어는 `assertThat()`을 `JUnit`에 추가했던 [Hamcrest](https://github.com/hamcrest/) 프로젝트에서 가져왔음을 밝힙니다. + +#### Predicate를 Matcher처럼 사용하기 #### + +gMock은 다양한 built-in matcher들을 제공하고 있습니다. 그럼에도 사용자가 필요로 하는 모든 경우를 만족하지는 못할 것입니다. 그럴 때에는 unary predicate function 또는 functor를 matcher처럼 사용하는 것도 가능합니다. 이를 위해서는 `Truly()`로 predicate를 감싸기만 하면 됩니다. + +```cpp +using ::testing::Truly; + +int IsEven(int n) { return (n % 2) == 0 ? 1 : 0; } +... + // Bar() must be called with an even number. + EXPECT_CALL(foo, Bar(Truly(IsEven))); +``` + +또한, predicate function(functor)이 꼭 `bool`타입을 반환하지 않아도 됩니다. 반환타입은 조건문 `if (condition)`에 사용할 수 있는 타입이기만 하면 됩니다. + +#### 복사가 안되는 argument를 비교하기 #### + +사용자가 `EXPECT_CALL(mock_obj, Foo(bar))`라고 구현했다면, gMock은 `Foo()`의 argument로 전달된 `bar`의 복사본을 만들고 내부적으로 저장해 둡니다. 그랬다가 추후에 `Foo()`가 실제로 호출되면 미리 저장해둔 `bar`의 복사본을 사용하는 것입니다. 이렇게 동작하는 이유는 `EXPECT_CALL()`이 호출되고 난 후에 `bar`의 값이 변경되는 문제를 방지하기 위한 것입니다. `Eq(bar)`, `Le(bar)`와 같은 다른 matcher를 사용하는 경우에도 동일한 방식이 적용됩니다. + +그런데 여기서 `bar`의 복사가 불가능하다면 어떻게 해야 할까요? (예를 들어 copy constructor가 없다거나) 첫번째 해결방법은 자체적으로 matcher function을 구현하여 `Truly()`와 함께 사용하는 것입니다. 두번째 해결방법은 `EXPECT_CALL()`이 호출된 이후에는 `bar`가 수정되지 않을 것임을 gMock에게 알려주는 것입니다. 그렇게 되면 gMock은 `bar`의 복사본 대신에 참조(주소)를 저장하게 됩니다. 아래와 같이 하면 됩니다. + +```cpp +using ::testing::ByRef; +using ::testing::Eq; +using ::testing::Lt; +... + // Expects that Foo()'s argument == bar. + EXPECT_CALL(mock_obj, Foo(Eq(ByRef(bar)))); + + // Expects that Foo()'s argument < bar. + EXPECT_CALL(mock_obj, Foo(Lt(ByRef(bar)))); +``` + +Remember: 위와 같이 참조형태(`ByRef`)로 전달한 후에 `bar`를 수정하는 것은 미정의 동작이므로 위험합니다. + +#### Object Member 비교하기 #### + +Mock function의 argument로 object가 전달되면 어떻게 해야 할까요? Object로 전달되는 argument를 비교하기 위해 object 전체를 비교하는 것은 좋은 방법이 아닐 것입니다. 즉, 특정한 member variable만 비교하고 싶은 경우를 의미합니다. 이런 경우에는 Field()` 또는 `Property()`를 사용하세요. 사용방법은 아래와 같습니다. + +```cpp +Field(&Foo::bar, m) +``` + +`Field()`는 `Foo`객체의 `bar`라는 member variable이 matcher `m`을 만족하는지 비교합니다. + +```cpp +Property(&Foo::baz, m) +``` + +`Property()`는 `Foo` 객체의 `baz()`라는 method의 반환값이 matcher `m`을 만족하는지 비교합니다. + +아래는 예제입니다. + +| Expression | Description | +| :------------------------------------------ | :-------------------------------------------------- | +| `Field(&Foo::number, Ge(3))` | Matches `x` where `x.number >= 3`. | +| `Property(&Foo::name, StartsWith("John "))` | Matches `x` where `x.name()` starts with `"John "`. | + +유의할 점은 `Property(&Foo::baz, ...)`에서 사용한 `baz()`는 argument가 없는 `const` function으로 선언되어야 한다는 점입니다. + +또한, `Field()`와 `Property()`는 object를 가리키는 포인터도 비교할 수 있습니다. 아래 예제를 확인하세요. + +```cpp +using ::testing::Field; +using ::testing::Ge; +... +Field(&Foo::number, Ge(3)) +``` + +위 표현은 `p`라는 포인터가 전달되면 `p->number >= 3`이라는 비교를 수행하게 됩니다. 만약, `p`가 `NULL`이라면 항상 실패하게 됩니다. + +만약, 1개 이상의 member variable을 비교하고 싶다면 어떻게 하면 될까요? 기존처럼 `AllOf()`를 사용하면 됩니다. + +#### 포인터가 가리키는 값을 비교하기 #### + +C++에서는 포인터 타입의 function argument 사용이 가능합니다. 이를 위해서 `IsNull()`, `NotNull()` 과 같은 matcher와 포인터를 위한 matcher들이 다수 준비되어 있습니다. 만약, 포인터 자체를 비교하는 것이 아니라 포인터가 가리키는 값을 비교하려면 어떻게 하면 될까요? 그런 경우에는 `Pointee(m)` matcher를 사용하면 됩니다. + +아래 예제의 `Pointee(m)`은 포인터가 가리키는 값을 비교하는데 사용합니다. + +```cpp +using ::testing::Ge; +using ::testing::Pointee; +... + EXPECT_CALL(foo, Bar(Pointee(Ge(3)))); +``` + +위 코드의 의미는 `foo.Bar()`가 포인터를 argument로 전달받는데 그 포인터가 가리키는 값이 3보다 크거나 같기를 기대한다는 뜻입니다. + +한 가지 좋은 점은 `Pointee()`는 `NULL` 포인터를 실패로 간주한다는 것입니다. 따라서 굳이 아래처럼 쓰지는 않아도 됩니다. + +```cpp +using ::testing::AllOf; +using ::testing::NotNull; +using ::testing::Pointee; +... + AllOf(NotNull(), Pointee(m)) +``` + +즉, `NULL` 포인터가 테스트 프로그램을 비정상 종료시킬 염려는 하지 않아도 됩니다. + +또한, `Pointee()`는 raw pointer뿐만 아니라 smart pointer(`linked_ptr`, `shared_ptr`, `scoped_ptr` 등)를 사용하는 경우에도 잘 동작합니다. + +그럼 포인터를 가리키는 포인터를 어떨까요? 추측해 보십시오. 네, `Pointee()`는 여러번 중첩해서 사용할 수 있습니다. 예를 들어 `Pointee(Pointee(Lt(3)))`라는 코드의 의미는 포인터가 가리키는 포인터가 가리키는 값이 3보다 작거나 같은지 비교합니다. + + +#### Object Property 테스트하기 #### + +Argument로 object가 전달되었을 때, 해당 object의 property를 검증하고 싶은 경우가 있을 것입니다. 그러나 그러한 경우를 위한 matcher는 아직 없으므로 필요한 경우에는 직접 정의해야 합니다. 여기서는 이처럼 matcher를 직접 구현하려 할 때 일반 function을 구현하는 것처럼 빠르게 구현하는 방법을 설명합니다. + +일단, `Foo`타입의 argument를 갖는 mock function이 있다고 가정합니다. 그리고 `Foo`타입은 `int bar()`와 `int baz()`라는 function을 가지고 있습니다. 이 때, `bar()`, `baz()`라는 function 2개의 반환값을 더한 값(`bar()` + `baz()`) 이 기대한 값을 만족하는지 구현하고 싶습니다. 어떻게 하면 될까요? 아래 예제를 참조하세요. + +```cpp +using ::testing::Matcher; +using ::testing::MatcherInterface; +using ::testing::MatchResultListener; + +class BarPlusBazEqMatcher : public MatcherInterface { + public: + explicit BarPlusBazEqMatcher(int expected_sum) + : expected_sum_(expected_sum) {} + + bool MatchAndExplain(const Foo& foo, + MatchResultListener* /* listener */) const override { + return (foo.bar() + foo.baz()) == expected_sum_; + } + + void DescribeTo(::std::ostream* os) const override { + *os << "bar() + baz() equals " << expected_sum_; + } + + void DescribeNegationTo(::std::ostream* os) const override { + *os << "bar() + baz() does not equal " << expected_sum_; + } + private: + const int expected_sum_; +}; + +Matcher BarPlusBazEq(int expected_sum) { + return MakeMatcher(new BarPlusBazEqMatcher(expected_sum)); +} + +... + EXPECT_CALL(..., DoThis(BarPlusBazEq(5)))...; +``` + +#### Container 비교하기 #### + +STL container(list, vector, map 등)를 비교하려면 어떻게 해야 할까요? 일단, C++ STL containter 대부분은 `==` 연산자를 제공하기 때문에 간단하게 `Eq(expected_container)`를 사용해도 됩니다. 여기서 `expected_container`는 argument로 로 전달되기를 바라는 기대값 container 입니다. + +여기에 더해서 좀 더 유연성 있게 비교하고 싶을 수도 있습니다. 예를 들면 "첫번째 argument는 완전히 같아야 하고 두번째 argument는 어떤 값이든 괜찮다"와 같은 경우가 있을 수 있습니다. 이러한 비교를 구현할 때, container에 포함된 element 자체가 그렇게 많지 않다면 expected container를 선언하는 것 자체가 조금 귀찮은 일이 되기도 합니다. + +이런 경우를 위해서 `ElementsAre()` 및 `UnorderedElementsAre()`이라는 matcher를 제공하고 있습니다. 먼저 `ElementAre()`의 사용예제는 아래와 같습니다. + +```cpp +using ::testing::_; +using ::testing::ElementsAre; +using ::testing::Gt; +... + MOCK_METHOD(void, Foo, (const vector& numbers), (override)); +... + EXPECT_CALL(mock, Foo(ElementsAre(1, Gt(0), _, 5))); +``` + +위의 matcher를 보면 `ElementsAre()`로 전달된 argument가 4개임을 볼 수 있습니다. 이것은 곧 argument로 전달된 container가 4개의 element를 가져야 함을 의미합니다. 다시 각각을 보면 container의 첫번째 element는 `1`과 같아야 하고, 두번째 element는 `0`보다 커야 하고, 세번째 element는 어떤 값이든 괜찮고, 네번째 element는 `5`이어야 함을 의미합니다. + +다음으로 `UnorderedElementsAre()`를 사용한 코드는 아래와 같습니다. + +```cpp +using ::testing::_; +using ::testing::Gt; +using ::testing::UnorderedElementsAre; +... + + MOCK_METHOD1(Foo, void(const vector& numbers)); +... + + EXPECT_CALL(mock, Foo(UnorderedElementsAre(1, Gt(0), _, 5))); +``` + +위의 matcher도 역시 container가 4개의 element를 가져야 함을 의미합니다. 다만 element들의 순서는 관계가 없고 조건을 만족하는 element 4개가 있는지만 확인합니다. + +`ElementsAre()`과 `UnorderedElementsAre()`는 argument의 개수가 0~10개까지만 사용할 수 있도록 overloaded되어 있습니다. 따라서 container의 element가 10개 이상이라면 위와 같은 방법으로는 비교할 수 없습니다. 이 때에는 C-style 배열을 사용해서 expected container(elements들)를 지정하는 것도 가능합니다. + +```cpp +using ::testing::ElementsAreArray; +... + // ElementsAreArray accepts an array of element values. + const int expected_vector1[] = {1, 5, 2, 4, ...}; + EXPECT_CALL(mock, Foo(ElementsAreArray(expected_vector1))); + + // Or, an array of element matchers. + Matcher expected_vector2[] = {1, Gt(2), _, 3, ...}; + EXPECT_CALL(mock, Foo(ElementsAreArray(expected_vector2))); +``` + +배열의 크기가 compile time에 정해지지 않은 상황이라면 배열크기 정보를 같이 전달해야 합니다. 아래 예제에서`ElementAreArray()`의 두번째 argument로 전달된 `count`가 바로 배열크기를 가리킵니다. + +```cpp +using ::testing::ElementsAreArray; +... + int* const expected_vector3 = new int[count]; + ... fill expected_vector3 with values ... + EXPECT_CALL(mock, Foo(ElementsAreArray(expected_vector3, count))); +``` + +**Tips:** + + * `ElementsAre*()`은 STL iterator pattern(`const_iterator` 타입을 제공하고 `begin(), end()`를 지원하는 것 등)에 부합하는 container라면 *어떤 것*에도 적용할 수 있습니다. 이것은 사용자가 만든 container타입도 STL iterator pattern에 부합한다면 사용할 수 있음을 의미합니다. + * `ElementsAre*()`을 중첩해서 사용할 수도 있습니다 즉, 중첩된 container에 대해서도 동작합니다. + * 만약, container가 참조형식이 아니라 포인터로 전달되는 경우라고 해도 `Pointee(ElementsAre*(...))`라고 써주기만 하면 됩니다. + * `ElementAre*` 계열은 element 간의 *순서가 중요할 때* 사용해야 합니다. 따라서 `hash_map`과 같이 순서가 정해지지 않은 container에 대해서는 사용하면 안됩니다. + +#### Matcher 공유하기 #### + +gMock의 matcher는 내부적으로 ref-count 방식을 사용하는 포인터로 구현되어 있습니다. 이러한 구조를 통해서 matcher를 복사할 때는 포인터만 복사하면 되므로 매우 빠르게 동작합니다. 또한, ref-count 방식이므로 마지막으로 가리키는 포인터가 사라지면 matcher는 삭제될 것입니다. + +자, 이제 복잡한 matcher를 하나 구현했다면 이것을 복사해서 재사용하면 됩니다. 코드를 복사할 필요 없이 matcher타입 변수에 matcher를 저장하고 필요할 때마다 가져다 쓰면 됩니다. 아래 예제를 참고하세요. + +```cpp +using ::testing::AllOf; +using ::testing::Gt; +using ::testing::Le; +using ::testing::Matcher; +... + Matcher in_range = AllOf(Gt(5), Le(10)); + ... use in_range as a matcher in multiple EXPECT_CALLs ... +``` + +#### Matcher는 부수효과(side-effect)를 가지면 안됩니다. + +WARNING: gMock은 언제 얼마나 많은 matcher가 실행될지 미리 알지 못합니다. 따라서 모든 matcher는 *순수하게 기능적인 동작*만 해야합니다. 즉, 프로그램 내의 다른 부분을 수정하면 안됩니다. 더불어 결과를 만들어낼 때 프로그램 내의 다른 부분으로부터 영향을 받아서도 안됩니다. 오직 matcher로 전달되는 parameter, argument, 및 내부변수만 가지고 결과를 만들어내야 합니다. Parameter나 argument가 참조 혹은 포인터 타입인 경우에는 수정하지 않도록 주의해야 합니다. + +이 내용은 standard matcher이든 custom matcher이든 관계없이 모두 충족해야 하는 요구사항입니다. 같은 맥락에서 matcher는 내부적으로 mock function을 호출해서도 안됩니다. 왜냐하면 mock object와 gMock에 어떠한 수정도 가해서는 안됩니다. + +### Expectation 설정하기 ## + +#### ON_CALL, EXPECT_CALL 구분하고 사용하는 방법 #### + +gMock의 `ON_CALL`은 아마도 자주 사용되는 기능은 아닐 것입니다. + +알다시피 mock object의 행위를 지정하는 방법은 2가지가 있습니다. `ON_CALL`, `EXPECT_CALL`이 그것입니다. 그럼 2개의 다른점은 무엇일까요? 먼저 `ON_CALL`은 mock method가 호출되었을 때 어떤 행위를 해야하는지는 지정할 수 있지만 *expectation*은 지정할 수 없습니다. 다음으로 `EXPECT_CALL`은 행위를 지정할 수도 있고 더불어 expectation도 지정할 수 있습니다. *예를 들어서 mock method로 어떤 argument가 전달되어야 하는지, 몇 번 호출되어야 하는지, mock method간의 호출순서는 어떠한지 등입니다.* + +그럼 "`EXPECT_CALL`이 제공하는 기능이 더 많으므로 `ON_CALL`보다 좋다"라고 하면 될까요? 물론 아닙니다. 왜냐하면 `EXPECT_CALL`은 테스트 대상의 행위에 어찌됐든 제약을 계속해서 추가하는 것입니다. 여기서 우리는 제약사항이 필요한 것보다 많은 상황이 부족한 상황보다 오히려 더 나쁜 것으로 봐야한다는 것입니다. + +약간 직관에 반하는 이야기이기도 합니다. 왜 더 많이 검증하는 것이 더 적게 검증하는 것보다 나쁜 걸까요? + +정답은 테스트가 *무엇을* 검증해야 하는가에 있습니다. **좋은 테스트는 코드의 상호작용을 검증해야 합니다.** 테스트가 너무 과도한 제약사항을 가지게 되면 구현을 자유롭게 할 수 없게 됩니다. 그 결과로 인해 코드의 상호작용, 즉 인터페이스를 망가트리지 않음에도 불구하고 refactoring이나 optimization을 자유롭게 할 수 없다면 큰 문제가 되는 것입니다. 심지어는 어떤 수정사항이 테스트에 실패하긴 하지만 그 외에는 아무런 문제가 없는 경우도 발생하게 됩니다. 또 그로 인해 테스트에 실패한 이유를 디버깅하고 해결하기 위해 의미없는 시간을 뺏기게 될 것입니다. + +1개의 테스트로 많은 것을 검증하려고 하면 안됩니다. **1개 테스트로는 1개만 검증하는 것이 좋은 습관입니다.** 그렇게 해야만 bug가 발생해도 1~2개의 테스트에서만 문제가 될 것입니다. 그렇지 않고 여러개의 테스트가 한 번에 잘못되면 디버깅하기가 훨씬 어려워 집니다. 또한, 테스트의 이름을 통해서 무엇을 검증하려 하는지 자세히 표현하는 것도 좋은 습관입니다. 그렇게 해야 log만 보고도 어떤 문제인지 예측할 수 있습니다. + +이제부터는 `ON_CALL`을 먼저 사용하고, 실제로 필요할 때만 `EXPECT_CALL`을 사용하시기 바랍니다. 예를 들어 test fixture에 여러개의 `ON_CALL`을 구현할 수도 있습니다. 그렇게 되면 모든 `TEST_F()`가 동일한 설정을 공유하도록 할 수 있습니다. 이렇게 `ON_CALL`을 통해 기본적인 설정을 공유한 다음에 개별 `TEST_F()`에는 조심스럽게 `EXPECT_CALL`을 적용하기 바랍니다. 이렇게 하면 개별 `TEST_F()` 각각에 많은 `EXPEC_CALL`을 사용하는 것보다 훨씬 유연한 테스트가 될 것입니다. 또한, 테스트의 목적도 명확하게 표현할 수 있기 때문에 가독성 및 유지보수성도 향상될 것입니다. + +만약, `EXPECT_CALL`을 사용하은 mock function들이 너무 많은 "Uninteresting mock function call"을 발생시켜 문제가 된다면 `NiceMock`을 사용하기 바랍니다. 또는 문제가 되는 mock function에 대해 `EXPECT_CALL(....).Times(AnyNumber())`를 사용하는 것도 괜찮습니다. 단지 warning message를 없애기 위한 목적으로 너무 상세한 `EXPECT_CALL(....)`을 작성해서는 안 됩니다. 그렇게 하면 유지보수가 힘들어집니다. + +#### Uninteresting Call 무시하기 #### + +만약, 어떤 mock method가 호출되는것에 관심이 없다면 별다른 설정을 하지 않으면 됩니다. 그런 상황에서 해당 mock method가 호출되면 gMock은 test program을 계속 진행하기 위한 목적으로 default action을 자동적으로 수행합니다. 만약 이러한 default action이 원하는 방향과 다르다면 default action을 변경할 수도 있습니다. 이를 위해서는 `ON_CALL`을 이용해도 되고 `DefaultValue::Set()`을 overriding해도 됩니다. (뒤에서 다시 설명합니다.) + +이렇게 `ON_CALL`을 사용한 default action 설정과 대비하여 다시 한 번 기억해야 할 부분은 `EXPECT_CALL`은 사용하는 순간부터 해당 mock method에 (엄격한) expectation을 설정하는 것이며 그러한 expectation이 만족하지 않으면 test error를 발생시킨다는 것입니다. + +#### Unexpected Call 허용하지 않기 #### + +어떤 mock method가 호출되지 않기를 바라는 경우도 있을 것입니다. 이에 대해서는 아래처럼 구현할 수 있습니다. + +```cpp +using ::testing::_; +... + EXPECT_CALL(foo, Bar(_)) + .Times(0); +``` + +동일한 mock method에 대해서 어떤 방식의 호출은 허용하고, 나머지는 허용하지 않으려면 여러개의 `EXPECT_CALL`을 사용하면 됩니다. + +```cpp +using ::testing::AnyNumber; +using ::testing::Gt; +... + EXPECT_CALL(foo, Bar(5)); + EXPECT_CALL(foo, Bar(Gt(10))) + .Times(AnyNumber()); +``` + +만약 `foo.Bar()`가 호출되었는데 위의 코드에서 설정한 2개의 `EXPECT_CALL`중 어느 것도 만족하지 않는다면 해당 테스트는 실패하게 됩니다. + +#### Uninteresting vs Unexpected 를 구분하자 #### + +gMock에서 _uninteresting_ 호출과 _unexpected_ 호출은 서로 다른 개념입니다. 매우 다릅니다. + +먼저, `x.Y(...)`이 **uninteresting** 이라는 의미는 `EXPECT_CALL(x, Y(....))`이 하나도 없음을 뜻합니다. 쉽게 말해서 테스트에서 해당 mock function이 어떻게 되든 신경쓰지 않겠다는 의미입니다. + +다음으로 `x.Y(...)`이 **unexpected** 라는 의미는 해당 함수에 대해 `EXPECT_CALL(x, Y(...))`을 사용하고 있지만, 실제로 `x.Y(...)`가 호출되었을 때, expectation을 만족하는 `EXPECT_CALL(x. Y(...))`을 찾지 못했음을 의미합니다. 즉, 해당 mock function이 기대한 대로 사용되지 않았기 때문에 해당 테스트의 결과는 실패가 됩니다. + +**unexpected call이 발생한 테스트는 항상 실패로 판정됩니다.** 왜냐하면 이것은 곧 mock function에 대한 expectation과 실제 코드의 동작이 다름을 의미하기 때문입니다. + +반대로 (기본모드의) **uninteresting call은 테스트 실패를 의미하지 않습니다.** 왜냐하면 테스트에서 해당 mock function의 동작을 정의한 적이 없기 때문입니다. gMock은 아무런 expectation도 설정되지 않은 mock function은 어떻게 수행되더라도 괜찮다고 판단합니다. 대신 언젠가 문제가 *될 수도 있음을* 알리기 위해 warning을 출력해 줍니다. (예를 들어 테스트를 구현하다가`EXPECT_CALL`을 실수로 빠트렸을 수도 있습니다.) + +gMock에서 `NiceMock`과 `StrictMock`은 mock class를 "nice" 또는 "strict"로 만들어 줍니다. 이것이 uninteresting call 과 unexpected call 에 어떤 영향을 미칠까요? + +**Nice mock**은 uninteresting call warning을 없애 줍니다. 기본모드에 비해 출력물이 변하긴 하지만 테스트 결과 측면에서 달라지는 점은 없습니다. 즉, 기본모드에서 실패라면 nice mock에서도 실패인 것입니다. + +**Strict mock**은 uninteresting call warning을 error로 간주합니다. 따라서 기본모드에서 성공하는 테스트라도 실패할 수 있게 됩니다. 즉, 테스트 결과가 달라질 수 있습니다. + +아래 예제를 보겠습니다. + +```cpp +TEST(...) { + NiceMock mock_registry; + EXPECT_CALL(mock_registry, GetDomainOwner("google.com")) + .WillRepeatedly(Return("Larry Page")); + + // Use mock_registry in code under test. + ... &mock_registry ... +} +``` + +위의 `EXPECT_CALL`은 `GetDomainOwner()`의 argument로 `"google.com"`을 전달받기를 기대합니다. 따라서 `GetDomainOnwer("yahoo.com")`라는 내용으로 호출되면 이것은 unexpected call이고 테스트는 실패합니다. 즉, `NiceMock`을 사용했다고 하더라도 unexpected call로 인한 실패는 동일하게 실패라는 것입니다. + +그러면 `GetDomainOnwer()`가 어떤 argument를 전달받더라도 테스트가 실패하지 않게 하려면 어떻게 하면 될까요? 이런 경우에는 "catch all" 목적의 `EXPECT_CALL` 을 추가하는 것이 일반적입니다. `_` 와 `AnyNumber()`를 사용해서 구현합니다. + +```cpp + EXPECT_CALL(mock_registry, GetDomainOwner(_)) + .Times(AnyNumber()); // catches all other calls to this method. + EXPECT_CALL(mock_registry, GetDomainOwner("google.com")) + .WillRepeatedly(Return("Larry Page")); +``` + +`_`는 argument로 어떤 값이 전달되더라도 성공하는 wildcard matcher라는 것은 이미 배운 내용입니다. 이와 함께 두번째 `EXPECT_CALL`에는 `GetDomainOwner("google.com")`도 지정하고 있습니다. 이것이 의미하는 바는 argument에 따라서 `GetDomainOnwer()` 함수의 행위를 다르게 하라는 것입니다. + +한가지 주의할 점은 이렇게 동일한 mock function에 대해 여러개의 `EXPECT_CALL`을 사용하면 소스코드 상에서 나중에 오는 것과 먼저 비교한다는 것입니다. 즉, 위의 코드에서는 `GetDomainOwner()`에 대한 호출이 발생하면 `GetDomainOwner("google.com")`와 먼저 비교해보고 이것을 만족하지 않으면 다음으로 `GetDomainOwner(_)`와의 비교를 수행하게 됩니다. + +Uninteresting call, nice mock, strict mock 에 대한 더 자세한 내용은 ["The Nice, the Strict, and the Naggy"](#the-nice-the-strict-and-the-naggy)를 참조하세요. + +#### 함수의 호출순서 지정하기 #### + +하나의 mock function에 대해 여러개의 `EXPECT_CALL`를 사용했을 때, `EXPECT_CALL`을 비교하는 순서가 있다고 바로 위에서 얘기했습니다. 그러나 이렇게 만족하는 `EXPECT_CALL`을 찾는 과정에서 순서가 있다고 해서 해당 mock function이 특정한 호출순서가 가진다고 말할 수는 없습니다. 예를 들어 어떤 mock function에 2개의 `EXPECT_CALL`을 설정했다면 기대를 만족하는 `EXPECT_CALL`은 첫번째 일수도 있고, 두번째 일수도 있습니다. 단지 두번째 것을 먼저 비교해보는 것 뿐이죠. 다시 말하면 비교순서는 정해져 있지만 호출순서는 아직 지정하지 않은 것입니다. + +그럼 이러한 호출순서를 명확히 지정하고 싶다면 어떻게 해야할까요? 이를 위해서는 아래코드와 같이 해당하는 `EXPECT_CALL()`들을 모아서 새로운 block에 넣고 상단에 `InSequence`라는 타입의 변수를 선언하면 됩니다. + +```cpp +using ::testing::_; +using ::testing::InSequence; + + { + InSequence s; + + EXPECT_CALL(foo, DoThis(5)); + EXPECT_CALL(bar, DoThat(_)) + .Times(2); + EXPECT_CALL(foo, DoThis(6)); + } +``` + +위 코드는 `foo.DoThis(5)`가 제일 먼저 호출되고, 그 다음에 `foo.DoThat()`, `foo.DoThis()`가 호출되기를 기대하는 코드입니다. 실제로 `foo.DoThis()`의 argument로 `6`이 전달되면 `DoThis(5)`를 만족하지 않기 때문에 테스트가 실패합니다. 즉, 현재 호출순서에 있는 `EXPECT_CALL`이 기대를 만족하지 않는다고 해서 기대를 만족하는 다른 `EXPECT_CALL`이 있는지 찾거나 하지 않습니다. 여기서 한가지 유의할 점은 기존에는 소스코드상에서 하단에 있는 `EXPECT_CALL`을 먼저 비교했지만, `InSequence`를 사용하는 상황에서는 위에서부터 순서대로 비교한다는 점입니다. + +#### 함수의 호출순서를 부분부분 지정하기 #### + +`InSequence`는 function들의 호출순서를 일렬로 지정합니다. 이에 더해서 function에 부분적인 순서를 지정하는 것도 가능합니다. 예를 들어 `A`가 `B`와`C`보다 먼저 호출되기를 바라는 것과 동시에 `B`와 `C`간에는 호출순서를 지정하고 싶지 않을 수도 있습니다. 이러한 상황에서 `InSequence`를 사용하면 원하는 것에 비해 많은 제약을 지정하기 때문에 적합하지 않습니다. + +gMock은 부분적인 호출순서를 지원하기 위해서 DAG(directed acyclic graph)를 적용했습니다. 이를 사용하기 위한 몇 가지 방법이 있는데 먼저 `EXPECT_CALL`에 [After](CheatSheet.md#the-after-clause)를 붙여서 사용하는 것이 한가지 방법입니다. + +또 다른 방법은 `InSequence()`를 사용하는 것입니다. (위에서 설명한 `InSequence` class가 아닙니다.) 이 개념은 jMock 2에서 가져왔습니다. `After()`보다는 유연성이 떨어지긴 하지만 길고 연속된 함수호출을 지정할 때 편리합니다. 왜냐하면 하나의 호출흐름 안에 있는 `EXPECT_CALL`들은 별도의 이름을 가질 필요가 없기 때문입니다. 아래에 계속해서 `InSequence()`의 동작방식을 설명하겠습니다. + +`EXPECT_CALL()`을 graph 자료구조의 node라고 가정해봅시다. 그럼 node A 에서 node B로 가는 edge를 추가함으로 DAG가 하나 만들어집니다. 이 때, 해당 DAG의 의미는 A가 B보다 먼저 호출되어야 함을 뜻합니다. 이렇게 DAG에서 직접적으로 연결되는 edge를 "sequence"라고 부릅니다. 여기서 하나의 sequence에는 또 다시 내부적으로 DAG를 구성할 수 있습니다. 이렇게 내부적으로 구성된 DAG는 자신이 가지고 있는 `EXPECT_CALL()`들의 호출순서 정보와 함께 바깥의 DAG를 만드는데 영향을 주는 `EXPECT_CALL()`도 알고 있어야 합니다. + +이러한 부분적인 호출순서를 실제로 구현하려면 2가지만 알면 됩니다. 먼저 DAG의 edge(sequence)를 의미하는 `Sequence` object를 정의합니다. 다음으로 DAG의 node(`EXPECT_CALL`)가 어느 `Sequence`에 속하는지 알려주는 것입니다. + +여기서 같은 `Sequence`에 속하는 `EXPECT_CALL`들은 소스코드상에서 위-아래로, 즉 작성된 순서대로 호출되어야 합니다. + +```cpp +using ::testing::Sequence; +... + Sequence s1, s2; + + EXPECT_CALL(foo, A()) + .InSequence(s1, s2); + EXPECT_CALL(bar, B()) + .InSequence(s1); + EXPECT_CALL(bar, C()) + .InSequence(s2); + EXPECT_CALL(foo, D()) + .InSequence(s2); +``` + +위의 코드에 대해 DAG를 그려보면 아래와 같습니다. (`s1` 은 `A -> B` / `s2`는 `A -> C -> D`) + +```bash + +---> B + | + A ---| + | + +---> C ---> D +``` + +위 DAG는 A가 B,C보다 먼저 호출되어야 함을 의미하고, C가 D보다 먼저 호출되어야 함을 의미합니다. 그 외의 제약은 없습니다. + +#### Expectation의 active/inactive 설정하기 #### + +어떤 mock method가 호출되어 만족하는 expectation(`EXPECT_CALL`)을 찾을 때, gMock은 active 상태인 expectation에 대해서만 이러한 작업을 수행합니다. Expectation들이 처음 생성되어 호출되기 전에는 기본적으로 active상태라고 보면 됩니다. 그러다가 자신보다 나중에 호출되어야 할 함수가 호출되는 시점에 inactive 상태가 됩니다. (이렇게 inactive 상태가 되는 것을 _retires_라고도 부릅니다.) + +```cpp +using ::testing::_; +using ::testing::Sequence; +... + Sequence s1, s2; + + EXPECT_CALL(log, Log(WARNING, _, "File too large.")) // #1 + .Times(AnyNumber()) + .InSequence(s1, s2); + EXPECT_CALL(log, Log(WARNING, _, "Data set is empty.")) // #2 + .InSequence(s1); + EXPECT_CALL(log, Log(WARNING, _, "User not found.")) // #3 + .InSequence(s2); +``` + +위의 코드는 `log.Log()`에 대해 #1, #2, #3 순서로 호출순서를 지정하고 있습니다. 따라서 `log.Log()`의 expectation #1번은 얼마든지 호출될 수 있지만, #2번 또는 #3번이 호출되는 순간 retire 됩니다. 왜냐하면 자신보다 나중에 호출되어야 할 expectation이 매칭되었기 때문입니다. 따라서 그 이후에 `log.Log()`에 "File too large."`라는 argument가 전달되면 테스트는 실패하게 될 것입니다. + +아래와 같은 방법으로 expectation을 구현하면 자동으로 retire되지 않는다는 점도 기억하기 바랍니다. + +```cpp +using ::testing::_; +... + EXPECT_CALL(log, Log(WARNING, _, _)); // #1 + EXPECT_CALL(log, Log(WARNING, _, "File too large.")); // #2 +``` + +위의 코드는 `"File too large."`라는 argument가 한 번만 전달되기를 바라면서 작성한 테스트코드입니다. `log.Log(WARNING, _, "File too large.")`라고 한 번 호출되었다면 문제가 없습니다. 그러나 두번째도 동일한 argument가 전달되면 테스트는 실패합니다. 왜냐하면 두번째 호출도 #2번 expectation을 사용하게 되는데 #2번 expectation은 1회만 호출되기를 기대하는 코드이기 때문입니다. (여기서 cardinality가 생략되어 있으므로 이는 곧 `Times(1)`을 의미합니다.) + +이러한 문제를 피하기 위해서는 expectation을 직접 retire시켜야 합니다. 즉, 원하는 횟수만큼은 충족(saturation)되었으니 retire시키라는 내용을 구현하면 됩니다. + +```cpp +using ::testing::_; +... + EXPECT_CALL(log, Log(WARNING, _, _)); // #1 + EXPECT_CALL(log, Log(WARNING, _, "File too large.")) // #2 + .RetiresOnSaturation(); +``` + +`RetiresOnSaturation()`을 사용해서 #2번 expectation이 1회 호출된 이후에는 retire되도록 구현했습니다. 이제 `Log(WARNING, _, "File too large.")`가 2회 호출되면 #2번 expectation을 건너뛰고 #1번 expectation으로 넘어갑니다. 왜냐하면 #2번은 retire되어 inactive상태로 변경되었고 gMock의 고려대상에서 제외되었기 때문입니다. + +### Action 사용하기 ## + +#### Mock Method에서 참조타입 반환하기 #### + +만약 mock function이 참조타입을 반환해야 한다면 `Return()` 대신 `ReturnRef()` 를 써야 합니다. + +```cpp +using ::testing::ReturnRef; + +class MockFoo : public Foo { + public: + MOCK_METHOD(Bar&, GetBar, (), (override)); +}; +... + MockFoo foo; + Bar bar; + EXPECT_CALL(foo, GetBar()) + .WillOnce(ReturnRef(bar)); +... +``` + +#### Mock Method에서 Live Value 반환하기 #### + +`Return(x)`는 해당 소스코드가 수행될 때 `x`의 복사본을 만들어서 미리 저장해둡니다. 따라서 `x`가 변수라고 해도 반환값은 고정됩니다. 즉, 미리 복사해놓은 값만 계속 반환하게 됩니다. 그러나 사용자는 이러한 고정값 대신 `x` 의 _live_ value를 원할때가 있습니다. 여기서 live value란 `x` 변수에 현재 저장되어 있는 값을 사용한다는 의미입니다. 즉 action 생성시점에 그 반환값을 결정하지 말고 실제로 mock method가 호출되는 시점에 `x`에 저장되어 있는 값을 반환받고 싶은 경우입니다. + +Mock function이 참조타입을 반환하게 하려면 `ReturnRef(x)`를 사용하면 됩니다. 그러나 `ReturnRef(x)`는 mock function의 return type이 실제로 참조일 때만 사용할 수 있습니다. 그럼 값을 반환하는 mock function가 live value를 반환하도록 하려면 어떻게 해야 할까요? + +`ByRef()`를 사용하면 될까요? + +```cpp +using testing::ByRef; +using testing::Return; + +class MockFoo : public Foo { + public: + MOCK_METHOD(int, GetValue, (), (override)); +}; +... + int x = 0; + MockFoo foo; + EXPECT_CALL(foo, GetValue()) + .WillRepeatedly(Return(ByRef(x))); // Wrong! + x = 42; + EXPECT_EQ(42, foo.GetValue()); +``` + +아쉽지만, 위의 코드는 원하는대로 동작하지 않습니다. 아래의 failure message와 함께 테스트는 실패할 것입니다. + +```bash +Value of: foo.GetValue() + Actual: 0 +Expected: 42 +``` + +그 이유는 `foo.GetValue()`의 반환값이 (mock method가 실제로 호출되는 시점의 `value`가 아니라) `Return(value)`라는 action이 수행되는 시점의 `value`로 정해지기 때문입니다. 이러한 결정은 `value`가 어떤 임시객체의 참조이거나 하는 위험한 상황을 대비하기 위한 것입니다. 결과적으로 `ByRef(x)`는 `const int&`가 아니라 `int`타입의 값으로 고정되어 버립니다. 그렇기 때문에 `Return(ByRef(x))`는 항상 `0`을 반환하고 위의 테스트가 실패하는 것입니다. + +위의 상황에서는 `ReturnPointee(pointer)`를 사용해야 합니다. 이제 `foo.GetValue()`는 자신이 실제로 호출되는 시점에 `pointer`에 저장된 값을 반환할 것입니다. 여기서 `pointer`에 저장된 값은 action생성시점에 고정되는 것이 아니고 그 값이 변화할 수 있으므로 live value라고 부릅니다. + +```cpp +using testing::ReturnPointee; +... + int x = 0; + MockFoo foo; + EXPECT_CALL(foo, GetValue()) + .WillRepeatedly(ReturnPointee(&x)); // Note the & here. + x = 42; + EXPECT_EQ(42, foo.GetValue()); // This will succeed now. +``` + +#### Action 조합하기 #### + +Mock function이 호출될 때, 1개 이상의 action을 수행하려면 어떻게 해야 할까요? `DoAll()`을 사용하면 여러개의 action을 모두 수행할 수 있습니다. 또한, 해당 mock function의 반환값으로는 마지막에 오는 action의 반환값을 사용한다는 점도 기억하기 바랍니다. + +```cpp +using ::testing::_; +using ::testing::DoAll; + +class MockFoo : public Foo { + public: + MOCK_METHOD(bool, Bar, (int n), (override)); +}; +... + EXPECT_CALL(foo, Bar(_)) + .WillOnce(DoAll(action_1, + action_2, + ... + action_n)); +``` + +#### 복잡한 argument 검증하기 + +Argument가 여러개이고 각각의 기대사항이 복잡한 mock method가 있다고 가정해 보겠습니다. 이런 경우에 각각의 argument의 기대사항이 다르다면(cardinality가 서로 다른경우 등) expectation을 지정하기가 쉽지 않습니다. 또한, 테스트가 실패했을때도 어느 것이 잘못된 것인지 구분하기가 어려울 수 있습니다. + +```c++ + // Not ideal: this could fail because of a problem with arg1 or arg2, or maybe + // just the method wasn't called. + EXPECT_CALL(foo, SendValues(_, ElementsAre(1, 4, 4, 7), EqualsProto( ... ))); +``` + +위와 같은 코드는 테스트가 실패했을 때, 어떤 부분에서 문제가 발생했는지 파악하기가 어렵습니다. 이럴 때에는 각각의 argument를 저장한 다음에 따로 검증하는 방법을 사용하면 좋습니다. + +```c++ + EXPECT_CALL(foo, SendValues) + .WillOnce(DoAll(SaveArg<1>(&actual_array), SaveArg<2>(&actual_proto))); + ... run the test + EXPECT_THAT(actual_array, ElementsAre(1, 4, 4, 7)); + EXPECT_THAT(actual_proto, EqualsProto( ... )); +``` + +#### Mocking에서 Side Effects(부수효과) 사용하기 #### + +Method가 반환값만으로 프로그램에 영향을 주는 것은 아닙니다. 예를 들어, method에서 global variable을 수정하면 반환값을 통하지 않더라도 프로그램의 상태를 변경할 수 있으며 문법적으로도 문제가 없습니다. 이렇게 반환값 외의 동작으로 프로그램에 영향을 주는 행위를 side-effect라고 합니다. side-effect라고 해서 꼭 문제되는 상황을 의미하는 것이 아님을 기억하기 바랍니다. 이러한 side-effect를 mocking에 사용하기 위한 가장 기본적인 방법은 `::testing::ActionInterface`를 사용자가 직접 정의하는 것입니다. 더불어 gMock은 side-effect와 관련된 기본적인 기능들도 역시 제공하고 있습니다. + +먼저, 포인터형식으로 전달된 argument의 값을 바꾸고 싶다면 built-in action인 `SetArgPointee()`를 사용하면 됩니다. + +```cpp +using ::testing::_; +using ::testing::SetArgPointee; + +class MockMutator : public Mutator { + public: + MOCK_METHOD(void, Mutate, (bool mutate, int* value), (override)); + ... +} +... + MockMutator mutator; + EXPECT_CALL(mutator, Mutate(true, _)) + .WillOnce(SetArgPointee<1>(5)); +``` + +위의 예제에서 `mutator.Mutate()`가 호출되면, 두번째 argument인 `value`라는 포인터가 가리키는 값이 `5`로 변경될 것입니다. `SetArgPointee<1>(5)`에서 `<1>`은 두번째 argument인 `value`를 의미합니다. (`<0>`부터 시작이기 때문에 `<1>`이 두번째 argument입니다.) + +`SetArgPointee()`는 전달해야 할 값의 복사본을 내부적으로 미리 만들어 둡니다. 이를 통해서 값이나 영역이 변경됨으로 인한 문제를 예방할 수 있습니다. 다만, 이를 위해서는 해당 타입이 copy constructor와 assgignment operator를 지원해야 합니다. + +Mock method에 `SetArgPointee()`를 사용함과 동시에 반환값도 지정하고 싶다면 `DoAll()`을 사용해서 `SetArgPointee()`와 `Return()`을 묶어 주기만 하면 됩니다. 예제코드는 아래와 같습니다. + +```cpp +using ::testing::_; +using ::testing::Return; +using ::testing::SetArgPointee; + +class MockMutator : public Mutator { + public: + ... + MOCK_METHOD(bool, MutateInt, (int* value), (override)); +} +... + MockMutator mutator; + EXPECT_CALL(mutator, MutateInt(_)) + .WillOnce(DoAll(SetArgPointee<0>(5), + Return(true))); +``` + +여기서 `ReturnOKWith()`를 사용하면 `SetArgPointee()`가 제공하는 값을 override하는 것도 가능합니다. + +Argument가 배열형식인 경우에도 변경할 수 있습니다. 이 때는 `SetArrayArgument(first, last)`를 사용하면 됩니다. 이 action은 배열의 [first, last) 구간에 있는 값들을 전달된 N번째(0부터 시작) argument에 복사합니다. + +```cpp +using ::testing::NotNull; +using ::testing::SetArrayArgument; + +class MockArrayMutator : public ArrayMutator { + public: + MOCK_METHOD(void, Mutate, (int* values, int num_values), (override)); + ... +} +... + MockArrayMutator mutator; + int values[5] = {1, 2, 3, 4, 5}; + EXPECT_CALL(mutator, Mutate(NotNull(), 5)) + .WillOnce(SetArrayArgument<0>(values, values + 5)); +``` + +`SetArrayArgument(first, last)`는 iterator에도 문제없이 잘 동작합니다. + +```cpp +using ::testing::_; +using ::testing::SetArrayArgument; + +class MockRolodex : public Rolodex { + public: + MOCK_METHOD(void, GetNames, (std::back_insert_iterator>), + (override)); + ... +} +... + MockRolodex rolodex; + vector names; + names.push_back("George"); + names.push_back("John"); + names.push_back("Thomas"); + EXPECT_CALL(rolodex, GetNames(_)) + .WillOnce(SetArrayArgument<0>(names.begin(), names.end())); +``` + +#### Mock Object 의 동작을 프로그램에 상태에 따라 변화시키기 #### + +Mock object의 동작이 프로그램의 상태에 따라 달라지기를 원한다면, `::testing::InSequence`를 사용하면 됩니다. + +```cpp +using ::testing::InSequence; +using ::testing::Return; + +... + { + InSequence seq; + EXPECT_CALL(my_mock, IsDirty()) + .WillRepeatedly(Return(true)); + EXPECT_CALL(my_mock, Flush()); + EXPECT_CALL(my_mock, IsDirty()) + .WillRepeatedly(Return(false)); + } + my_mock.FlushIfDirty(); +``` + +위 코드에서 `my_mock.IsDirty()`는 몇 번 호출되는지에 관계없이(`WillRepeatedly` 사용) `true`를 반환합니다. 하지만 그러다가 `my_mock.Flush()`가 호출되면 다음 expectation으로 넘어가게 되고 이제는 `false`를 반복해서 반환할 것입니다. 즉, `Flush()`의 호출여부에 따라 동작이 달라집니다. + +좀 더 복잡한 구조를 가지는 프로그램에서는 변수를 사용해서 mock method로 전달되는 값을 저장하고 다시 그것을 반환하도록 하는 방법도 유용할 것입니다. + +```cpp +using ::testing::_; +using ::testing::SaveArg; +using ::testing::Return; + +ACTION_P(ReturnPointee, p) { return *p; } +... + int previous_value = 0; + EXPECT_CALL(my_mock, GetPrevValue) + .WillRepeatedly(ReturnPointee(&previous_value)); + EXPECT_CALL(my_mock, UpdateValue) + .WillRepeatedly(SaveArg<0>(&previous_value)); + my_mock.DoSomethingToUpdateValue(); +``` + +위의 코드에서 `my_mock.GetPrevValue()`는 `previous_value`의 최신값을 항상 반환합니다. 또한, 이러한 `previous_value`는 `UpdateValue()`에 의해서 변경됩니다. + +#### Return Type의 Default Value 변경하기 #### + +사용자가 어떤 mock method의 반환값을 별도로 지정하지 않은 경우에는 해당타입에 대한 C++ built-in default value가 반환됩니다. 물론, 이러한 값은 대부분의 경우에 `0`일 것입니다. 이와 더불어 C++ 11 이상부터는 mock method의 return type이 default constructor를 가지고 있는 경우, `0`이 아니라 default constructor를 통해 생성된 값을 반환합니다. 물론, default value가 적절하지 않은 상황이라면 사용자가 자유롭게 `Return()`과 같은 action을 통해 다른 동작을 지정하면 됩니다. + +여기서는 이러한 default value 자체를 바꾸는 방법을 공유합니다. (gMock이 특정타입의 default value를 판단할 수 없는 경우 이것을 직접 명세해야할 경우가 발생할 수도 있습니다) 이를 위해서는 `::testing ::DefaultValue`라는 template class를 사용하면 됩니다. + +```cpp +using ::testing::DefaultValue; + +class MockFoo : public Foo { + public: + MOCK_METHOD(Bar, CalculateBar, (), (override)); +}; + + +... + Bar default_bar; + // Sets the default return value for type Bar. + DefaultValue::Set(default_bar); + + MockFoo foo; + + // We don't need to specify an action here, as the default + // return value works for us. + EXPECT_CALL(foo, CalculateBar()); + + foo.CalculateBar(); // This should return default_bar. + + // Unsets the default return value. + DefaultValue::Clear(); +``` + +다만, 위와 같이 default value를 직접 변경하는 것은 test program의 가독성을 저하시키기 때문에 신중하게 고민해서 사용하기 바랍니다. 또는 `Set()`을 사용한 후에 `Clear()`를 같이 사용함으로써 default value를 변경했다가 다시 기존 default value로 바꾸는 것도 좋은 방법입니다. + +#### Default Action을 상황에 따라 변화시키기 #### + +위에서 특정타입에 대한 default value를 변경하는 방법에 대해 배웠습니다. 그러나 이렇게 반환값을 변경하는 것만으로는 부족할 수도 있습니다. 예를 들어 동일한 return type을 사용하는 2개의 mock method가 있을때 각각에 대해 다른 동작을 지정하고 싶을 수 있습니다. 이런 경우에는 `ON_CALL()`을 사용하면 mock method마다 다른 행위를 지정할 수 있습니다. + +```cpp +using ::testing::_; +using ::testing::AnyNumber; +using ::testing::Gt; +using ::testing::Return; +... + ON_CALL(foo, Sign(_)) + .WillByDefault(Return(-1)); + ON_CALL(foo, Sign(0)) + .WillByDefault(Return(0)); + ON_CALL(foo, Sign(Gt(0))) + .WillByDefault(Return(1)); + + EXPECT_CALL(foo, Sign(_)) + .Times(AnyNumber()); + + foo.Sign(5); // This should return 1. + foo.Sign(-9); // This should return -1. + foo.Sign(0); // This should return 0. +``` + +동일한 mock method에 대해서 `ON_CALL()`을 여러개 사용하면 코드상에서 나중에 오는 것부터 먼저 비교하게 됩니다. 즉, 밑에서 위 방향으로 하나씩 비교하면서 expectation을 만족하는 `ON_CALL`이 있는지 찾게 됩니다. 이러한 비교순서는 mock object의 constructor나 test fixture에서 적용한 expectation의 우선순위가 개별 `TEST()`에서 지정한 expectation의 우선순위보다 낮아지도록 만듭니다. 쉽게 말하면 동일한 이름의 지역변수와 전역변수가 있을 때, 지역변수가 먼저 선택되는 것과 같습니다. + +#### Functions/Functors/Lambda를 Action으로 사용하기 #### + +gMock에서 제공되는 built-in action만으로는 부족한 경우도 있을 것입니다. 이런 경우에는 기존에 사용하던 function, functor, `std::function`, lambda 등을 action처럼 사용하는 것도 가능합니다. 아래 예제를 참조하세요. + +```cpp +using ::testing::_; +using ::testing::Invoke; + +class MockFoo : public Foo { + public: + MOCK_METHOD(int, Sum, (int x, int y), (override)); + MOCK_METHOD(bool, ComplexJob, (int x), (override)); }; + +int CalculateSum(int x, int y) { return x + y; } +int Sum3(int x, int y, int z) { return x + y + z; } + +class Helper { + public: + bool ComplexJob(int x); +}; + +... + MockFoo foo; + Helper helper; + EXPECT_CALL(foo, Sum(_, )) + .WillOnce(&CalculateSum) + .WillRepeatedly(Invoke(NewPermanentCallback(Sum3, 1))); + EXPECT_CALL(foo, ComplexJob()) + .WillOnce(Invoke(&helper, &Helper::ComplexJob)); + .WillRepeatedly([](int x) { return x > 0; }); + + foo.Sum(5, 6); // Invokes CalculateSum(5, 6). + foo.Sum(2, 3); // Invokes Sum3(1, 2, 3). + foo.ComplexJob(10); // Invokes helper.ComplexJob(10). + foo.ComplexJob(-1); // Invokes the inline lambda. +``` + +한가지 유의할 점은 fucntion type을 어느정도 지켜줘야 한다는 것입니다. Mock function의 signature와 완벽히 동일하지는 않더라도 _compatible_해야 합니다. 즉, 양쪽의 argument와 return type이 암시적으로 형변환 가능해야 합니다. 이렇듯 타입을 엄격하게 *검사하지는 않기 때문에* 좀 더 유연하게 이 기능을 사용할 수 있을 것입니다. 물론, 사용자 스스로 안전하다고 판단한 경우에만 사용해야 함은 당연합니다. + +**Note: lambda를 action으로 사용할 때** + +* Callback의 소유권은 action에 있으며 따라서 action이 삭제될 때, callback도 삭제됩니다. +* 만약 사용하려는 callback이 `C`라는 base callback type을 상속받았다면 overloading 문제를 해결하기 위해 base callback type으로 casting해줘야 합니다. 아래 예제코드를 확인하세요. + +```c++ +using ::testing::Invoke; +... + ResultCallback* is_ok = ...; + ... + Invoke(is_ok) ...; // This works. + + BlockingClosure* done = new BlockingClosure; + ... + Invoke(implicit_cast(done)) ...; // The cast is necessary. +``` +#### Argument개수가 더 많은 Function/Functor/Lambda를 Action으로 사용하기 + +현재까지는 mock function에 `Invoke()`를 사용하기 위해서 argument의 개수가 같은 function, functor, lambda만을 사용했습니다. 만약 function, functor, labmda 등의 argument가 mock function보다 많으면 어떻게 해야할까요? 이런 경우에는 pre-bound argument를 위한 callback을 사용합니다. 아래 예제를 확인하세요. + +```c++ +using ::testing::Invoke; + +class MockFoo : public Foo { + public: + MOCK_METHOD(char, DoThis, (int n), (override)); +}; + +char SignOfSum(int x, int y) { + const int sum = x + y; + return (sum > 0) ? '+' : (sum < 0) ? '-' : '0'; +} + +TEST_F(FooTest, Test) { + MockFoo foo; + + EXPECT_CALL(foo, DoThis(2)) + .WillOnce(Invoke(NewPermanentCallback(SignOfSum, 5))); + EXPECT_EQ('+', foo.DoThis(2)); // Invokes SignOfSum(5, 2). +} +``` + +#### Agument 없는 Function/Functor/Lambda를 Action으로 사용하기 #### + +`Invoke()`는 복잡한 action을 구현해야 할 때 매우 유용합니다. 특히, `Invoke()`는 mock function으로 전달된 argument들을 연결된 function이나 functor로 그래도 전달해주기 때문에 mock function과 동일한 context 위에서 원하는 동작을 구현할 수 있게 해줍니다. + +그럼 function, functor와 mock function의 signature가 서로 다르고 compatible하지 않다면 어떻게 해야될까요? 현재까지 배운내용을 사용해서 구현하려면 mock function과 signature가 compatible한 wrapper function을 먼저 만들고 그것을 `Invoke()`를 통해 호출합니다. 그 다음에 wrapper function 내부에서 function, functor를 호출하도록 구현합니다. 네, 당연히 반복적이고 귀찮은 일입니다. + +gMock은 이러한 반복적인 작업을 대신 해줍니다. 이 때에는 `InvokeWithoutArgs()`를 사용하면 됩니다. 기본적인 동작은 `Invoke()`와 동일하지만 mock function의 argument를 전달하지 않는다는 부분만 다릅니다. 아래에 예제가 있습니다. + +```cpp +using ::testing::_; +using ::testing::InvokeWithoutArgs; + +class MockFoo : public Foo { + public: + MOCK_METHOD(bool, ComplexJob, (int n), (override)); +}; + +bool Job1() { ... } +bool Job2(int n, char c) { ... } + +... + MockFoo foo; + EXPECT_CALL(foo, ComplexJob(_)) + .WillOnce(InvokeWithoutArgs(Job1)) + .WillOnce(InvokeWithoutArgs(NewPermanentCallback(Job2, 5, 'a'))); + + foo.ComplexJob(10); // Invokes Job1(). + foo.ComplexJob(20); // Invokes Job2(5, 'a'). +``` + +#### Mock Function에 전달된 Callable Argument를 호출하기 #### + +Mock function의 argument로 fuction pointer, functor가 전달되는 경우도 있을 것입니다. 즉, "callable" 타입이 argument로 전달되는 경우입니다. 아래에 예제가 있습니다. + +```cpp +class MockFoo : public Foo { + public: + MOCK_METHOD(bool, DoThis, (int n, (ResultCallback1* callback)), + (override)); +}; +``` + +위와 같이 전달된 callable argument를 호출하고 싶다면 어떻게 하면 될까요? 아래코드의 `WillOnce(...)`에 사용할 action은 직접 구현해야 할까요? + +```cpp +using ::testing::_; +... + MockFoo foo; + EXPECT_CALL(foo, DoThis(_, _)) + .WillOnce(...); + // Will execute callback->Run(5), where callback is the + // second argument DoThis() receives. +``` + +NOTE: C++에 lambda가 적용되기 전에 작성된 legacy documentation임을 밝힙니다. + +(Lambda가 없다고 가정해도) callable argument를 호출하기 위한 action을 직접 구현할 필요는 없습니다. gMock은 이를 위한 action도 이미 제공하고 있습니다. + +```cpp +InvokeArgument(arg_1, arg_2, ..., arg_m) +``` + +`InvokeArgument(arg1, arg2, ..., arg_m)`이라는 action은 mock function의 N번째 argument로 전달된 callable을 호출해줍니다. 게다가 callable에 argument (`arg_1`, `arg_2`, ..., `arg_m`)를 전달하는 것도 가능합니다. + +아래는 관련내용이 추가된 코드입니다. + +```cpp +using ::testing::_; +using ::testing::InvokeArgument; +... + EXPECT_CALL(foo, DoThis(_, _)) + .WillOnce(InvokeArgument<1>(5)); + // Will execute callback->Run(5), where callback is the + // second argument DoThis() receives. +``` + +만약, callable에 참조형식 argument를 전달하고 싶다면 어떻게 해야할까요? 여기서도 `ByRef()`를 사용하면 됩니다. + +```cpp + ... + MOCK_METHOD(bool, Bar, + ((ResultCallback2* callback)), + (override)); + ... + using ::testing::_; + using ::testing::ByRef; + using ::testing::InvokeArgument; + ... + MockFoo foo; + Helper helper; + ... + EXPECT_CALL(foo, Bar(_)) + .WillOnce(InvokeArgument<0>(5, ByRef(helper))); + // ByRef(helper) guarantees that a reference to helper, not a copy of it, + // will be passed to the callback. +``` + +Callable에 참조형식 argument를 전달할 때, `ByRef()`를 사용하지 않으면 어떻게 될까요? 그런 경우에는 `InvokeArgument()`가 해당 argument의 *복사본을 만들고 다시 그 복사본의 참조를 전달하게 됩니다* 이러한 동작방식은 argument가 temporary value일 때 상당히 유용하며 gMock이 temporary value의 복사본을 만들어서 관리하고 있기 때문에 안전성도 보장됩니다. 아래코드는 이러한 temporary value가 사용된 예제입니다. + +```cpp + ... + MOCK_METHOD(bool, DoThat, (bool (*f)(const double& x, const string& s)), + (override)); + ... + using ::testing::_; + using ::testing::InvokeArgument; + ... + MockFoo foo; + ... + EXPECT_CALL(foo, DoThat(_)) + .WillOnce(InvokeArgument<0>(5.0, string("Hi"))); + // Will execute (*f)(5.0, string("Hi")), where f is the function pointer + // DoThat() receives. Note that the values 5.0 and string("Hi") are + // temporary and dead once the EXPECT_CALL() statement finishes. Yet + // it's fine to perform this action later, since a copy of the values + // are kept inside the InvokeArgument action. +``` + +#### Action's Result 무시하기 #### + +모두 그런것은 아니지만, `Return`, `Invoke`와 같은 action은 무언가를 반환할 수 있습니다. 그러나 이러한 기능이 불편한 경우가 있습니다. 예를 들어 mock function의 return type이 void일 때는 void가 아닌 다른 것을 반환하는 action을 사용할 수 없기 때문입니다. 또는 반환값이 있는 action을 `DoAll()`의 중간에 두고 싶을 수도 있습니다. (반환값이 있는 action은 원래`DoAll()`의 마지막에 와야합니다.) 이런 상황이 발생하면 `IgnoreResult()`를 사용하기 바랍니다. + +```cpp +using ::testing::_; +using ::testing::DoAll; +using ::testing::IgnoreResult; +using ::testing::Return; + +int Process(const MyData& data); +string DoSomething(); + +class MockFoo : public Foo { + public: + MOCK_METHOD(void, Abc, (const MyData& data), (override)); + MOCK_METHOD(bool, Xyz, (), (override)); +}; + + ... + MockFoo foo; + EXPECT_CALL(foo, Abc(_)) + // .WillOnce(Invoke(Process)); + // The above line won't compile as Process() returns int but Abc() needs + // to return void. + .WillOnce(IgnoreResult(Process)); + EXPECT_CALL(foo, Xyz()) + .WillOnce(DoAll(IgnoreResult(DoSomething), + // Ignores the string DoSomething() returns. + Return(true))); +``` + +당연한 이야기지만 `IgnoreResult()`를 return type이 이미 `void`인 action에는 사용하면 안 됩니다. + +#### Action의 Argument를 선택하기 #### + +Argument가 7개인 mock function `Foo()`가 있고 여기다가 직접 구현한 action을 연결하려 합니다. 그런데 action은 3개의 argument만 받도록 구현되어 있습니다. 어떻게 해야할까요? + +```cpp +using ::testing::_; +using ::testing::Invoke; +... + MOCK_METHOD(bool, Foo, + (bool visible, const string& name, int x, int y, + (const map>), double& weight, double min_weight, + double max_wight)); +... +bool IsVisibleInQuadrant1(bool visible, int x, int y) { + return visible && x >= 0 && y >= 0; +} +... + EXPECT_CALL(mock, Foo) + .WillOnce(Invoke(IsVisibleInQuadrant1)); // Uh, won't compile. :-( +``` + +위 코드는 당연히 컴파일이 안 될 것입니다. 이 문제를 해결하기 위해서는 먼저 `Foo()`와 동일한 signature를 갖는 "adaptor"를 하나 구현해서 `Invoke()`가 호출할 수 있도록 만들어줘야 합니다. + +```cpp +using ::testing::_; +using ::testing::Invoke; +... +bool MyIsVisibleInQuadrant1(bool visible, const string& name, int x, int y, + const map, double>& weight, + double min_weight, double max_wight) { + return IsVisibleInQuadrant1(visible, x, y); +} +... + EXPECT_CALL(mock, Foo) + .WillOnce(Invoke(MyIsVisibleInQuadrant1)); // Now it works. +``` + +이제 문제없이 동작할 것입니다. 그런데 보기에는 좀 안 좋습니다. + +gMock에서 제공하는 *action adaptor*를 사용하면 훨씬 쉽고 간결하게 구현할 수 있습니다. Adaptor를 구현하는데에 많은 시간을 쓰지 않아도 됩니다. + +```cpp +WithArgs(action) +``` + +`WithArgs`을 사용하면 괄호안의 내부 `action`을 호출해줍니다. (`WithArgs`자체도 action이므로 구분하기 위해 내부 `action`이라고 했습니다.) 이를 통해 mock function으로 전달된 argument들 중에서 필요한 것만 골라서 내부 `action`을 호출할 수 있습니다. + +```cpp +using ::testing::_; +using ::testing::Invoke; +using ::testing::WithArgs; +... + EXPECT_CALL(mock, Foo) + .WillOnce(WithArgs<0, 2, 3>(Invoke(IsVisibleInQuadrant1))); + // No need to define your own adaptor. +``` + +아래처럼 유사한 기능들도 제공합니다. + +- `WithoutArgs(action)`는 내부 `action`이 argument를 받지 않을 때 사용합니다. +- `WithArg(action)`은 내부 `action`이 argument를 1개만 받을 때 사용합니다. (`Args`에서 `s`가 빠졌습니다) + +이름에서 유추할 수 있듯이 `InvokeWithoutArgs(...)`이라는 action은 `WithoutArgs(Invoke(...))`와 동일한 동작을 하며 보기좋게 만들어 제공하는 것 뿐입니다. + +몇가지 사용팁을 소개합니다. + +- `WithArgs`에서 사용하는 내부 action에는 `Invoke()`가 아닌 action을 사용해도 괜찮습니다. +- 특정 argument를 반복해서 사용할 수도 있습니다. 예를 들어 `WithArgs<2, 3, 3, 5>(....)`와 같이 index=`3`인 argument를 여러번 전달해도 됩니다. +- argument들의 순서를 바꿔서 내부 action을 호출하는 것도 가능합니다. 예를 들어 `WithArgs<3, 2, 1>(...)`과 같이 전달해도 됩니다. +- 선택한 argument type과 내부 action의 argument type이 정확히 같지는 않아도 됩니다. 암시적인 변환이 가능하다면 동작합니다. 예를 들어 mock function으로 전달된 `int` type argument는 action의 `double` type argument에 잘 전달될 것입니다. + +#### 관심없는 Argument 무시하기 #### + +[Action의 Argument를 선택하기]()에서 mock function이 action으로 argument를 전달하게 하는 방법을 배웠습니다. 이 때, `WithArgs<...>()`를 사용함에 있어서 한가지 단점은 테스트를 구현하는 사람이 조금 귀찮을 수 있다는 것입니다. + +만약, `Invoke*()`에 사용할 function, functor, lambda를 구현하고 있는 중이라면 `Unused`를 사용해서 `WithArgs`를 대체하는 것도 괜찮습니다. `Unused`는 해당 argument는 사용하지 않을 것이라고 명시적으로 표현하는 것이기 때문에 굳이 `WithArgs`를 사용하지 않아도 mock function과 내부 action을 연결할 수 있습니다. 이것의 장점은 소스코드가 깔끔해지고 argument가 변경에 대해서도 쉽고 빠르게 대응할 수 있습니다. 마지막으로 action의 재사용성도 증대됩니다. 그럼 예제코드를 보겠습니다. + +```cpp + public: + MOCK_METHOD(double, Foo, double(const string& label, double x, double y), + (override)); + MOCK_METHOD(double, Bar, (int index, double x, double y), (override)); +``` + +위 코드와 같이 첫번째 argument만 다른 2개의 mock function에 action을 연결하려면 어떻게 해야 할까요? 지금까지 배운 내용을 사용하면 2개의 action을 각각 정의하고 `Invoke`와 연결해야 합니다. 즉, 아래처럼 구현해야 합니다. + +```cpp +using ::testing::_; +using ::testing::Invoke; + +double DistanceToOriginWithLabel(const string& label, double x, double y) { + return sqrt(x*x + y*y); +} +double DistanceToOriginWithIndex(int index, double x, double y) { + return sqrt(x*x + y*y); +} +... + EXPECT_CALL(mock, Foo("abc", _, _)) + .WillOnce(Invoke(DistanceToOriginWithLabel)); + EXPECT_CALL(mock, Bar(5, _, _)) + .WillOnce(Invoke(DistanceToOriginWithIndex)); +``` + +그러나 `Unused`를 사용하면 action은 1개만 정의해도 됩니다. + +```cpp +using ::testing::_; +using ::testing::Invoke; +using ::testing::Unused; + +double DistanceToOrigin(Unused, double x, double y) { + return sqrt(x*x + y*y); +} +... + EXPECT_CALL(mock, Foo("abc", _, _)) + .WillOnce(Invoke(DistanceToOrigin)); + EXPECT_CALL(mock, Bar(5, _, _)) + .WillOnce(Invoke(DistanceToOrigin)); +``` + +#### Action 공유하기 #### + +gMock에서 matcher는 내부적으로 ref-counted 포인터를 사용하기 때문에 여러 곳에서 공유하기가 편리하다는 내용을 위에서 설명했습니다. Action도 마찬가지입니다. 내부적으로 ref-counted 포인터를 사용하기 때문에 효율적으로 공유될 수 있습니다. 즉, 동일한 action object를 여러곳에서 참조하고 있다면 마지막 참조자가 없어질 때에 action object도 실제로 소멸될 것입니다. + +따라서 복잡한 action을 재사용할 필요가 있다면 매번 새롭게 만들기보다 재사용 가능하도록 구현하는 것이 좋습니다. gMock에서는 action을 복사하는 것도 가능하므로 매우 효율적일 것입니다. 물론 해당 action이 별도의 내부적인 상태를 갖지 않는 경우에만 자유롭게 재사용이 가능할 것입니다. 예를 들어 아래와 같은 `set_flag` action은 내부적으로는 어떠한 상태로 갖지 않으므로 반복해서 사용해도 문제가 없습니다. + +```cpp +using ::testing::Action; +using ::testing::DoAll; +using ::testing::Return; +using ::testing::SetArgPointee; +... + Action set_flag = DoAll(SetArgPointee<0>(5), + Return(true)); + ... use set_flag in .WillOnce() and .WillRepeatedly() ..... +``` + +그러나, action이 스스로 상태를 정의하고 관리하는 경우에는 공유할 때 주의해야 합니다. 예를 들어서 `IncrementCounter(init)`라는 action factory가 있다고 하겠습니다. 이 factory는 `init`이라는 argument로 전달된 값을 통해 내부변수를 초기화하고 그 변수에 다시 +1 하여 반환하는 action을 생성해줍니다. 아래에 2가지 예제코드는 `IncrementCounter(init)`이라는 action의 공유여부에 따라 결과값이 달리지는 것을 보여줍니다. + +```cpp + EXPECT_CALL(foo, DoThis()) + .WillRepeatedly(IncrementCounter(0)); + EXPECT_CALL(foo, DoThat()) + .WillRepeatedly(IncrementCounter(0)); + foo.DoThis(); // Returns 1. + foo.DoThis(); // Returns 2. + foo.DoThat(); // Returns 1 - Blah() uses a different + // counter than Bar()'s. +``` + +먼저, 위의 코드는 action을 공유하지 않는 코드입니다. 즉, `DoThis()`와 `DoThat()`은 서로 다른 `IncrementCounter()`를 갖게 됩니다. 2개 mock function에서 사용하는 action이 독립적인 상태를 유지하기 때문에 1,2,1이 출력됩니다. 반면에 아래 코드는 `DoThis()`와 `DoThat()`이 `IncrementCounter()`를 공유합니다. 즉, action 내부변수도 공유하기 때문에 `0`으로 초기화한 후에 차례대로 1,2,3 이 출력될 것입니다. 이처럼 공유여부에 따라 action의 수행결과가 달라질 수 있기 때문에 사용하는 목적에 따라 주의해서 구현해야 합니다. + +```cpp + Action increment = IncrementCounter(0); + + EXPECT_CALL(foo, DoThis()) + .WillRepeatedly(increment); + EXPECT_CALL(foo, DoThat()) + .WillRepeatedly(increment); + foo.DoThis(); // Returns 1. + foo.DoThis(); // Returns 2. + foo.DoThat(); // Returns 3 - the counter is shared. +``` + +#### 비동기적인 동작을 검증하기 + +비동기적인 동작을 검증하는 것은 gMock을 사용하면서 자주 겪게되는 어려움 중에 하나입니다. EventQueue라는 class가 하나 있고 이를 위한 interface인 EventDispatcher도 있다고 가정해 보겠습니다. EventQueue class는 말 그대로 전달된 event에 대응하는 동작을 수행해주는 class입니다. 단, 이 class가 별도의 thread를 생성해서 코드를 수행한다면 이를 어떻게 검증할 수 있을까요? `sleep()`을 적절히 조절해서 event가 전달되기를 기다려야 할까요? 가능한 방법이긴 하지만`sleep()`을 사용하면 테스트의 동작이 non-deterministic하게 됩니다. 이런 상황에서 제일 좋은 방법은 gMock action와 notification object를 조합하는 것입니다. 이를 통해 비동기적인 동작을 동기적으로 변경할 수 있습니다. 아래 예제코드가 있습니다. + +```c++ +using ::testing::DoAll; +using ::testing::InvokeWithoutArgs; +using ::testing::Return; + +class MockEventDispatcher : public EventDispatcher { + MOCK_METHOD(bool, DispatchEvent, (int32), (override)); +}; + +ACTION_P(Notify, notification) { + notification->Notify(); +} + +TEST(EventQueueTest, EnqueueEventTest) { + MockEventDispatcher mock_event_dispatcher; + EventQueue event_queue(&mock_event_dispatcher); + + const int32 kEventId = 321; + Notification done; + EXPECT_CALL(mock_event_dispatcher, DispatchEvent(kEventId)) + .WillOnce(Notify(&done)); + + event_queue.EnqueueEvent(kEventId); + done.WaitForNotification(); +} +``` + +위의 코드는 일반적인 gMock expectation의 구현방법과 크게 다르지 않지만 `Notification`이라는 object와 이를 사용하는 action이 하나 추가되었습니다. 내용은 `kEventId`가 `DispatchEvent()`로 전달되면 테스트가 성공하는 것입니다. 기존에는 `sleep()`을 통해서 얼마간 기다렸다가 `kEventId`가 전달되었는지 확인했다면 여기서는 `Notification::WaitForNotification()`을 호출해서 실제로 전달되는 시점을 확인할 수 있게 했습니다. 즉, 비동기적인 호출이 완료되기를 기다렸다가 안전하게 테스트를 종료할 수 있습니다. + +Note: 이 예제의 단점도 있습니다. 만약 expectation이 만족하지 않으면 테스트가 영원히 끝나지 않기 때문입니다. 언젠가는 타임아웃이 발생하고 실패로 판정되긴 하겠지만 무작정 기다리는 것은 테스트 수행시간을 늘어나게 하며 디버깅도 어렵게 합니다. 이러한 문제를 해결하기 위해서 `WaitForNotificationWithTimeout(ms)`를 사용하면 얼마간의 시간동안만 기다렸다가 타임아웃을 발생시킬 수 있습니다. + + +### gMock의 다양한 사용법 ### + +#### Method가 Move-Only Type을 사용할 때의 Mocking 방법 #### + +C++11 에서 _move-only-type_ 이 소개되었습니다. Move-only-type이란 이동은 가능하지만 복사는 불가능한 객체를 의미합니다. C++ 의 `std::unique_ptr`가 대표적인 예입니다. + +이러한 move-only-type 을 반환하는 함수를 mocking하는 것은 사실 좀 어렵습니다. 그러나 역시 불가능한 것은 아닙니다. 다만, 이에대한 해결방법이 2017년 4월에 gMock에 추가되었으므로 예전버전을 사용하고 있다면 [Legacy workarounds for move-only types]()를 참조하기를 바랍니다. + +가상의 프로젝트를 진행하고 있다고 해봅시다. 그 프로젝트는 누군가 "buzzes"라고 불리는 짦은글을 작성하고 공유할 수 있게 해주는 프로젝트입니다. 프로젝트에서는 아래와 같은 타입들을 정의하고 사용하고 있습니다. + +```cpp +enum class AccessLevel { kInternal, kPublic }; + +class Buzz { + public: + explicit Buzz(AccessLevel access) { ... } + ... +}; + +class Buzzer { + public: + virtual ~Buzzer() {} + virtual std::unique_ptr MakeBuzz(StringPiece text) = 0; + virtual bool ShareBuzz(std::unique_ptr buzz, int64_t timestamp) = 0; + ... +}; +``` + +간단히 설명하면 `Buzz` object는 작성하고 있는 글을 의미하는데 `Buzzer` interface를 상속받고 구체화한 class가 `Buzz` object를 사용하는 주체가 될 것입니다. `Buzzer`의 method들은 return type과 argument type에 `unique_ptr`를 사용하고 있습니다. 그럼 이제 `Buzzer`를 mocking해 봅시다. + +먼저, move-only-type을 반환하는 method를 mocking할 때도 `MOCK_METHOD`를 사용합니다. + +```cpp +class MockBuzzer : public Buzzer { + public: + MOCK_METHOD(std::unique_ptr, MakeBuzz, (StringPiece text), (override)); + MOCK_METHOD(bool, ShareBuzz, (std::unique_ptr buzz, int64_t timestamp), + (override)); +}; +``` + +위와 같이 mock class를 정의했으며 이제 사용할 차례입니다. 아래에 `MockBuzzer` class 의 객체 `mock_buzzer_`를 생성했습니다. + +```c++ + MockBuzzer mock_buzzer_; +``` + +다음으로 `unique_ptr`를 반환하는 `MakeBuzz()`에 expectation을 설정하려면 어떻게 해야 할까요? + +지금까지 해왔던 것처럼 `.WillOnce()`, `.WillRepeatedly()`를 사용해서 expectation을 지정하면 됩니다. 물론 action을 따로 연결하지 않는다면 default action이 수행될 것입니다. `unique_ptr<>`를 반환하기 위한 default action은 default constructor를 사용해 object를 생성해주는 것입니다. 따라서 별도의 action을 연결하지 않는다면 `nullptr`을 가리키는 `unique_ptr` object가 반환될 것입니다. + +```cpp + // Use the default action. + EXPECT_CALL(mock_buzzer_, MakeBuzz("hello")); + + // Triggers the previous EXPECT_CALL. + EXPECT_EQ(nullptr, mock_buzzer_.MakeBuzz("hello")); +``` + +만약, default action을 변경하고 싶다면 [Setting Default Actions](#setting-the-default-actions-for-a-mock-method)를 참조하세요. + +`Return(ByMove(...))`를 사용하면 특정한 move-only-type 값을 반환하도록 지정할 수도 있습니다. + +```cpp + // When this fires, the unique_ptr<> specified by ByMove(...) will + // be returned. + EXPECT_CALL(mock_buzzer_, MakeBuzz("world")) + .WillOnce(Return(ByMove(MakeUnique(AccessLevel::kInternal)))); + + EXPECT_NE(nullptr, mock_buzzer_.MakeBuzz("world")); +``` + +`ByMove()`가 없다면 당연히 compile error가 발생할 것입니다. + +여기서 문제입니다, `Return(ByMove(...))` action이 한 번 이상 수행될 수 있을까요? 예를 들면 `WillRepeatedly(Return(ByMove(...)));` 와 같이 사용하는 것이 가능할까요? 해당 action이 한 번 수행되면 해당 값은 mock fuction을 호출한 caller쪽으로 이동하게 됩니다. 즉, mock function이 다시 호출되서 action을 수행하려고 해도 이동해야할 값이 이미 없어진 상태가 됩니다. 이러한 상황은 runtime error를 발생시킵니다. 결론적으로 `Return(ByMove(...))`는 한 번만 호출될 수 있습니다. + +여기서 lambda 혹은 callable object를 사용하면 더 다양한 활용이 가능합니다. 특히, move-only-type에 대해서도 `WillRepeatedly`를 사용할 수 있습니다. 아래는 그러한 예제코드입니다. + +```cpp + EXPECT_CALL(mock_buzzer_, MakeBuzz("x")) + .WillRepeatedly([](StringPiece text) { + return MakeUnique(AccessLevel::kInternal); + }); + + EXPECT_NE(nullptr, mock_buzzer_.MakeBuzz("x")); + EXPECT_NE(nullptr, mock_buzzer_.MakeBuzz("x")); +``` + +`Return(ByMove(...))`로는 불가능한 일이 가능해졌습니다. 이제 `mock_buzzer`가 호출될 때마다 새로운 `unique_ptr`가 생성되고 반환될 것입니다. + +지금까지 move-only-type을 반환하는 방법에 대해서 배웠습니다. 그럼 move-only argument를 전달 받으려면 어떻게 해야할까요? 정답은 대부분의 경우에 별도의 조치를 취하지 않아도 잘 동작한다는 것입니다. 혹시나 문제가 되는 부분이 있다고 해도 compile error를 통해 미리 확인할 수 있을 것입니다. `Return`, [lambda, functor](#using-functionsmethodsfunctors-as-actions)도 언제든 사용할 수 있습니다. + +```cpp + using ::testing::Unused; + + EXPECT_CALL(mock_buzzer_, ShareBuzz(NotNull(), _)).WillOnce(Return(true)); + EXPECT_TRUE(mock_buzzer_.ShareBuzz(MakeUnique(AccessLevel::kInternal)), + 0); + + EXPECT_CALL(mock_buzzer_, ShareBuzz(_, _)).WillOnce( + [](std::unique_ptr buzz, Unused) { return buzz != nullptr; }); + EXPECT_FALSE(mock_buzzer_.ShareBuzz(nullptr, 0)); +``` + +`WithArgs`, `WithoutArgs`, `DeleteArg`, `SaveArg`를 비롯한 많은 built-in action들이 이미 move-only argument를 받을 수 있도록 변화하고 있습니다. 다만 아직 구현이 완료된 것은 아니기 때문에 사용중에 문제가 발생한다면 이슈로 등록해 주시기 바랍니다. + +`DoAll`과 같은 일부 action은 내부적으로 argument의 복사본을 만듭니다. 따라서 복사가 불가능한 object는 `DoAll`에 사용할 수 없습니다. 이런 경우에는 functor를 사용해야 합니다. + +##### Legacy : move-only type 해결방법 + +Move-only argument를 지원하는 기능은 2017년 4월에 gMock에 추가되었습니다 + +그보다 오래된 코드를 사용하고 있다면 아래와 같은 방법을 사용해야 할 것입니다. (사실 더 이상 필요하지는 않지만 참조용으로 남겨둔 내용입니다.) + +```cpp +class MockBuzzer : public Buzzer { + public: + MOCK_METHOD(bool, DoShareBuzz, (Buzz* buzz, Time timestamp)); + bool ShareBuzz(std::unique_ptr buzz, Time timestamp) override { + return DoShareBuzz(buzz.get(), timestamp); + } +}; +``` + +해결방법은 move-only argument를 전달받는 `SharedBuzz()`의 역할을 위임하기 위한 `DoShareBuzz()`를 새로 추가하는 것입니다. 그런 다음에 `ShareBuzz()` 대신에 `DoShareBuzz`를 mocking 하면 됩니다. + +```cpp + MockBuzzer mock_buzzer_; + EXPECT_CALL(mock_buzzer_, DoShareBuzz(NotNull(), _)); + + // When one calls ShareBuzz() on the MockBuzzer like this, the call is + // forwarded to DoShareBuzz(), which is mocked. Therefore this statement + // will trigger the above EXPECT_CALL. + mock_buzzer_.ShareBuzz(MakeUnique(AccessLevel::kInternal), 0); +``` + + + +#### 컴파일을 빠르게 하기 #### + +동의할지는 모르겠지만, mock class를 컴파일하는 시간의 대부분은 constructor와 destructor를 만드는데 사용됩니다. 왜냐하면 constructor나 destructor에서 expectation을 검증하는 것과 같이 중요한 일들을 수행하기 때문입니다. 더 나아가서 mock method의 signature가 여러가지 타입에 대응해야 한다면 당연히 constructor나 destructor도 각각의 타입에 대해 생성될 것입니다. 결과적으로 다양한 타입을 사용하는 다수의 method를 mocking해야할 때, 컴파일 속도가 느려질 수도 있습니다. + +사용자가 컴파일 속도가 느려서 문제를 겪고 있다면, 이러한 mock class의 constructor와 destructor의 정의를 class body 밖으로 빼내서 별도의 `.cpp`파일에 구현하는 것이 좋은 방법입니다. 이렇게 하면 mock class가 여러파일에 분리되어 있더라도 각각의 constructor와 destructor는 한번만 생성되기 때문에 컴파일 속도가 빨라질 것입니다. + +이러한 주장을 예제로 확인해 보겠습니다. + +```cpp +// File mock_foo.h. +... +class MockFoo : public Foo { + public: + // Since we don't declare the constructor or the destructor, + // the compiler will generate them in every translation unit + // where this mock class is used. + + MOCK_METHOD(int, DoThis, (), (override)); + MOCK_METHOD(bool, DoThat, (const char* str), (override)); + ... more mock methods ... +}; +``` + +먼저 위의 코드는 개선사항을 적용하기 전입니다. Class에 constructor와 destructor가 없기 때문에 컴파일러가 직접 생성해줘야 하며 이를 위해 많은 컴파일 시간이 소모됩니다. + +반면에 아래 코드는 constructor와 destructor를 직접 선언한 후에 정의(구현)는 하지 않았습니다. + +```cpp +// File mock_foo.h. +... +class MockFoo : public Foo { + public: + // The constructor and destructor are declared, but not defined, here. + MockFoo(); + virtual ~MockFoo(); + + MOCK_METHOD(int, DoThis, (), (override)); + MOCK_METHOD(bool, DoThat, (const char* str), (override)); + ... more mock methods ... +}; +``` +이제 constructor와 destructor에 대한 정의는 별도 파일인 `mock_foo.cpp`에 구현합니다. +```cpp +// File mock_foo.cc. +#include "path/to/mock_foo.h" + +// The definitions may appear trivial, but the functions actually do a +// lot of things through the constructors/destructors of the member +// variables used to implement the mock methods. +MockFoo::MockFoo() {} +MockFoo::~MockFoo() {} +``` + +#### 검증을 바로 수행하기 #### + +Mock object는 자신이 소멸되는 시점에 연관된 모든 expectation에 대한 검증을 수행하고 성공이나 실패를 알려줍니다. 이런 동작방식을 채택함으로서 사용자가 expectation을 검증할 직접 구현할 필요가 없어졌습니다. 그러나 이러한 동작은 mock object가 파괴되지 않는다면 또 문제가 될 수 있습니다. + +Mock object가 소멸되지 않는다면 어떤일이 발생할까요? 어떤 bug로 인해서 mock object가 소멸되지 않았다고 가정해봅시다. 그렇다면 검증도 일어나지 않게 되니 실제로 문제가 되는 bug들도 발견하지 못하고 넘어갈 수 있습니다. + +이러한 문제를 완화시키기 위해서는 heap checker를 사용하는 것이 좋습니다. Heap checker는 mock object의 소멸여부를 알려주는 역할을 수행합니다. 다만, 사실 heap checker의 구현도 100% 완벽하다고는 할 수 없습니다. 이러한 이유로 gMock은 사용자가 직접 검증을 수행할 수 있는 방법을 제공하고 있습니다. `Mock::VerifyAndClearExpectations(&mock_object)`를 사용하면 됩니다. + +```cpp +TEST(MyServerTest, ProcessesRequest) { + using ::testing::Mock; + + MockFoo* const foo = new MockFoo; + EXPECT_CALL(*foo, ...)...; + // ... other expectations ... + + // server now owns foo. + MyServer server(foo); + server.ProcessRequest(...); + + // In case that server's destructor will forget to delete foo, + // this will verify the expectations anyway. + Mock::VerifyAndClearExpectations(foo); +} // server is destroyed when it goes out of scope here. +``` + +**Tip:** `Mock::VerifyAndClearExpectations()`은 검증의 성공여부를 `bool`타입으로 반환하여 알려줍니다. 따라서 해당 mock object의 검증결과를 `ASSERT_TRUE()`계열을 통해서 다시 확인하는 것도 가능합니다. + +#### Check Point 사용하기 #### + +어떤 mock object에 설정한 다양한 내용들을 "reset"하고 싶을 때도 있습니다. 그러한 시점을 "check point"라고 부릅니다. 즉 현재까지 설정된 모든 expectation에 대한 검증을 수행해 버리고 새로운 expectation을 다시 지정하는 것입니다. 이러한 방법은 mock object가 "phases" 개념 위에서 동작하도록 만들어 줍니다. 즉, 단계별로 검증해야 할 항목들을 나누는 것이 가능해집니다. + +가능한 예상 시나리오는 먼저 `SetUp()`에서 1차적인 검증을 수행한 후에 개별 `TEST_F`를 정의할 때는 `SetUp()`에서 사용한 기존의 expectation을 초기화하고 새롭게 지정하는 것입니다. + +위에서 확인한 것처럼 `Mock::VerifyAndClearExpectations()`함수는 호출되는 시점에 바로 검증을 수행하므로 이러한 경우에 사용하기 적합합니다. 혹시, `ON_CALL()`을 사용해서 default action을 지정했다가 특정시점에 변경하는 방법을 사용하고 있다면 `Mock::VerifyAndClear(&mock_object)`를 사용하기를 추천합니다. 이 함수는 `Mock::VerfiyAndClearExpectations(&mock_object)`와 동일한 동작을 함과 동시에 추가적으로 `ON_CALL()`을 통해 설정한 내용도 초기화 해주기 때문입니다. + +또 다른 방법은 expectation을 sequence안에 두는 것입니다. 그런 다음 가짜 "check-point"를 만들어서 검증하려는 함수의 호출순서를 강제화하기 위해 사용할 수 있습니다. 예를 들어 아래코드를 검증한다고 해봅시다. + +```cpp + Foo(1); + Foo(2); + Foo(3); +``` + +여기서 검증하려는 내용은 `Foo(1)`과 `Foo(3)`이 `mock.Bar("a")`를 호출해야 한다는 것과 `Foo(2)`는 아무것도 호출하지 않기를 바란다는 것입니다. 이제 아래처럼 구현하면 됩니다. + +```cpp +using ::testing::MockFunction; + +TEST(FooTest, InvokesBarCorrectly) { + MyMock mock; + // Class MockFunction has exactly one mock method. It is named + // Call() and has type F. + MockFunction check; + { + InSequence s; + + EXPECT_CALL(mock, Bar("a")); + EXPECT_CALL(check, Call("1")); + EXPECT_CALL(check, Call("2")); + EXPECT_CALL(mock, Bar("a")); + } + Foo(1); + check.Call("1"); + Foo(2); + check.Call("2"); + Foo(3); +} +``` + +위의 expectation은 첫번째 `Bar("a")`가 check point "1" 이전에 수행되기를 기대하고 두번째 `Bar("a")`는 check point "2" 이후에 수행되기를 기대합니다. check point "1" 과 check point "2" 사이에는 `Bar("a")` 가 호출되면 안됩니다. 이렇게 명시적으로 check point를 만들어서 `Bar("a")`가 3개의 `Foo()` 호출 중 어느것에 매칭되어야 하는지 표현할 수 있습니다. + +#### Destructor Mocking 하기 #### + +때떄로 mock object가 원하는 시간에 소멸되기를 바랄 수 있습니다. 예를 들어 mock object가 `bar->A()`와 `bar->B()`가 호출되는 중간에 소멸되기를 기대하는 것입니다. Mock function이 호출되는 순서에 대해서는 이미 배웠기 때문에 destructor를 mocking하는 방법만 배우면 구현할 수 있을 것입니다. + +한 가지만 제외하면 그다지 어렵지 않습니다. 먼저 destructor는 일반적인 함수와는 문법부터 좀 다릅니다. 따라서 `MOCK_METHOD` 매크로를 사용할 수 없습니다. + +```cpp +MOCK_METHOD(void, ~MockFoo, ()); // Won't compile! +``` + +좋은 소식은 간단한 패턴을 적용하면 동일한 효과를 얻을 수 있다는 것입니다. 먼저 `Die()`라는 mock function을 하나 생성합니다. 그런 다음 `Die()`를 destructor에서 호출하도록 구현합니다. + +```cpp +class MockFoo : public Foo { + ... + // Add the following two lines to the mock class. + MOCK_METHOD(void, Die, ()); + virtual ~MockFoo() { Die(); } +}; +``` + +`Die()`라는 이름을 이미 사용중이면 다른 이름을 사용해도 됩니다. 이제 끝났습니다. `MockFoo` class의 destructor가 호출되는 시점을 확인할 수 있게 되었습니다. 아래와 같이 구현하면 `Die()`가 `bar->A()`, `bar->B()` 중간에 호출되는지를 검증할 수 있습니다. + +```cpp + MockFoo* foo = new MockFoo; + MockBar* bar = new MockBar; + ... + { + InSequence s; + + // Expects *foo to die after bar->A() and before bar->B(). + EXPECT_CALL(*bar, A()); + EXPECT_CALL(*foo, Die()); + EXPECT_CALL(*bar, B()); + } +``` + +#### gMock과 Thread 사용하기 #### + +사실 **unit** test는 single-threaded context에 구현하는 것이 제일 좋은 방법이긴 합니다. 그렇게 해야 race condition과 dead lock을 피할 수 있고, 디버깅하기가 훨씬 쉽기 때문입니다. + +그러나 실제로 많은 프로그램은 multi-threaded context로 구현되어 있고, 이를 위한 테스트도 필요하게 됩니다. gMock은 관련한 테스트 방법들도 제공하고 있습니다. + +Mock을 사용하는 과정을 떠올려 봅시다. + +1. `foo`라는 mock object를 생성합니다. +2. `ON_CALL()`과 `EXPECT_CALL()`을 이용해서 `foo`의 method들에 기대하는 내용(default action, expectation)을 지정합니다. +3. 테스트 대상코드가 `foo`의 method들을 실제로 호출합니다. +4. (선택사항) 검증하고 mock을 초기화합니다. +5. Mock을 직접 소멸시키거나 테스트 대상코드에서 소멸시키도록 합니다. 이제 destructor가 검증을 수행하게 됩니다. + +이렇게 mock을 사용하는 5단계를 진행하면서 아래 규칙들만 잘 지킨다면 thread를 적용하는데도 문제가 없을 것입니다. + +- 테스트 코드는 하나의 thread에서 시작합니다. (테스트 대상코드는 아닐수도 있지만) +- Mock 사용과정 - 1단계에서 동기화도구(lock 종류)는 사용하지 않습니다. +- Mock 사용과정 - 2단계, 5단계를 수행할때는 다른 thread가 `foo`에 접근하지 못하도록 합니다. +- Mock 사용과정 - 3단계, 4단계는 원하는 환경으로 수행할 수 있습니다. (single thread 혹은 multiple threads) gMock이 내부적으로 동기화 해주기 때문에 테스트코드에서 필요한 것이 아니라면 굳이 직접 동기화를 구현할 필요는 없습니다. + +만약 이러한 규칙을 어긴다면 미정의 동작이 발생하며 문제가 됩니다. 예를 들어 위의 2단계처럼 `foo`의 어떤 method에 대해 expectation을 설정하고 있을 때, 다른 thread가 해당 method를 호출하면 문제가 발생하게 됩니다. 그러한 부분을 주의하시기 바랍니다. + +gMock은 어떤 mock function의 action이 해당 mock function을 호출한 thread와 동일한 thread에서 수행되는 것을 보장합니다. + +```cpp + EXPECT_CALL(mock, Foo(1)) + .WillOnce(action1); + EXPECT_CALL(mock, Foo(2)) + .WillOnce(action2); +``` + +즉, 위의 코드에서 `Foo(1)`이 thread - #1에서 호출되고 `Foo(2)`가 thread - #2에서 호출된다면 `action1`은 thread - #1 수행되고 `action2`는 thread - #2에서 수행된다는 의미입니다. + +gMock은 같은 sequence에 속한 action들이 서로 다른 thread에서 수행되는 것을 *허용하지 않습니다.* 만약 그렇게 되면 action들간에 협력이 필요한 경우에 deadlock을 유발할 수도 있기 때문입니다. 이것은 곧 위 예제의 `action1`과 `action2`에는 sequence가 사용되지 않았기 때문에 서로 다른 thread에서 수행해도 된다는 것을 의미합니다. 단, 사용자의 구현 과정에서 발생하는 동기화 문제들에 대해서는 직접 동기화 로직을 추가하고 thread-safe로 만들어야 합니다. + +다음으로 `DefaultValue`를 사용하면 현재 test program에서 살아있는 모든 mock object에 영향을 준다는 것입니다. 따라서 multiple threads 환경이거나 수행중인 action이 있을 때는 사용하지 않는 것이 좋습니다. + +#### gMock이 출력할 정보의 양 조절하기 #### + +gMock은 잠재적으로 error 가능성이 있는 부분에 대해서는 warning message를 출력해 줍니다. 대표적인 예로 uninteresting call 이 있습니다. 즉, expectation이 없는 mock function에 대해서 error로 여기지는 않지만 사용자가 실수했을 수도 있다는 것을 알리기 위해 warning message로 알려주게 되는 것입니다. 이 때, 해당 uninteresting function에 전달된 argument와 return value 등의 정보를 출력해줌으로써 사용자는 이것이 실제로 문제인지 아닌지 다시 한 번 검토해 볼 수 있게 됩니다. + +만약, 현재 테스트코드에 문제가 없다고 생각되는 순간이 오면 이와 같은 정보가 달갑지 않을 것입니다. 반대로 테스트코드를 디버깅하고 있거나 테스트대상의 동작을 공부하는 중이라면 그러한 정보가 많을 수록 좋을 것입니다. 결론부터 말하면 사람마다 필요한 정보의 양의 다르다는 것입니다. + +gMock에서는 이러한 정보의 양을 조절하는 방법을 제공합니다. 실행시점에 `--gmock_verbose=LEVEL` 이라는 flag를 사용하면 됩니다. 여기서 `LEVEL`에는 3개의 값을 지정할 수 있습니다. + +- `info`: gMock은 warning, error등의 모든 정보를 최대한 자세하게 출력합니다. 또한, `ON_CALL/EXPECT_CALL` 사용에 대한 log도 출력해 줍니다. +- `warning`: gMock은 warning, error를 출력해 줍니다. 단, 그 내용이 `info`모드보다 자세하지는 않습니다. 현재 gMock의 기본설정입니다. +- `error`: gMock은 error만 출력해줍니다. + +아래와 같은 변수를 수정해서 flag와 동일한 기능을 적용할 수 있습니다. + +```cpp + ::testing::FLAGS_gmock_verbose = "error"; +``` + +이제, 어떤 모드가 본인에게 제일 적합한지 잘 판단하고 사용하기 바랍니다. + +#### Mock 호출을 자세하게 들여다보기 #### + +gMock은 mock function에 지정한 expectation이 만족되지 않으면 이를 알려줍니다. 그러나 왜 그런 문제가 발생했는지가 궁금한 사용자도 있을 것입니다. 예를 들어 matcher를 사용할 때 어떤 오타가 있었는지, `EXPECT_CALL`의 순서가 틀렸는지, 테스트 대상코드에 문제가 있었는지 등등, 어떻게 하면 이런 내용을 알아낼 수 있을까요? + +마치 X-ray를 보듯이 모든 `EXPECT_CALL`과 mock method 호출에 대해서 추적할 수 있다면 좋지 않을까요? 각각의 호출에 대해서 실제 전달된 argument를 알고 싶나요? 또한 어떤 `EXPECT_CALL`과 비교되었는지도 알고 싶을 수 있습니다. + +`--gmock_verbose=info` flag를 사용하면 이러한 X-ray를 사용할 수 있습니다. 아래에 예제를 보시죠. + +```cpp +#include "gmock/gmock.h" + +using testing::_; +using testing::HasSubstr; +using testing::Return; + +class MockFoo { + public: + MOCK_METHOD(void, F, (const string& x, const string& y)); +}; + +TEST(Foo, Bar) { + MockFoo mock; + EXPECT_CALL(mock, F(_, _)).WillRepeatedly(Return()); + EXPECT_CALL(mock, F("a", "b")); + EXPECT_CALL(mock, F("c", HasSubstr("d"))); + + mock.F("a", "good"); + mock.F("a", "b"); +} +``` + +위 test program을 `--gmock_verbose=info` flag와 함께 실행하면 아래와 같은 정보가 출력될 것입니다. + +```bash +[ RUN ] Foo.Bar + +foo_test.cc:14: EXPECT_CALL(mock, F(_, _)) invoked +Stack trace: ... + +foo_test.cc:15: EXPECT_CALL(mock, F("a", "b")) invoked +Stack trace: ... + +foo_test.cc:16: EXPECT_CALL(mock, F("c", HasSubstr("d"))) invoked +Stack trace: ... + +foo_test.cc:14: Mock function call matches EXPECT_CALL(mock, F(_, _))... + Function call: F(@0x7fff7c8dad40"a",@0x7fff7c8dad10"good") +Stack trace: ... + +foo_test.cc:15: Mock function call matches EXPECT_CALL(mock, F("a", "b"))... + Function call: F(@0x7fff7c8dada0"a",@0x7fff7c8dad70"b") +Stack trace: ... + +foo_test.cc:16: Failure +Actual function call count doesn't match EXPECT_CALL(mock, F("c", HasSubstr("d")))... + Expected: to be called once + Actual: never called - unsatisfied and active +[ FAILED ] Foo.Bar +``` + +만약, 3번째 `EXPECT_CALL`의 `"c"`가 원래 `"a"`를 쓰려다가 잘못 쓴 오타라고 해봅시다. 즉, `mock.F("a", "good")`호출은 원래 3번째 `EXPECT_CALL`과 매칭되었어야 합니다. 이제 위에 출력된 정보를 다시 보면 `mock.F("a", "good")`이 첫번째 `EXPECT_CALL`과 매칭되어 문제가 됐음을 알 수 있고, 빠르게 오타를 수정하게 될 것입니다. + +Mock call trace은 보고싶지만 stack trace는 보고 싶지 않다면, test program을 실행할 때 flag를 2개를 조합해서(`--gmock_verbose=info --gtest_stack_trace_depth=0`) 실행하면 됩니다. + +#### Emacs에서 테스트 실행하기 #### + +Emacs에서 `M-x google-complie` 명령을 통해 테스트를 구현하고 실행하는 사용자들은 googletest, gMock error가 발생했을 때, 관련 소스파일의 위치가 강조되어 출력되는걸 볼 수 있습니다. 그 때, 강조된 부분에서 ``를 누르면 바로 해당 위치로 이동하게 될 것입니다. 또는 `C-x`를 누르게 되면 다음 error 위치로 바로 이동할 수 있습니다. + +`~/.emacs` 파일에 아래 내용을 추가하면 좀 더 편리하게 사용할 수 있습니다. + +```bash +(global-set-key "\M-m" 'google-compile) ; m is for make +(global-set-key [M-down] 'next-error) +(global-set-key [M-up] '(lambda () (interactive) (next-error -1))) +``` + +이제 `M-m`을 눌러서 빌드를 시작하고, `M-up` / `M-down`을 눌러서 error간에 이동할 수 있습니다. 더불어서 빌드할 때마다 테스트도 같이 수행하고 싶다면 `M-m`명령의 build command 설정부분에 `foo_test.run` 혹은 `runtests`라는 내용을 추가하면 됩니다. + +### Extending gMock ### + +#### 새로운 Matcher를 빠르게 구현하기 #### + +WARNING: gMock은 matcher가 언제 몇 번 호출될지를 보장하지 않습니다. 따라서 모든 matcher는 순수하게 기능적인 동작만 수행하도록 구현해야 합니다. 즉, 프로그램 내의 다른 정보에 대한 side effect나 의존성을 가지면 안된다는 것입니다. 자세한 내용은 [여기]()를 참조하세요. + +gMock이 제공하는 `MATCHER*` macro는 사용자가 새로운 matcher를 만들 수 있도록 도와줍니다. 기본적인 문법은 아래와 같습니다. + +```cpp +MATCHER(name, description_string_expression) { statements; } +``` + +위의 매크로는 `statements`를 실행하는 matcher를 만들어 줍니다. 이렇게 생성된 matcher는 성공,실패를 알려야 하기 떄문에 항상 `bool`값을 반환합니다. `statements`를 구현할 때는 특수 argument들을 사용할 수 있습니다. 그 중에서도 `arg`, `arg_type`은 mock function에서 matcher로 전달되는 argument와 argument type을 의미합니다. + +`description_string_expression`은 `string` 타입으로서 matcher가 무엇을 해야하는지 알려주는 용도로 사용되며 같은 내용이 failure message를 출력할때도 사용됩니다. 이 부분을 구현할 때 `negation`이라고 불리우는 `bool`타입의 특수 argument를 참조할 수 있는데 이것은 matcher의 성공, 실패 여부에 따라 관련정보를 다르게 출력하기 위한 용도로 주로 사용합니다. 아래쪽에서 관련예제와 함께 다시 설명하겠습니다. + +사용편의성을 위해서 `description_string_expression`에는 (`""`)와 같이 빈 문자열도 사용 가능합니다. 이 때에는 `name`을 조금 변경해서 description으로 대체로 사용합니다. + +아래에 간단한 matcher를 정의한 예제가 있습니다. +```cpp +MATCHER(IsDivisibleBy7, "") { return (arg % 7) == 0; } +``` +이렇게 정의된 matcher는 built-in matcher을 사용하듯이 동일한 방법으로 사용하면 됩니다. +```cpp + // Expects mock_foo.Bar(n) to be called where n is divisible by 7. + EXPECT_CALL(mock_foo, Bar(IsDivisibleBy7())); +``` +`EXPECT_THAT`을 사용하는 것도 가능합니다. + +```cpp + using ::testing::Not; + ... + // Verifies that two values are divisible by 7. + EXPECT_THAT(some_expression, IsDivisibleBy7()); + EXPECT_THAT(some_other_expression, Not(IsDivisibleBy7())); +``` +위 assertion들이 실패하면, failure message도 아래처럼 출력될 것입니다. +```bash + Value of: some_expression + Expected: is divisible by 7 + Actual: 27 + ... + Value of: some_other_expression + Expected: not (is divisible by 7) + Actual: 21 +``` +Expected 부분에서 볼 수 있는 `"is divisible by 7"`과 `"not (is divisible by 7)"`은 matcher의 이름인 `IsDivisibleBy7`이라는 이름을 통해서 자동적으로 생성되는 내용입니다. + +만약, 자동으로 생성되는 출력문에 만족하지 못하는 경우에는 `description_string_expression` 재정의하면 됩니다. 이 때 바로 `negation`을 사용할 수 있습니다. 아래 예제가 있습니다. + +```cpp +MATCHER(IsDivisibleBy7, + absl::StrCat(negation ? "isn't" : "is", " divisible by 7")) { + return (arg % 7) == 0; +} +``` + +또한, `result_listener`라는 숨겨진 argument를 사용하면 추가적인 정보를 출력하게 할 수 있습니다. 예를 들어서 `IsDivisibleBy7`이 수행한 동작에 대한 부연설명을 아래처럼 추가하는 것이 가능합니다. + +```cpp +MATCHER(IsDivisibleBy7, "") { + if ((arg % 7) == 0) + return true; + + *result_listener << "the remainder is " << (arg % 7); + return false; +} +``` + +이제 바뀐 출력정보를 확인할 수 있습니다. + +```bash + Value of: some_expression + Expected: is divisible by 7 + Actual: 27 (the remainder is 6) +``` + +`result_listener`는 사용자에게 도움을 주는 내용이라면 무엇이든 출력해도 상관 없습니다. 다만, 한 가지 기억해야 할 점은 매칭이 성공이라면 왜 성공했는지에 대한 정보까지 알려줘야 유용하다는 것입니다. 이것은 해당 matcher가 `Not()`과 함께 사용되었을 때 특히 유용합니다. 마지막으로 argument가 가지고 있는 값을 출력하는 부분은 gMock에 이미 구현되어 있으므로 따로 구현하지 않아도 됩니다. + +**Notes:** `arg_type`은 matcher를 사용하는 context 혹은 compiler에 따라서 달라질 수도 있지만 사용자가 신경 쓸 필요는 없습니다. 이것은 matcher를 polymorphic하게 사용하기 위해 선택된 동작방식입니다. 예를 들어 `IsDivisibleBy7()`과 같은 예제에서 `(arg % 7)`을 계산하고 그 결과를 `bool`타입으로 반환할 수만 있다면 `arg_type`은 어떤 타입이 되더라도 괜찮습니다. 즉, (암시적인 형변환까지 포함해서) `%`연산을 수행할 수 있는 타입이라면 모두 사용할 수 있는 matcher가 만들어지는 것입니다. 예제코드의 `Bar(IsDivisibleBy7())`를 보면, `Bar()`라는 method는 `int`타입 argument를 전달받고 있기 때문에 `arg_type`도 역시 `int`로 결정됩니다. 이 때, argument가 `unsigned long`이라고 해도 문제가 없는 것입니다. 단지 `arg_type`이 `unsigned long`가 되는 것 뿐입니다. + +#### 새로운 Parameterized Matcher를 빠르게 구현하기 #### + +Matcher 자체적으로 parameter를 가지길 원할 수도 있습니다. 본격적인 설명에 앞서 matcher에서 사용하는 argument와 parameter라는 용어를 구분할 필요가 있습니다. 먼저, argument란 mock function으로부터 matcher로 전달되는 값들을 의미합니다. 그러나 parameter는 matcher 자신이 스스로 관리하고 사용하려는 목적의 값들을 의미합니다. 즉, mock function과는 직접적인 연관이 없습니다. 여기서는 matcher가 parameter를 사용하려면 어떻게 구현해야 하는지 소개합니다. 기본적으로는 아래 macro를 사용하게 됩니다. + +```cpp +MATCHER_P(name, param_name, description_string) { statements; } +``` +위 macro를 보면 `description_string`에는 string expression를 전달할 수 있습니다. `""`도 역시 사용 가능합니다. 더불어 `description_string`을 구현할 때는 `negation`, `param_name`과 같은 특수 argument를 사용하는 것도 가능합니다. + +먼저, `MATHCER_P`를 사용해 간단한 parameterized matcher를 구현한 예제입니다. +```cpp +MATCHER_P(HasAbsoluteValue, value, "") { return abs(arg) == value; } +``` +이렇게 정의된 `HasAbsoluteValue`라는 matcher는 기존의 matcher처럼 사용하기만 하면 됩니다. + +```cpp + EXPECT_THAT(Blah("a"), HasAbsoluteValue(n)); +``` +여기서 `10`이라는 값이 바로 matcher로 전달되는 parameter이며 parameter name(`param_name`)이 `value`가 되는 것입니다. 실행하면 아래와 같은 출력문을 확인할 수 있습니다. + +```bash + Value of: Blah("a") + Expected: has absolute value 10 + Actual: -9 +``` + +출력문의 Expected 부분에 matcher description과 parameter가 함께 출력되는 것을 볼 수 있습니다. 이렇게 출력되는 결과를 통해서 사용자가 문제를 이해하게 쉽게 도와주고 있습니다. + +`MATCHER_P`의 `statements`를 구현할 때는 parameter의 타입을 참조하는 것도 가능합니다. 예를 들어 위에서 `value`라는 parameter의 타입을 확인하려면 `value_type`을 사용하면 됩니다. + +gMock은 parameter를 여러개 사용하고 싶은 경우를 위한 macro도 지원합니다. 각 macro의 이름은 `MATCHER_P2`, `MATCHER_P3`, ... 과 같습니다. 단, `MATCHER_P10`까지만 지원하기 때문에 최대로 사용할 수 있는 paratmeter의 개수는 10개입니다. + +```cpp +MATCHER_Pk(name, param_1, ..., param_k, description_string) { statements; } +``` + +위에서 `description_string`은 matcher의 **instance** 마다 다를 수 있음을 기억하기 바랍니다. 위에서 얘기했듯이 `description_string`이 `param_name`을 참조하기 때문에 `param_name`의 값에 따라 출력되는 내용이 달라질 수 있다는 의미입니다. 또한, `description_string`에서 parameter를 참조하여 출력하는 방식을 직접 구현할 수도 있습니다. + +예제를 보겠습니다. +```cpp +using ::testing::PrintToString; +MATCHER_P2(InClosedRange, low, hi, + absl::StrFormat("%s in range [%s, %s]", negation ? "isn't" : "is", + PrintToString(low), PrintToString(hi))) { + return low <= arg && arg <= hi; +} +... +EXPECT_THAT(3, InClosedRange(4, 6)); +``` +위의 코드는 `low`, `hi`라는 2개의 `param_name`을 사용하고 있으며 `description_string`에서 이것들을 참조하고 있습니다. 추가로 `negation`도 참조가능함을 볼 수 있습니다. (`negation`의미가 *부정*이기 때문에 값이 `false`일 때가 곧 매칭 성공을 의미한다는 것에 주의하기 바랍니다.) +```bash + Expected: is in range [4, 6] +``` + +만약, 위와 같이 `description_string`을 직접 제공하지 않고, `""`를 전달한다면 출력문의 Expected 부분을 자동으로 출력해 줍니다. 이 때에는 matcher name을 단어별로 먼저 출력하고 다음으로 parameter에 저장된 값을 출력해 줍니다. + +```cpp + MATCHER_P2(InClosedRange, low, hi, "") { ... } + ... + EXPECT_THAT(3, InClosedRange(4, 6)); +``` +결과적으로 위와 같이 description_string에 `""`를 전달해서 만든 matcher는 아래와 같은 출력문을 보여줄 것입니다. 이렇듯 출력문의 가독성을 높이기 위해서라도 matcher name을 신중하게 작성할 필요가 있습니다. +```bash + Expected: in closed range (4, 6) +``` + +마무리하면 `MATCHER_Pk ` macro는 parameter의 개수에 따라 아래와 같이 일반화된 형태로 나타낼수 있습니다. +```cpp +MATCHER_Pk(Foo, p1, ..., pk, description_string) { ... } +``` +단, 위의 macro는 축약형이며 실제로는 아래와 같은 모습이긴 합니다. +```cpp +template +FooMatcherPk +Foo(p1_type p1, ..., pk_type pk) { ... } +``` + +즉, 바로 위의 예제에서 정의된 matcher를 `Foo(v1, .., vk)`라는 사용한다고 가정하면 이 떄, `v1`, ... , `vk`의 타입은 일반적인 template function의 동작방식과 같이 compiler에 의해 추론됩니다. 만약, 이러한 타입추론이 불편한 경우에는 명시적으로 직접 지정해도 괜찮습니다. 예를 들면 `Foo(5, false)` 와 같이 구현하기만 하면 됩니다. 다만, 이것은 어디까지나 parameter type에 대한 얘기이며 argument type(`arg_type`)은 matcher를 사용하는 context에 따라 달라지게 됩니다. + +`Foo(p1, ..., pk)`와 같은 matcher의 결과를 반환할 떄는 `FooMatcherPk`을 사용해야 합니다. 이런 구현은 여러개의 matcher를 묶어서 사용할 떄 유용합니다. 단, parameter가 없거나 한 개만 있는 matcher는 조금 다르게 구현합니다. 예를 들어 parameter가 없는 matcher `Foo()`가 있다면 이것을 반환할때는 `FooMatcher`라는 타입으로 반환해야 합니다. 더불어 `Foo(p)`와 같이 parameter가 1개 있는 경우에는 `FooMatcherP`라는 타입으로 반환하면 됩니다. + +Matcher template을 참조타입 parameter로 초기화할 수도 있지만, 포인터 타입을 사용하는 것이 가독성 측면에서 더 좋습니다. 왜냐하면 참조타입으로 전달하게 되면 failure message가 출력되었을 때, 가리키는 대상의 주소는 볼 수 없고 값만 볼 수 있기 때문입니다. + +마지막으로 이미 눈치챘겠지만 matcher는 parameter의 개수에 따라 overloaded function으로 확장됩니다. + +```cpp +MATCHER_P(Blah, a, description_string_1) { ... } +MATCHER_P2(Blah, a, b, description_string_2) { ... } +``` + +지금까지 배운 내용을 보면 `MATCHER*` 매크로를 사용하는 것이 편리하고 유용한 방법인 것은 맞습니다. 하지만 matcher와 같은 새로운 기능을 구현해야 할 때, 가능한 여러가지 방법들을 고민해보고 적절한 방법을 선택하기 바랍니다. 특히, 다음에 설명하는 `MatcherInterface`, `MakePolymorphicMatcher()`도 괜찮을 방법들입니다. 물론 `MATCHER*`에 비해서 해야할 일이 늘어나긴 하지만 좀 더 세밀한 작업도 가능합니다. 예를 들어 타입지정을 다양하게 조절할 수 있고, compile error message가 좀 더 깔끔해집니다. 또한, matcher를 parameter 개수 뿐만 아니라 parameter 타입에 의해서 overload 할 수 있게 도와줍니다. + +#### 새로운 Monomorphic Matcher 구현하기 #### + +gMock에서 제공하는 `::testing::MatcherInterface`를 상속받고 구현하면 `T`타입의 argument를 받는 matcher를 만들 수 있습니다. 이렇게 만들어진 matcher는 2가지 일을 가능하게 합니다. 먼저, argument type(`T`)과 argument value를 같이 검사할 수 있게 됩니다. 다음으로는 출력문을 자유롭게 구현할 수 있습니다. 즉, expectation을 만족하지 못했을 때 error message에 어떤 값들을 비교했는지와 같은 정보를 보다 상세히 알려줄 수 있습니다. + +이를 위한 interface인 `MatcherInterface`는 아래처럼 선언되어 있습니다. + +```cpp +class MatchResultListener { + public: + ... + // Streams x to the underlying ostream; does nothing if the ostream + // is NULL. + template + MatchResultListener& operator<<(const T& x); + + // Returns the underlying ostream. + ::std::ostream* stream(); +}; + +template +class MatcherInterface { + public: + virtual ~MatcherInterface(); + + // Returns true iff the matcher matches x; also explains the match + // result to 'listener'. + virtual bool MatchAndExplain(T x, MatchResultListener* listener) const = 0; + + // Describes this matcher to an ostream. + virtual void DescribeTo(::std::ostream* os) const = 0; + + // Describes the negation of this matcher to an ostream. + virtual void DescribeNegationTo(::std::ostream* os) const; +}; +``` + +새로운 matcher를 만들고 싶은데 `Truly()`가 맘에 들지 않나요? (`Truly()`는 function이나 functor를 matcher로 변환해주는 기능입니다.) `Truly()`가 생성해주는 출력문으로 부족한가요? 앞으로는 2단계만 거치면 원하는 matcher를 좀 더 자유롭게 만들 수 있습니다. 첫번째 단계는 `MatcherInteface`를 상속받아 구현하는 것이고 두번째 단계는 해당 matcher의 instance를 생성하기 위한 factory function을 정의하는 것입니다. 여기서 두번째 단계는 matcher를 편리하게 사용하기 위해 필요한 것이지 꼭 해야하는 것도 아닙니다. + +그럼 예제를 보겠습니다. 아래코드는 어떤 `int`타입 값이 7로 나누어 떨어지는지 검사하는 matcher를 구현한 것입니다. + +```cpp +using ::testing::MakeMatcher; +using ::testing::Matcher; +using ::testing::MatcherInterface; +using ::testing::MatchResultListener; + +class DivisibleBy7Matcher : public MatcherInterface { + public: + bool MatchAndExplain(int n, + MatchResultListener* /* listener */) const override { + return (n % 7) == 0; + } + + void DescribeTo(::std::ostream* os) const override { + *os << "is divisible by 7"; + } + + void DescribeNegationTo(::std::ostream* os) const override { + *os << "is not divisible by 7"; + } +}; + +Matcher DivisibleBy7() { + return MakeMatcher(new DivisibleBy7Matcher); +} + +... + EXPECT_CALL(foo, Bar(DivisibleBy7())); +``` + +위의 예제코드에서 보이듯이 출력문은 기본적으로 `Describe*` 함수에 구현합니다. 그러나 `MatchAndExplain()`에서 직접 출력문 내용을 변경하고 싶을 수도 있습니다. 그런 경우에는 `listener` argument를 사용하면 출력문에 내용을 추가할 수 있습니다. 예제코드는 아래와 같습니다. + +```cpp +class DivisibleBy7Matcher : public MatcherInterface { + public: + bool MatchAndExplain(int n, + MatchResultListener* listener) const override { + const int remainder = n % 7; + if (remainder != 0) { + *listener << "the remainder is " << remainder; + } + return remainder == 0; + } + ... +}; +``` + +최종적으로 `EXPECT_THAT(x, DivisibleBy7());` 은 아래와 같은 결과를 출력할 것입니다. + +```bash +Value of: x +Expected: is divisible by 7 + Actual: 23 (the remainder is 2) +``` + +#### 새로운 Polymorphic Matcher 구현하기 #### + +새로운 matcher를 만드는 방법들을 계속 배웠습니다. 하지만 문제는 `MakeMatcher()`를 통해 만들었던 matcher들은 한가지 타입에만 사용이 가능하다는 것입니다. `Eq(x)`처럼 어떤 타입에도 사용가능한(`==`연산자가 구현되어 있는) matcher는 어떻게 만들 수 있을까요? 이제부터는 _polymorphic_ matcher를 정의함으로써 다양한 타입에 사용가능한 matcher를 만들어 보도록 하겠습니다. 관련 기술들이 `"testing/base/public/gmock-matchers.h"`에 구현되어 있으나 내용이 조금 어려울 수 있습니다. + +가장 쉬운 방법은 `MakePolymorphicMatcher()`를 사용하는 것입니다. 아래에 나오는 `NotNull()`이라는 polymorphic matcher 예제를 참조하시기 바랍니다. + +```cpp +using ::testing::MakePolymorphicMatcher; +using ::testing::MatchResultListener; +using ::testing::PolymorphicMatcher; + +class NotNullMatcher { + public: + // To implement a polymorphic matcher, first define a COPYABLE class + // that has three members MatchAndExplain(), DescribeTo(), and + // DescribeNegationTo(), like the following. + + // In this example, we want to use NotNull() with any pointer, so + // MatchAndExplain() accepts a pointer of any type as its first argument. + // In general, you can define MatchAndExplain() as an ordinary method or + // a method template, or even overload it. + template + bool MatchAndExplain(T* p, + MatchResultListener* /* listener */) const { + return p != NULL; + } + + // Describes the property of a value matching this matcher. + void DescribeTo(::std::ostream* os) const { *os << "is not NULL"; } + + // Describes the property of a value NOT matching this matcher. + void DescribeNegationTo(::std::ostream* os) const { *os << "is NULL"; } +}; + +// To construct a polymorphic matcher, pass an instance of the class +// to MakePolymorphicMatcher(). Note the return type. +PolymorphicMatcher NotNull() { + return MakePolymorphicMatcher(NotNullMatcher()); +} + +... + + EXPECT_CALL(foo, Bar(NotNull())); // The argument must be a non-NULL pointer. +``` + +**Note:** polymorphic matcher class를 구현할 때는 `MatcherInterface`를 비롯해 어떤 class도 상속받지 않습니다. (monomorphic matcher와 다른 점입니다.) 그리고 method를 virtual method로 선언할 필요도 없습니다. + +그럼에도 monomorphic matcher처럼 `MatchAndExplain()`의 argument인 `listener`를 통해 출력문 내용을 추가하는 것도 가능합니다. + +#### 새로운 Cardinality 구현하기 #### + +Cardinality(호출횟수)는 `Times()`와 함께 사용되며 gMock에 mock function이 몇 번 호출되기를 기대하는지 알려주기 위한 용도로 사용합니다. 다만, 꼭 정확한 횟수를 지정해야 하는 것은 아닙니다. 예를 들어 `AtLeast(5)` 또는 `Between(2, 4)`와 같이 특정범위를 지정하는 cardinality도 제공하고 있습니다. + +혹시, 기본제공되는 built-in cardinality가 부족하다고 생각된다면 직접 정의하는 것도 가능합니다. 이를 위해서는 `testing` namespace에 있는 아래 interface를 구현하면 됩니다. + +```cpp +class CardinalityInterface { + public: + virtual ~CardinalityInterface(); + + // Returns true iff call_count calls will satisfy this cardinality. + virtual bool IsSatisfiedByCallCount(int call_count) const = 0; + + // Returns true iff call_count calls will saturate this cardinality. + virtual bool IsSaturatedByCallCount(int call_count) const = 0; + + // Describes self to an ostream. + virtual void DescribeTo(::std::ostream* os) const = 0; +}; +``` + +아래 코드는 `CardinalityInterface`를 상속받은 후에 짝수단위로만 호출되기를 원하는 cardinality를 구현한 것입니다. + +```cpp +using ::testing::Cardinality; +using ::testing::CardinalityInterface; +using ::testing::MakeCardinality; + +class EvenNumberCardinality : public CardinalityInterface { + public: + bool IsSatisfiedByCallCount(int call_count) const override { + return (call_count % 2) == 0; + } + + bool IsSaturatedByCallCount(int call_count) const override { + return false; + } + + void DescribeTo(::std::ostream* os) const { + *os << "called even number of times"; + } +}; + +Cardinality EvenNumber() { + return MakeCardinality(new EvenNumberCardinality); +} + +... + EXPECT_CALL(foo, Bar(3)) + .Times(EvenNumber()); +``` + +#### 새로운 Action을 빠르게 구현하기 + +Built-in action만으로 부족하다고 느끼는 사용자가 있다면 action을 직접 구현하는 것도 어렵지 않습니다. Action의 signature와 매칭되는 functor class를 정의하면 됩니다. (template도 가능합니다.) + +```c++ +struct Increment { + template + T operator()(T* arg) { + return ++(*arg); + } +} +``` + +같은 방법으로 stateful functor를 구현하는 것도 가능합니다. + +```c++ +struct MultiplyBy { + template + T operator()(T arg) { return arg * multiplier; } + + int multiplier; +} + +// Then use: +// EXPECT_CALL(...).WillOnce(MultiplyBy{7}); +``` + +##### Legacy : 새로운 Action를 빠르게 구현하기 ##### + +C++11 이전에는 functor를 기반으로 한 action을 지move-only원하지 않았으며 주로 `ACTION*`이라는 macro를 통해서 action을 정의할 수 있었습니다. 다만, 이제는 새로운 기능으로 대부분 대체되었습니다. 물론 사용할 수는 있지만 언젠가 compile error가 발생할 수도 있으며 되도록이면 새로운 방법을 사용하기를 권장합니다. 대체된 기능이긴 하지만 `ACTION*` macro의 사용자를 위해 관련 내용을 공유합니다. + +먼저, `ACTION*` macro는 아래와 같은 모습을 가지고 있습니다. + +```cpp +ACTION(name) { statements; } +``` +어떤 namespace 내에서 (class, function 내부는 안됩니다) 위의 macro를 사용하면, `name`이라는 이름을 가지면서 `statement`코드를 수행하는 action을 생성해줍니다. 여기서 `statement`의 반환값은 곧 action의 반환값이 됩니다. 또한, `statement`를 구현할 때, mock function의 argument를 참조할 수도 있습니다. 이를 위해서 gMock에서는 mock function의 K번째 인수라는 의미로 `argK`를 제공하고 있습니다. 예를 들어 아래와 같습니다. + +```cpp +ACTION(IncrementArg1) { return ++(*arg1); } +``` +이렇게 정의된 action은 built-in action과 동일한 방식으로 사용하면 됩니다. +```cpp +... WillOnce(IncrementArg1()); +``` + +여기서는 mock function으로부터 전달되는 argument type을 따로 명세하지는 않고 있습니다. 그렇지만 어떤 타입이 전달되든지 코드는 type-safe 합니다. 왜냐하면 예제와 같은 코드는 `*arg1`가 `++` 연산자를 지원하지 않는다면 compile error가 발생할 것이기 때문입니다. 또한 `++(*arg1)`이라는 코드가 mock function의 return type과 맞지 않아도 compile error가 발생할 것입니다. + +또 다른 예제를 보면 좀 더 익숙해 질 것입니다. +```cpp +ACTION(Foo) { + (*arg2)(5); + Blah(); + *arg1 = 0; + return arg0; +} +``` +위 코드는 먼저 `Foo()`라는 action을 정의합니다. `Foo`는 mock function의 2번째 argument를 받아서 호출합니다. 여기서 2번째 argument는 function pointer이기 때문에 호출이 가능합니다. 계속해서 `Blah()`를 호출하고 1번째 argument에는 0을 대입합니다. 그리고 마지막으로 0번째 argument인 `arg0`을 반환합니다. + +`ACTION` macro를 좀 더 편리하게 사용하기 위해서 아래와 같은 pre-defined symbol을 알아두면 편리합니다. 이들은 action statement 안에서 자유롭게 사용할 수 있습니다. + +| `argK_type` | The type of the K-th (0-based) argument of the mock function | +| :-------------- | :----------------------------------------------------------- | +| `args` | All arguments of the mock function as a tuple | +| `args_type` | The type of all arguments of the mock function as a tuple | +| `return_type` | The return type of the mock function | +| `function_type` | The type of the mock function | + +아래와 같은 mock function을 위한 action을 구현할 때 + +```cpp +int DoSomething(bool flag, int* ptr); +``` +`ACTION` macro의 `statement`에서 사용가능한 pre-defined symbol들과 그 값은 아래와 같습니다. + +| **Pre-defined Symbol** | **Is Bound To** | +| :--------------------- | :-------------------------------------- | +| `arg0` | the value of `flag` | +| `arg0_type` | the type `bool` | +| `arg1` | the value of `ptr` | +| `arg1_type` | the type `int*` | +| `args` | the tuple `(flag, ptr)` | +| `args_type` | the type `::testing::tuple` | +| `return_type` | the type `int` | +| `function_type` | the type `int(bool, int*)` | + +##### Legacy : 새로운 Parameterized Action을 빠르게 구현하기 ##### + +Action이 parameter를 받을 수 있도록 구현해야 할 필요도 있을 것입니다. 이를 위해서 `ACTION_P*` 계열 매크로를 제공하고 있습니다. + +```cpp +ACTION_P(name, param) { statements; } +``` + +사용방법은 아래와 같습니다. +```cpp +ACTION_P(Add, n) { return arg0 + n; } +``` +이제 `Add`라는 action이 parameter를 전달받을 수 있게 되었습니다. +```cpp +// Returns argument #0 + 5. +... WillOnce(Add(5)); +``` + +인수(_argument_)라는 용어와 parameter가 조금 헷갈릴 수 있습니다. 정리하자면 인수는 mock function이 전달받는 것들입니다. 필요에 따라 action으로 전달할 수도 있고, 안하는 것도 가능한 경우입니다. 다음으로 parameter는 mock function에서 전달되는게 아니라 테스트코드에서 action으로 직접 전달되는 것들을 의미합니다. + +사용자가 parameter type을 직접 제공할 필요는 없습니다. 예를 들어서 사용자가 `ACTION_P*` 계열을 이용해 `param`이라는 parameter를 정의했다면 `param`의 타입은 컴파일러에 의해 추론되며 이렇게 추론된 것을 다시 gMock에서 `param_type`이라는 명칭으로 제공하여 사용자가 사용할 수 있도록 합니다. 위에서 `ACTION_P(Add, n)` 이라고 구현한 코드에서 `n`의 타입은 컴파일러와 gMock에 의해 `n_type`이라는 변수로 사용자에게 제공됩니다. + + +또한, `ACTION_P2`, `ACTION_P3`와 같이 parameter의 개수에 따라 다른 매크로를 사용할 수 있습니다. + +```cpp +ACTION_P2(ReturnDistanceTo, x, y) { + double dx = arg0 - x; + double dy = arg1 - y; + return sqrt(dx*dx + dy*dy); +} +``` +위에서 정의한 action은 아래처럼 사용하면 됩니다. +```cpp +... WillOnce(ReturnDistanceTo(5.0, 26.5)); +``` + +즉, action을 생성을 돕는 가장 기본매크로인 `ACTION`은 parameter의 개수가 하나도 없는 parameterized action 이라고 봐도 무방할 것입니다. + +이렇게 parameterized action은 내부적으로 overloaded function이 된다는 점도 알아두시기 바랍니다. + +```cpp +ACTION_P(Plus, a) { ... } +ACTION_P2(Plus, a, b) { ... } +``` + +#### ACTION 으로 전달되는 인수나 파라미터의 타입을 제한하기 #### + +최대한 간결하고 재사용성을 높이기 위해서 `ACTION*` macro는 mock function의 argument type과 action parameter를 요구하지 않습니다. 대신, compiler가 그런 타입을 추론할 수 있도록 구현해 두었습니다. + +다만, 모호한 경우 등을 대비해 argument type을 명시적으로 표현하는 방법도 제공하고 있습니다. 아래 예제를 확인하세요. + +```cpp +ACTION(Foo) { + // Makes sure arg0 can be converted to int. + int n = arg0; + ... use n instead of arg0 here ... +} + +ACTION_P(Bar, param) { + // Makes sure the type of arg1 is const char*. + ::testing::StaticAssertTypeEq(); + + // Makes sure param can be converted to bool. + bool flag = param; +} +``` +위의 코드는 첫번째 argument의 타입을 의미하는 `arg1_type`를 통해서 action의 시작부분에서 타입을 확인하고 있습니다. 이를 위해서 `StaticAssertTypeEq`라는 googletest의 타입비교 기능을 사용할 수 있습니다. (compile time에 비교를 수행합니다.) + +#### 새로운 Action Template을 빠르게 구현하기 #### + +Action을 정의할 때, action으로 전달되는 parameter의 타입을 값으로부터 추론하기 어려운 경우가 있습니다. 이런 경우에는 template parameter을 통해 명시적으로 parameter의 타입을 지정해야 합니다. `ACTION_TEMPLATE()`이 이러한 기능을 지원합니다. 이름에서 알 수 있듯이 `ACTION()`과 `ACTION_P*()`의 확장판이라고 생각하면 됩니다. + +```cpp +ACTION_TEMPLATE(ActionName, + HAS_m_TEMPLATE_PARAMS(kind1, name1, ..., kind_m, name_m), + AND_n_VALUE_PARAMS(p1, ..., p_n)) { statements; } +``` + +이 macro를 통해 생성되는 action은 _m_개의 template parameter와 _n_개의 value parameter를 전달 받습니다. 여기서 _m_의 범위는 1~10까지이고, _n_의 범위는 0~10까지로 약간 다름에 유의하십시오. 다음으로 `name_i`는 i번째 template parameter를 의미하며 `kind_i`는 곧 해당 template parameter의 타입을 의미합니다. `p_i`는 i번째 value parameter이며 이미 말한것처럼 `p_i`의 타입은 compiler에 의해 추론될 것이기 때문에 `kind_i`처럼 명시적으로 적는 부분은 없습니다. + +예제를 보겠습니다. + +```cpp +// DuplicateArg(output) converts the k-th argument of the mock +// function to type T and copies it to *output. +ACTION_TEMPLATE(DuplicateArg, + // Note the comma between int and k: + HAS_2_TEMPLATE_PARAMS(int, k, typename, T), + AND_1_VALUE_PARAMS(output)) { + *output = T(::std::get(args)); +} +``` + +이제 위에서 만든 action template을 사용하려면, 아래처럼 instance를 생성하면 됩니다. + +```cpp +ActionName(v1, ..., v_n) +``` + `t`는 template argument이고 `v`는 value argument 입니다. + +```cpp +using ::testing::_; +... + int n; + EXPECT_CALL(mock, Foo).WillOnce(DuplicateArg<1, unsigned char>(&n)); +``` + +Value argument의 타입은 compiler가 추론한다고 말했지만, 만약 value argument의 타입도 명시적으로 지정하고 싶은 사용자가 있다면 아래와 같이 template argument에 관련내용을 추가할 수 있습니다. + +```cpp +ActionName(v1, ..., v_n) +``` +`u_i`는 `v_i`의 타입을 의미합니다. (예상되는 타입) + +`ACTION_TEMPLATE`과 `ACTION / ACTION_P*`는 value parameter의 개수에 따라서 overloading 될 것입니다. + +```cpp + OverloadedAction(x); +``` + +위 코드를 overloading 할 때, template parameter의 개수가 아닌 value parameter의 개수에 따라 overloading함을 주의하십시오. 왜냐하면 template parameter를 기준으로 하면 아래와 같이 2가지 해석이 가능해지므로 문제가 됩니다. 즉, 모호함이 발생할 수 있으므로 안됩니다. + +* template parameter 1개 (`int`) + value paramter 1개 (`bool x`) +* template parameter 2개 (`int`, `bool`) + value parameter 1개 (임의의 타입 `x` , 컴파일러가 나중에 추론) + +#### ACTION Object의 타입 확인하기 #### + +만약, `ACTION` object를 반환하는 function을 구현하려 한다면, 해당 `ACTION` object의 타입을 알아야만 합니다. 이러한 타입은 어떤 macro를 사용했는지에 따라 달라집니다. 다행히도 그 규칙은 비교적 간단하며 아래 표에서 확인할 수 있습니다. + +| **Given Definition** | **Expression** | **Has Type** | +| :----------------------------------------------------------- | :----------------------------------------- | :------------------------------------- | +| `ACTION(Foo)` | `Foo()` | `FooAction` | +| `ACTION_TEMPLATE(Foo, HAS_m_TEMPLATE_PARAMS(...), AND_0_VALUE_PARAMS())` | `Foo()` | `FooAction` | +| `ACTION_P(Bar, param)` | `Bar(int_value)` | `BarActionP` | +| `ACTION_TEMPLATE(Bar, HAS_m_TEMPLATE_PARAMS(...), AND_1_VALUE_PARAMS(p1))` | `Bar(int_value)` | `FooActionP` | +| `ACTION_P2(Baz, p1, p2)` | `Baz(bool_value, int_value)` | `BazActionP2` | +| `ACTION_TEMPLATE(Baz, HAS_m_TEMPLATE_PARAMS(...), AND_2_VALUE_PARAMS(p1, p2))` | `Baz(bool_value, int_value)` | `FooActionP2` | +| ... | ... | ... | + +위 표를 보면 `ACTION`, `ACTION_P`, `ACTION_P2`와 같이 macro의 suffix들이 서로 다릅니다. 이 때, macro의 이름은 action으로 전달되는 parameter 개수로 구분되며 overload를 방지하는 효과가 있습니다. + +#### 새로운 Monomorphic Action 구현하기 #### + +`ACTION*` macro는 편리하지만, 사용하기에 적절하지 않은 경우도 있습니다. 왜냐하면`ACTION*` macro를 사용하면 mock function argument와 action parameter를 직접적으로 지정할 수가 없습니다. 이런 이유로 인해 compile error가 발생하기도 하며 사용자를 혼란스럽게 합니다. 또한, `ACTION*` macro만 가지고는 action을 overloading 하기가 상당히 까다롭습니다. (할 수는 있지만) + +이런 상황에서는 `::testing::ActionInterface`를 사용하는 것도 괜찮은 방법입니다. 여기서 `F`는 action을 사용하게 될 mock function의 function type입니다. 아래 예제를 통해 확인하세요. + +```c++ +template +class ActionInterface { + public: + virtual ~ActionInterface(); + + // Performs the action. Result is the return type of function type + // F, and ArgumentTuple is the tuple of arguments of F. + // + + // For example, if F is int(bool, const string&), then Result would + // be int, and ArgumentTuple would be ::std::tuple. + virtual Result Perform(const ArgumentTuple& args) = 0; +}; +``` + +```cpp +using ::testing::_; +using ::testing::Action; +using ::testing::ActionInterface; +using ::testing::MakeAction; + +typedef int IncrementMethod(int*); + +class IncrementArgumentAction : public ActionInterface { + public: + int Perform(const ::std::tuple& args) override { + int* p = ::std::get<0>(args); // Grabs the first argument. + return *p++; + } +}; + +Action IncrementArgument() { + return MakeAction(new IncrementArgumentAction); +} + +... + EXPECT_CALL(foo, Baz(_)) + .WillOnce(IncrementArgument()); + + int n = 5; + foo.Baz(&n); // Should return 5 and change n to 6. +``` + +#### 새로운 Polymorphic Action 구현하기 #### + +바로 위에서 action을 직접 구현하는 방법에 대해 배웠습니다. 다만, 그건 어디까지나 mock function의 function type을 알고 있을 때만 가능한 방법입니다. 그렇다면 `Return()`, `SetArgPointee()`처럼 *여러가지 타입*에 적용할 수 있는 action을 만들기 위해서는 어떻게 하면 될까요? + +Action을 여러가지 mock function에 사용하기 위해서는(즉, *polymorphic action*을 만들기 위해서는) template function인 `MakePolymorphicAction()`을 사용해야 합니다. 사용법은 어렵지 않습니다. + +```cpp +namespace testing { +template +PolymorphicAction MakePolymorphicAction(const Impl& impl); +} // namespace testing +``` + +이제 `MakePolymorphicAction()`이 사용할 action 만들어 보겠습니다. 예를 들어 mock function의 argument들 중에서 2번째 argument를 반환하는 action을 만들려고 합니다. 제일 먼저 해당 implementation class를 구현합니다. + +```cpp +class ReturnSecondArgumentAction { + public: + template + Result Perform(const ArgumentTuple& args) const { + // To get the i-th (0-based) argument, use ::std::get(args). + return ::std::get<1>(args); + } +}; +``` + +위의 class는 monomorphic action과는 다르게 어떤 class로부터도 상속받지 않았습니다. 여기서 중요한 점은 `Perform()`이라는 template method는 꼭 구현해야 한다는 것입니다. `Perform()`은 mock function의 argument를 전부 모아서 tuple 형태의 변수 **하나**로 전달받게 됩니다. 그런 다음 action의 결과를 반환합니다. 이 function는 `const`로 만들어도 되고 꼭 아니어도 괜찮습니다. 다만 template argument들을 잘 맞춰줘야 합니다. 즉, `R`이 mock function의 return type이고 `args`가 mock function의 argument들일 때, `Perform(args)`이라는 명령으로 호출될 수 있어야 합니다. + +이제 `MakePolymorphicAction()`을 통해서 바로 위에서 만든 class를 polymorphic action으로 사용할 것입니다. 이름이 좀 일반적이니 적절하게 wrapper로 감싸주면 사용하기가 더 편리할 것입니다. 아래처럼 해줍니다. + +```cpp +using ::testing::MakePolymorphicAction; +using ::testing::PolymorphicAction; + +PolymorphicAction ReturnSecondArgument() { + return MakePolymorphicAction(ReturnSecondArgumentAction()); +} +``` + +모두 끝났습니다. `ReturnSecondArgument()`를 built-in action과 똑같은 방법으로 사용하기만 하면 됩니다. + +```cpp +using ::testing::_; + +class MockFoo : public Foo { + public: + MOCK_METHOD(int, DoThis, (bool flag, int n), (override)); + MOCK_METHOD(string, DoThat, (int x, const char* str1, const char* str2), + (override)); +}; + + ... + MockFoo foo; + EXPECT_CALL(foo, DoThis).WillOnce(ReturnSecondArgument()); + EXPECT_CALL(foo, DoThat).WillOnce(ReturnSecondArgument()); + ... + foo.DoThis(true, 5); // Will return 5. + foo.DoThat(1, "Hi", "Bye"); // Will return "Hi". +``` + +#### gMock이 사용자타입 정보도 출력 가능하게 만들기 #### + +gMock은 uninteresting call이나 unexpected call이 발생하면 해당 mock function으로 전달된 argument와 stack trace 정보를 출력해 줍니다. 마찬가지로 `EXPECT_THAT`, `EXPECT_EQ`과 같은 macro도 assertion 실패 시에 관련 정보를 출력해 줍니다. googletest와 gMock은 user-extensible value printer를 통해 이러한 동작을 구현하고 있습니다. + +다만, 위의 printer가 출력할 수 있는 대상은 built-in C++ type, array, STL container, 그리고 `<<` 연산자를 정의한 타입들만 해당됩니다. 다시말하면 그 외의 사용자정의 타입들은 관련정보를 출력할 수 없으며 단순히 byte dump만 출력하도록 구현되어 있습니다. 이러한 문제를 개선하려면 [googletests`s advanced guide](../../googletest/docs/advanced.md#teaching-googletest-how-to-print-your-values)를 참고하여 더 많은 정보를 출력할 수 있도록 변경할 수 있습니다. + +### Useful Mocks Created Using gMock + +#### std::function Mocking하기 + +C++11부터는 general function type으로 `std::function`을 사용할 수 있게 되었습니다. 또한, general function type은 callback agrument에 사용해도 좋기 때문에 많은 개발자들이 사용하고 있습니다. 이렇게 callback argument로 사용할 때 pointer형태로 주고받게 되면 mocking하기가 약간 까다로운 부분이 있었는데, 이제는 그런 두려움을 가지지 않아도 됩니다. gMock의 `MockFunction`을 사용하면 이러한 어려움을 극복할 수 있습니다. + +먼저, `MockFunction`은 `Call()`이라는 mock method를 가지고 있습니다. + +```c++ + R Call(T1, ..., Tn); +``` + +또한, `std::function` proxy롤 생성해주는 `AsStdFunction()`이라는 method도 가지고 있습니다. + +```c++ + std::function AsStdFunction(); +``` + +`MockFunction`을 사용하기 위해서는 먼저 `MockFunction` object를 생성하고 `Call()`을 통해서 expectation을 지정합니다. 그 다음에 `AsStdFunction()`을 통해서 `std::function`의 proxy를 확보하고 이를 테스트 대상코드에 전달하면 됩니다. 예제코드는 아래와 같습니다. + +```cpp +TEST(FooTest, RunsCallbackWithBarArgument) { + // 1. Create a mock object. + MockFunction mock_function; + + // 2. Set expectations on Call() method. + EXPECT_CALL(mock_function, Call("bar")).WillOnce(Return(1)); + + // 3. Exercise code that uses std::function. + Foo(mock_function.AsStdFunction()); + // Foo's signature can be either of: + // void Foo(const std::function& fun); + // void Foo(std::function fun); + + // 4. All expectations will be verified when mock_function + // goes out of scope and is destroyed. +} +``` + +`AsStdFunction()`를 통해서 생성한 function object는 `mock_function`의 proxy일 뿐입니다. 따라서 여러번 생성한다고 해서 별도의 expectation을 새로 만드는 것이 아님을 유의하시기 바랍니다. Proxy가 아무리 여러번 생성되어도 결국엔 `EXPECT_CALL(mock_function, Call("bar")).WillOnce(Return(1));`을 사용하게 됩니다. + +C++의 `std::function`에는 argument 개수에 제한이 없지만 `MockFunction`은 10개로 제한하고 있다는 점도 유의하시기 바랍니다. diff --git a/googlemock/docs/kr/for_dummies.md b/googlemock/docs/kr/for_dummies.md new file mode 100644 index 00000000..983fd617 --- /dev/null +++ b/googlemock/docs/kr/for_dummies.md @@ -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]()를 참조하세요. diff --git a/googlemock/docs/kr/gmock_faq.md b/googlemock/docs/kr/gmock_faq.md new file mode 100644 index 00000000..9e63ced3 --- /dev/null +++ b/googlemock/docs/kr/gmock_faq.md @@ -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를 제거하고 다시 시도해보기 바랍니다. diff --git a/googletest/docs/kr/advanced.md b/googletest/docs/kr/advanced.md new file mode 100644 index 00000000..decaa540 --- /dev/null +++ b/googletest/docs/kr/advanced.md @@ -0,0 +1,1941 @@ +# Advanced googletest Topics + +## 소개 + +이 문서를 읽기 전에 [googletest primer](primer.md)를 먼저 보시기 바랍니다. Googletest primer를 다 읽으셨다면 이제 다양한 심화기능을 공부 할 시간입니다. 이 문서는 googletest의 다양한 assertion 사용법, 사용자정의 failure message 만들기, fatal failure 전파하기, test fixture를 빠르고 재사용하기 쉽게 만들기, test program실행시 flag사용법과 같은 여러가지 주제를 포함하고 있습니다. + +## Googletest가 지원하는 다양한 Assertion 소개 및 사용법 + +자주 사용되지는 않지만, 여전히 중요하고 또 필요한 여러가지 assertion들을 소개하려 합니다. + +### 성공과 실패 명시하기 + +`SUCCEED()`, `FAIL()`, `ADD_FAILURE()`라는 assertion들은 value나 expression의 판정하지는 않지만 성공과 실패를 명시적으로 나타내기 위해 사용합니다. 다른 assertion macro들처럼 failure message를 변경하는 것도 가능합니다. + +```c++ +SUCCEED(); +``` + +위의 코드는 테스트가 성공했음을 의미합니다. 단, 실제로 해당 테스트가 **성공했음을 의미하는 것은 아닙니다.** 코드상에서 명시적으로 테스트가 여기까지 진행되었다면 성공이다라는 걸 의미할 뿐이며 해당 테스트가 다른 assertion으로 인해 실패했을 때는 그대로 실패입니다. 즉, 테스트결과에 실제로 영향을 주거나 하지는 않습니다. + +NOTE: `SUCCEED()`는 코드상에서 추가적인 정보를 전달합니다. 그 외에 사용자가 확인할 수 있는 출력물을 제공하거나 하지는 않습니다. 추후에 변경될 수는 있지만 현재로서는 그렇습니다. + +```c++ +FAIL(); +ADD_FAILURE(); +ADD_FAILURE_AT("file_path", line_number); +``` + +`FAIL()`은 fatal failure를 발생시키며 `ADD_FAILURE()` 와 `ADD_FAILURE_AT()` 는 non-fatal failure를 발생시킵니다. 바로 위에서 설명한 `SUCCEED()`는 결과에 영향을 주지 않지만, `FAIL()`, `ADD_FAILURE*()`는 실제로 해당 테스트를 실패로 판정합니다. 제어흐름상에서 불필요한 부분을 표현하는데 유용합니다. 예를 들어 아래와 같은 코드에 사용할 수 있습니다. + +```c++ +switch(expression) { + case 1: + ... some checks ... + case 2: + ... some other checks ... + default: + FAIL() << "We shouldn't get here."; +} +``` + +NOTE: `FAIL()`은 return type이 `void`인 function에만 사용할 수 있습니다. 이와 관련한 자세한 내용은 [Assertion Placement section](#assertion-placement)를 참조하세요. + +### Exception Assertions + +아래 assertion들은 주어진 코드(`statement`)가 exception를 던지는지 아닌지를 검증하는데 사용합니다. + +| Fatal assertion | Nonfatal assertion | Verifies | +| ------------------------------------------ | ------------------------------------------ | ------------------------------------------------- | +| `ASSERT_THROW(statement, exception_type);` | `EXPECT_THROW(statement, exception_type);` | `statement` throws an exception of the given type | +| `ASSERT_ANY_THROW(statement);` | `EXPECT_ANY_THROW(statement);` | `statement` throws an exception of any type | +| `ASSERT_NO_THROW(statement);` | `EXPECT_NO_THROW(statement);` | `statement` doesn't throw any exception | + +예제코드: + +```c++ +ASSERT_THROW(Foo(5), bar_exception); + +EXPECT_NO_THROW({ + int n = 5; + Bar(&n); +}); +``` + +**Availability**: 기본적으로 빌드시에 exception이 활성화되어 있어야 동작합니다. + +### Predicate Assertion을 통해 더 유용한 Error Message 만들기 + +지금까지 확인한 것처럼 googletest는 다양한 assertion들을 제공하지만 여전히 사용자가 원하는 모든 경우를 커버하지는 않습니다. 또 그렇게 모든 상황에 대한 assertion을 제공하는 것이 좋다고 하기도 좀 어렵습니다. 사실 googletest에서 제공하는 assertion들만 가지고 원하는 목적을 달성하려면 `EXPECT_TRUE()`와 같은 단순한 assertion에 어쩔 수 없이 복잡한 expression을 사용하기도 합니다. 복잡한 expression을 사용한다고 해서 꼭 문제가 되는 것은 아니지만 불편하긴 합니다. 예를 들어 expression이 복잡하면 기본적으로 제공되는 failure message만으로는 부족할 수도 있습니다. 그렇다보니 사용자가 직접 failure message를 변경하게 되는데 만약 expression이 side-effect(부수효과)를 포함하고 있다면 문제가 되기도 합니다. + +Googletest는 위와 같은 상황에서 유용하게 사용할 수 있는 몇 가지 방법을 제공합니다. + +#### 이미 정의해 놓은 Boolean Function 사용하기 + +만약, `bool`을 반환하는 function나 functor가 이미 있다면 그대로 사용할 수 있습니다. 즉, 해당 function이나 functor가 *assertion*처럼 동작하게 만들고 원하는 failure message를 출력하도록 지정하면 됩니다. 이러한 기능을 *predicate assertion*이라고 합니다. + +| Fatal assertion | Nonfatal assertion | Verifies | +| ---------------------------------- | ---------------------------------- | --------------------------- | +| `ASSERT_PRED1(pred1, val1);` | `EXPECT_PRED1(pred1, val1);` | `pred1(val1)` is true | +| `ASSERT_PRED2(pred2, val1, val2);` | `EXPECT_PRED2(pred2, val1, val2);` | `pred2(val1, val2)` is true | +| `...` | `...` | `...` | + +위에서 `predn` 은 `n`개의 argument(`val1`, `val2`, ...)를 받는 function이나 functor를 의미합니다. 이렇게 정의된 predicate assertion도 다른 assertion과 동일하게 동작합니다. 즉, `true`가 반환되면 성공을 의미하고 `false`가 반환되면 실패를 의미합니다. 그리고 assertion이 실패할 때는 각 argument의 정보도 출력해줍니다. + +아래는 예제를 보겠습니다. + +```c++ +// Returns true if m and n have no common divisors except 1. +bool MutuallyPrime(int m, int n) { ... } + +const int a = 3; +const int b = 4; +const int c = 10; +``` + +전달된 argument 2개(`m`, `n`)에 공약수가 1밖에 없으면 `true`를 반환하는 `MutuallyPrime()`이라는 function이 정의되어 있습니다. 이제 아래와 같이 구현하면 해당 function을 assertion처럼 사용할 수 있습니다. + +```c++ + EXPECT_PRED2(MutuallyPrime, a, b); +``` + +위 코드에서 `a`, `b` 는 1 외에는 공약수가 없으므로 아래 assertion은 성공합니다. + +다음으로 아래에 있는 assertion은 `b`, `c`가 1 외에도 공약수(2)를 가지므로 실패합니다. + +```c++ + EXPECT_PRED2(MutuallyPrime, b, c); +``` + + 그리고 아래와 같은 failure message를 출력해줍니다. + +```none +MutuallyPrime(b, c) is false, where +b is 4 +c is 10 +``` + +> NOTE: +> +> 1. `ASSERT_PRED*` 또는 `EXPECT_PRED*`를 사용할 때, "no matching function to call"와 같은 컴파일 에러가 발생하면 [여기](faq.md#the-compiler-complains-no-matching-function-to-call-when-i-use-assert_pred-how-do-i-fix-it)를 참조하세요. + +#### AssertionResult를 반환하는 function을 사용하기 + +`EXPECT_PRED*()`계열이 유용하지만 argument개수에 따라 macro가 달라지기 때문에 약간 불편한 부분도 있습니다. C++ 문법보다는 Lisp 문법에 가까워 보이기도 하네요. 또 다른 방법으로 `::testing::AssertionResult` class를 사용해 보겠습니다. + +`AssertionResult`는 이름 그대로 assertion을 결과를 의미합니다. 사용법은 간단합니다. Googletest에서 제공하는 factory function을 사용하면 됩니다. Factory function 2개는 아래와 같습니다. + +```c++ +namespace testing { + +// Returns an AssertionResult object to indicate that an assertion has +// succeeded. +AssertionResult AssertionSuccess(); + +// Returns an AssertionResult object to indicate that an assertion has +// failed. +AssertionResult AssertionFailure(); + +} +``` + +더불어 `AssertionResult` object에 `<<` 연산자를 구현하면 stream message를 변경할 수도 있습니다. + +사용방법은 `bool MutuallyPrime()`를 predicate assertion으로 사용했던 것과 동일합니다. 다만, function의 return type에 `bool`이 아니라 `AssertionResult`을 사용해야 합니다. 먼저 아래와 같이 `n`이 짝수인지 아닌지 판정하는 `IsEven()`이라는 function이 있다고 가정해 보겠습니다. + +```c++ +bool IsEven(int n) { + return (n % 2) == 0; +} +``` + +`IsEven()`을 `AssertionResult`를 반환할 수 있도록 변경합니다. + +```c++ +::testing::AssertionResult IsEven(int n) { + if ((n % 2) == 0) + return ::testing::AssertionSuccess(); + else + return ::testing::AssertionFailure() << n << " is odd"; +} +``` + +모두 준비가 끝났습니다. 이제 기존 assertion과 함께 사용하기만 하면 됩니다. + +예를 들어 `EXPECT_TRUE(IsEven(Fib(4)))`와 같이 사용하면 됩니다. 만약 실패하면 아래와 같은 failure message를 출력해 줄 것입니다. "3 is odd" 라는 추가 정보가 출력된 부분을 주목하십시오. 이렇듯 간단하게 디버깅에 필요한 더 많은 정보를 제공할 수 있습니다. + +```none +Value of: IsEven(Fib(4)) + Actual: false (3 is odd) +Expected: true +``` + +위의 failure message가 왜 좋을까요? 만약, 아래처럼 "3 is odd"라는 디버깅 정보가 없다면 문제점을 파악하는데 더 많은 시간이 걸리기 때문입니다. + +```none +Value of: IsEven(Fib(4)) + Actual: false +Expected: true +``` + +지금까지도 유용하지만 `EXPECT_FALSE` 와 `ASSERT_FALSE`같은 negative assertion에도 사용할 수 있도록 조금 더 개선해 보겠습니다. + +```c++ +::testing::AssertionResult IsEven(int n) { + if ((n % 2) == 0) + return ::testing::AssertionSuccess() << n << " is even"; + else + return ::testing::AssertionFailure() << n << " is odd"; +} +``` + +이제 `EXPECT_FALSE`와 함께 사용해도 추가적인 디버깅 정보를 제공할 수 있게 되었습니다. `IsEven()`의 최종코드는 `EXPECT_FALSE(IsEven(Fib(6)))`라는 assertion에 대해서도 꽤 괜찮은 디버깅 정보를 출력해 줍니다. + +```none + Value of: IsEven(Fib(6)) + Actual: true (8 is even) + Expected: false +``` + +#### Predicate-Formatter 사용하기 + +지금까지 `(ASSERT|EXPECT)_PRED*` 와 `(ASSERT|EXPECT)_(TRUE|FALSE)`을 기존의 function 또는 functor와 함께 사용하는 방법들을 배웠습니다. 아직도 부족한가요? 네, 그럴 수 있습니다. 예를 들어 argument가 `ostream`을 사용하지 못하는 타입이라면 위의 2가지 방법도 적용할 수 없습니다. 그럴 때는 *predicate-formatter assertions*을 사용하면 됩니다. *predicate-formatter assertions*를 사용하면 아예 모든 message를 사용자가 정의할 수 있습니다. + +| Fatal assertion | Nonfatal assertion | Verifies | +| ------------------------------------------------ | ------------------------------------------------ | ---------------------------------------- | +| `ASSERT_PRED_FORMAT1(pred_format1, val1);` | `EXPECT_PRED_FORMAT1(pred_format1, val1);` | `pred_format1(val1)` is successful | +| `ASSERT_PRED_FORMAT2(pred_format2, val1, val2);` | `EXPECT_PRED_FORMAT2(pred_format2, val1, val2);` | `pred_format2(val1, val2)` is successful | +| `...` | `...` | `...` | + +`(ASSERT|EXPECT)_PRED_FORMAT*`계열과 앞서 설명한 2가지 방법과의 다른 점은 predicate가 아니라 *predicate-formatter*(`pred_formatn`) 를 사용한다는 것입니다. 여기서 *predicate-formatter*란 것은 아래와 같은 형식으로 정의된 function이나 functor를 의미합니다. + +```c++ +::testing::AssertionResult PredicateFormattern(const char* expr1, + const char* expr2, + ... + const char* exprn, + T1 val1, + T2 val2, + ... + Tn valn); +``` + +`valn`는 실제로 assertion에 사용되는 argument를 의미합니다. `exprn`은 `valn`의 caller쪽 소스코드를 그대로 저장하고 있는 문자열입니다. 쉽게 말해서 `Foo(variable)`라는 호출이 있다면 `expr1`은 `"variable"`이라는 문자열이 됩니다. 마지막으로 `Tn`은 `valn`의 타입을 의미하는데 값타입, 참조타입 둘 다 가능합니다. 예를 들어 `int` 타입이라면 그대로 `int` 혹은 `const Foo&`등이 올 수 있습니다. + +이제 예제를 보겠습니다. `AssertMutuallyPrime()`은 `EXPECT_PRED2()`를 설명할 때 사용했던 `MutuallyPrime()`을 *predicate-formatter*로 변경한 코드입니다. + +```c++ +// Returns the smallest prime common divisor of m and n, +// or 1 when m and n are mutually prime. +int SmallestPrimeCommonDivisor(int m, int n) { ... } + +// A predicate-formatter for asserting that two integers are mutually prime. +::testing::AssertionResult AssertMutuallyPrime(const char* m_expr, + const char* n_expr, + int m, + int n) { + if (MutuallyPrime(m, n)) return ::testing::AssertionSuccess(); + + return ::testing::AssertionFailure() << m_expr << " and " << n_expr + << " (" << m << " and " << n << ") are not mutually prime, " + << "as they have a common divisor " << SmallestPrimeCommonDivisor(m, n); +} +``` + +이렇게 구현된 `AssertMutuallyPrime()`는 아래처럼 사용하면 됩니다. + +```c++ + EXPECT_PRED_FORMAT2(AssertMutuallyPrime, b, c); +``` + +`b`, `c`의 값이 각각 `4`, `10`이었다면 최종적으로 아래와 같은 failure message가 출력됨을 확인할 수 있습니다. + +```none +b and c (4 and 10) are not mutually prime, as they have a common divisor 2. +``` + +이미 눈치챘을 수도 있지만 `(EXPECT|ASSERT)_PRED_FORMAT*`은 가장 기본적인 assertion 정의방법이며 상당수의 built-in assertion들이 이를 기반으로 만들어 졌습니다. + +### Floating-Point 비교하기 + +Floating-point를 비교하는 것은 까다로운 문제입니다. 반올림 문제가 있기 때문에 2개의 floating-point가 정확히 같다고 판정하기 어렵기 때문입니다. 따라서 `ASSERT_EQ`를 사용한 단순한 비교로는 정확한 답을 얻을 수 없습니다. 더군다나 floating-point 값의 범위가 커진다면 오차범위를 고정(single fixed error bound)하기 보다는 상대적인 오차범위(fixed relative error bound)를 사용하는 것이 정밀도 측면에서 더 좋습니다. + +이렇듯 floating-point를 정확히 비교하기 위해서는 오차범위를 신중하게 선택해야 합니다. 만약, 사용자가 직접 오차범위를 직접 지정하기 싫다면 ULPs(Units in the Last Place)를 기본설정으로 사용하는 것도 좋은 방법입니다. 실제로 googletest도 ULPs를 사용하고 있습니다. ULPs에 대한 자세한 설명은 [여기](https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/)를 참조하기 바랍니다. + +#### Floating-Point Macros + +| Fatal assertion | Nonfatal assertion | Verifies | +| ------------------------------- | ------------------------------- | ---------------------------------------- | +| `ASSERT_FLOAT_EQ(val1, val2);` | `EXPECT_FLOAT_EQ(val1, val2);` | the two `float` values are almost equal | +| `ASSERT_DOUBLE_EQ(val1, val2);` | `EXPECT_DOUBLE_EQ(val1, val2);` | the two `double` values are almost equal | + +위 표에서 "almost equal"이란, 2개의 값이 4 ULP's 오차범위 내에서 같음을 의미합니다. + +만약, 오차범위를 직접 지정하고 싶다면 아래와 같은 assertion을 사용하기 바랍니다. + +| Fatal assertion | Nonfatal assertion | Verifies | +| ------------------------------------- | ------------------------------------- | ------------------------------------------------------------ | +| `ASSERT_NEAR(val1, val2, abs_error);` | `EXPECT_NEAR(val1, val2, abs_error);` | the difference between `val1` and `val2` doesn't exceed the given absolute error | +#### Floating-Point Predicate-Format Functions + +Floating-point 연산자들을 더 다양하게 만들어서 제공할 수도 있지만 macro가 너무 많아지는 것도 좋지 않습니다. 따라서 googletest는 predicate-formatter assertion을 floating-piont에도 사용할 수 있도록 했습니다. 사용방법은 기존과 동일하며 첫번째 argument에 `FloatLE`, `DoubleLE`과 같이 floating-point 관련 function을 전달하는 부분만 다릅니다. + +```c++ +EXPECT_PRED_FORMAT2(::testing::FloatLE, val1, val2); +EXPECT_PRED_FORMAT2(::testing::DoubleLE, val1, val2); +``` + +위 예제는 `val1`이 `val2`보다 작거나 같은지(almost equal)를 검사하는 코드입니다. `ASSERT_PRED_FORMAT2`의 사용방법도 동일합니다. + +### gMock Matchers를 이용한 Asserting + +C++ mocking framework인 [gMock](../../googlemock)을 개발하면서 mock object로 전달되는 argument를 확인하기 위한 방법으로 matcher라는 것을 만들었습니다. 이러한 gMock *matcher*는 사실상 predicate랑 원리가 같습니다. 더불어 지원하는 기능도 풍부하기 때문에 matcher를 이용하면 좀 더 간단하게 predicate assertion을 구현할 수 있습니다. + +아래는 matcher를 이용한 assertion macro들입니다. + +| Fatal assertion | Nonfatal assertion | Verifies | +| ------------------------------ | ------------------------------ | --------------------- | +| `ASSERT_THAT(value, matcher);` | `EXPECT_THAT(value, matcher);` | value matches matcher | + +예를 들어, built-in matcher인 `StartsWith(prefix)`를 사용하면 어떠한 문자열이 `prefix`로 시작하는지는 검사해줍니다. 아래와 같이 구현하면 됩니다. + +```c++ +using ::testing::StartsWith; +... + // Verifies that Foo() returns a string starting with "Hello". + EXPECT_THAT(Foo(), StartsWith("Hello")); +``` + +이렇듯 matcher의 사용법도 어렵지 않습니다. 보다 자세한 사용방법은 [여기](../../googlemock/docs/cook_book.md#using-matchers-in-google-test-assertions)을 참조하세요. + +gMock은 `StartsWith()` 외에도 다양한 matcher들을 제공합니다. 이를 통해 googletest에서는 불가능했던 것들도 가능하므로 관련 [문서](../../googlemock/docs/cook_book.md#using-matchers)을 한 번 읽어보시길 추천합니다. 게다가 필요한 matcher를 직접 만드는 것도 가능하기 때문에 상당히 유용하며 이렇게 matcher를 직접 만드는 방법에 대한 정보는 [여기]()를 참조하시기 바랍니다. + +마지막으로 matcher를 사용하기 위해서 추가적으로 필요한 환경설정은 없습니다. gMock 자체가 googletest와 함께 제공되는 번들 소프트웨어이므로 헤더파일만 포함( `#include "testing/base/public/gmock.h"`)하면 바로 사용할 수 있습니다. + +### More String Assertions + +([이전](#asserting-using-gmock-matchers) 섹션을 먼저 읽으세요) + +gMock은 문자열과 관련된 [string matchers](../../googlemock/docs/CheatSheet.md#string-matchers)도 풍부하게 제공합니다. 이렇게 제공되는 built-in matcher들을 `EXPECT_THAT()` 또는 `ASSERT_THAT()`과 함께 사용하기만 하면 됩니다. 이를 통해 sub-string, prefix, suffix, regular expression과 같이 다양한 방법으로 string assertion을 수행할 수 있습니다. 사용방밥은 아래 예제코드와 같습니다. + +```c++ +using ::testing::HasSubstr; +using ::testing::MatchesRegex; +... + ASSERT_THAT(foo_string, HasSubstr("needle")); + EXPECT_THAT(bar_string, MatchesRegex("\\w*\\d+")); +``` + +또한, 문자열이 HTML이나 XML을 포함할때는 [XPath expression](http://www.w3.org/TR/xpath/#contents)를 통해서 DOM tree와의 비교를 수행할 수 있습니다. + +```c++ +// Currently still in //template/prototemplate/testing:xpath_matcher +#include "template/prototemplate/testing/xpath_matcher.h" +using prototemplate::testing::MatchesXPath; +EXPECT_THAT(html_string, MatchesXPath("//a[text()='click here']")); +``` + +### Windows HRESULT assertions + +Windows 환경의 `HRESULT`를 위한 assertion도 제공하고 있습니다. + +| Fatal assertion | Nonfatal assertion | Verifies | +| -------------------------------------- | -------------------------------------- | ----------------------------------- | +| `ASSERT_HRESULT_SUCCEEDED(expression)` | `EXPECT_HRESULT_SUCCEEDED(expression)` | `expression` is a success `HRESULT` | +| `ASSERT_HRESULT_FAILED(expression)` | `EXPECT_HRESULT_FAILED(expression)` | `expression` is a failure `HRESULT` | + +위의 assertion을 사용하면 주어진 `expression`을 수행하고 그 결과인 `HRESULT`를 사람이 알아볼 수 있게 출력해 줍니다. + +사용방법은 아래와 같습니다. + +```c++ +CComPtr shell; +ASSERT_HRESULT_SUCCEEDED(shell.CoCreateInstance(L"Shell.Application")); +CComVariant empty; +ASSERT_HRESULT_SUCCEEDED(shell->ShellExecute(CComBSTR(url), empty, empty, empty, empty)); +``` + +### Type Assertions + +타입 `T1`과 `T2`가 같은지 확인하기 위한 assertion은 아래와 같습니다. + +```c++ +::testing::StaticAssertTypeEq(); +``` + +위의 assertion은 성공하면 별도의 동작없이 그대로 계속 진행하게 됩니다. 단, 실패하게 되면 compile error가 발생합니다. 즉, compiler에 의해서 assertion의 결과가 판정됩니다. 이에 대한 error message는 compiler마다 다르겠지만 보통 `T1`, `T2`의 실제 타입을 출력해 줍니다. 템플릿 코드를 구현할 때 유용하게 쓸 수 있을 것입니다. + +**Caveat**: 한가지 주의할 점은 위의 assertion을 function template이나 class template에서 사용하게 되면 해당 타입에 대한 template 코드가 실제로 만들어 질 때만 동작한다는 것입니다. 왜냐하면 C++에서는 호출되지 않거나 사용되지 않는 template은 compile 대상에도 포함되지 않기 때문입니다. 예를 들어 아래와 같은 class template이 있다고 가정해보겠습니다. + +```c++ +template class Foo { + public: + void Bar() { ::testing::StaticAssertTypeEq(); } +}; +``` + +이 때, 아래 코드는 `StaticAssertTypeEq`로 전달되는 2개 타입(`int`, `bool`)이 서로 다름에도 compile error가 발생하지 않습니다. 왜냐하면 `Bar()`는 자신을 호출하는 코드가 없기 때문에 compile 대상에서 제외되기 때문입니다. + +```c++ +void Test1() { Foo foo; } +``` + +반면에 아래 코드는 `Foo::Bar()`를 호출하는 코드가 존재하기 때문에 assertion이 동작하게 될 것입니다. 물론, 그 결과는 타입 불일치로 인한 compile error입니다. + +```c++ +void Test2() { Foo foo; foo.Bar(); } +``` + +### Assertion을 사용가능한 곳 + +Assertion은 C++ function이라면 어디에서든 사용할 수 있습니다. 꼭 test fixture class의 member function일 필요도 없습니다. 다만, 유일한 한 가지 제약은 fatal failure(`FAIL*`, `ASSERT_*`) 계열 assertion들은 return type이 `void`인 function에서만 사용할 수 있다는 것인데 그 이유는 googletest가 exception을 사용하지 않기 때문입니다. Return type이 `void`가 아닌 function에서 fatal assertion을 사용하면 아래와 같은 compile error 중 하나가 발생할 것입니다. + +```bash +error: void value not ignored as it ought to be +``` +```bash +cannot initialize return object of type 'bool' with an rvalue of type 'void' +``` +```bash +error: no viable conversion from 'void' to 'string'. +``` + +만약 return type이 `void`가 아닌 function에서 fatal assertion을 사용해야 한다면 조금 수정이 필요합니다. 먼저 해당 function의 return type을 `void`로 변경하고 원래 반환하려고 했던 값은 반환하는게 아니라 argument에 대입하도록 수정해야 합니다. 예를 들어 `T2 Foo(T1 x)`라는 function을 `void Foo(T1 x, T2* result)`로 변경하면 됩니다. 그러면 return type이 `void`로 변경되었으니 `Foo()` 내부에서도 fatal assertion을 사용할 수 있게 됩니다. 주의할 점은 이러한 변경이 `Foo()`의 caller와 `Foo()` 간의 관계도 변경하는 것이기 때문에 `Foo()`가 어떤 이유로 인해서든 `result`에 의미있는 값을 대입하지 못하고 비정상 종료되더라도 `Foo()`를 사용하는 쪽에서는 문제가 없도록 구현하고 해야 합니다. + +만약, 이처럼 return type을 변경할 수 없는 상황이라면 어쩔 수 없이 `ADD_FAILURE*` 또는 `EXPECT_*`와 같은 non-fatal failure를 사용하기를 바랍니다. + +NOTE: class의 constructor나 destructor는 return type이 따로 없기 때문에 fatal assertion도 사용할 수 없습니다. 사용하더라도 compile error가 발생할 것입니다. 이를 위한 첫 번째 대안으로 `abort`를 사용할 수 있는데 `abort`는 test program을 아예 종료하기 때문에 원하는 동작이 맞는지는 확인해봐야 합니다. 두 번째 대안은 `SetUp`/ `TearDown`을 사용하는 것이며 이와 관련 내용은 [constructor/destructor vs. `SetUp`/`TearDown`](https://github.com/google/googletest/blob/master/googletest/docs/faq.md#CtorVsSetUp)에서 자세하게 확인할 수 있습니다. + +WARNING: constructor, desturctor에서 fatal assertion을 사용하는 또 다른 방법은 assertion을 수행 할 function을 별도로 만들고(private 영역에) constructor나 destructor가 해당 function을 호출하도록 하는 것입니다. 여기서 주의할 점은 constructor나 destructor에서 발생한 fatal assertion은 진행중인 테스트를 중단시키지는 못하고 자기자신만 중단한다는 것입니다. 만약 constructor나 destructor가 수행도중에 중단되어 버리면 상황에 따라 object의 생성이나 소멸이 완료되지 못하는 문제가 발생할 수도 있습니다. 따라서 constructor나 destructor에서 fatal assertion을 사용하려는 사용자는 그에 따른 문제가 없는지를 철저히 확인해야 하며 그러한 부분이 부담된다면 `SetUp/TearDown`을 사용하거나 아니면 `abort`를 사용하는 것이 더 편할 것입니다. + +## googletest의 디버깅정보 출력방식 변경하기 + +`EXPECT_EQ`같은 assertion이 실패하면, googletest는 argument로 전달된 값을 비롯해서 디버깅에 필요한 정보를 출력해줍니다. 이렇게 디버깅 정보를 출력해주는 기능을 printer라고 하는데 이 printer는 사용자가 원하는 방향으로 확장도 가능합니다. + +Googletest는 기본적으로 C++ built-in 타입, STL container, `<<`연산자를 정의한 타입에 대해서는 디버깅 정보를 출력해주지만 그 외의 타입들은 raw bytes 값만 출력해주기 때문에 출력되더라도 알아보기 힘들 수 있습니다. + +이런 경우에는`<<`연산자를 재정의해서 printer는 확장하는 것이 좋습니다. 아래에 예제코드가 있습니다. + +```c++ +// Streams are allowed only for logging. Don't include this for +// any other purpose. +#include + +namespace foo { + +class Bar { // We want googletest to be able to print instances of this. +... + // Create a free inline friend function. + friend std::ostream& operator<<(std::ostream& os, const Bar& bar) { + return os << bar.DebugString(); // whatever needed to print bar to os + } +}; + +// If you can't declare the function in the class it's important that the +// << operator is defined in the SAME namespace that defines Bar. C++'s look-up +// rules rely on that. +std::ostream& operator<<(std::ostream& os, const Bar& bar) { + return os << bar.DebugString(); // whatever needed to print bar to os +} + +} // namespace foo +``` + +다만, 경우에 따라서는 `<<`연산자를 재정의하는 것에 대해 동료들이 반대할 수도 있고 `<<`연산자가 이미 구현되어 있어서 바꿀 수 없을 수도 있습니다. 그런 경우에는 `PrintTo()`와 같은 디버깅정보 출력을 위한 별도의 function을 정의하는 것도 괜찮습니다. 아래 예제코드가 있습니다. + +```c++ +// Streams are allowed only for logging. Don't include this for +// any other purpose. +#include + +namespace foo { + +class Bar { + ... + friend void PrintTo(const Bar& bar, std::ostream* os) { + *os << bar.DebugString(); // whatever needed to print bar to os + } +}; + +// If you can't declare the function in the class it's important that PrintTo() +// is defined in the SAME namespace that defines Bar. C++'s look-up rules rely +// on that. +void PrintTo(const Bar& bar, std::ostream* os) { + *os << bar.DebugString(); // whatever needed to print bar to os +} + +} // namespace foo +``` + +만약, 사용자가 `<<`와 `PrintTo()`를 2개 모두 구현했다면 `PrintTo()`가 우선적으로 선택됩니다. 그 이유는 2개를 비교했을 때, `<<`연산자는 이미 구현해서 다른 용도로 사용하고 있을 확률이 더 높기 때문입니다. + +여기까지 해서 `Bar` class에 `<<` 혹은 `PrintTo()`를 정의했다면 한다면, 다음으로는 확인하고 싶은 값을 출력하기만 하면 됩니다. 예를 들어 `x`의 값을 출력하고 싶다면 `::testing::PrintToString(x)`라고 구현하면 됩니다. `::testing::PrintToString(x)`의 return type은 `std::string`입니다. 사용방법은 아래 예제코드를 확인하세요. + +```c++ +vector > bar_ints = GetBarIntVector(); + +EXPECT_TRUE(IsCorrectBarIntVector(bar_ints)) + << "bar_ints = " << ::testing::PrintToString(bar_ints); +``` + +## Death Tests + +원하는 조건이 충족되지 않았을 때 스스로를 종료시키는 프로그램도 당연히 존재할 것입니다. 예를 들어 프로그램이 동작하다가 치명적인 문제를 일으킬 수도 있는 비정상상태로 진입했음을 알게되면 문제가 더 악화되기 전에 스스로를 종료시키는 상황이 있을 것입니다. (여기서 치명적인 문제라는 것은 memory corruption이나 security holes와 같은 문제들이 될 것입니다.) + +Googletest는 이렇게 스스로 종료하도록 구현된 프로그램들이 실제로 원하는 상황에서 원하는 방향으로 잘 종료되는지를 확인하기 위한 방법을 제공하고 있으며 이를 *death test*라고 부릅니다. 쉽게 말해서 *death test*란 프로그램이 원하는 방향으로 종료되었는지 확인하는 것입니다. + +단, exception은 death test에 포함되지 않습니다. 왜냐하면 exception은 catch해서 적절한 조치를 취하도록 구현하는 것이 목적이기 때문에 프로그램을 아예 종료하는 death test와는 다른 관점에서 봐야 합니다. 만약, exception에 대한 테스트가 필요하다면 [Exception Assertions](#exception-assertions)에서 관련 내용을 별도로 다루고 있습니다. + +### Death Test 구현하기 + +Death test를 위해서 아래와 같은 macro를 제공하고 있습니다. + +| Fatal assertion | Nonfatal assertion | Verifies | +| ------------------------------------------------ | ------------------------------------------------ | ------------------------------------------------------------ | +| `ASSERT_DEATH(statement, matcher);` | `EXPECT_DEATH(statement, matcher);` | `statement` crashes with the given error | +| `ASSERT_DEATH_IF_SUPPORTED(statement, matcher);` | `EXPECT_DEATH_IF_SUPPORTED(statement, matcher);` | if death tests are supported, verifies that `statement` crashes with the given error; otherwise verifies nothing | +| `ASSERT_EXIT(statement, predicate, matcher);` | `EXPECT_EXIT(statement, predicate, matcher);` | `statement` exits with the given error, and its exit code matches `predicate` | + +먼저 `statement`는 종료되기를 기대하는 대상 프로그램이나 코드를 의미합니다. 당연히 여러줄의 코드도 가능합니다. 다음으로 `predicate`는 `statement`의 exit status를 확인하는 function이나 function object입니다. 마지막으로 `matcher`에는 `const std::string&`을 전달받을 수 있는 gMock matcher 또는 (Perl) regular expression을 사용할 수 있습니다. 이러한 `matcher`는 `statement`가 stderr로 출력하는 error message를 확인하는데 사용됩니다. `statement`의 stderr 출력이 `matcher`를 만족하지 못하면 death test는 실패합니다. 만약에 `matcher`에 순수 문자열만 전달한다면 `ContainsRegex(str)`로 자동 변환됩니다. 그 이유는 이전 버전에서는 regular expression만 전달받을 수 있었다가 gMock matcher도 사용할 수 있게 변경되었기 때문에 하위호환을 위해 그렇게 구현되었습니다. + + +Death test assertion도 `ASSERT`계열은 현재 실행중인 test function을 중단시키고 `EXPECT`계열은 계속해서 진행합니다. + +> NOTE: 위 표에서 사용된 "crash"라는 단어는 process가 종료될 때의 exit status가 `0`이 아님을 의미합니다. 2가지 가능성이 있습니다. 해당 process가 `exit()` 또는 `_exit()`를 호출하면서 `0`이 아닌 값을 전달했거나 혹은 signal을 수신해서 종료된 경우입니다. +> +> 반대로 `statement`의 exit status가 `0`이라면 이는 곧 crash가 아님을 의미하기 떄문에 `EXPECT_DEATH`를 사용할 수가 없게 됩니다. 만약,이처럼 crash가 아니거나 또는 exit status를 좀 더 세부적으로 조작하고 싶은 경우에는 `EXPECT_EXIT`를 사용하기 바랍니다. + +`predicate`는 return type이 `int` 또는 `bool`인 function만 사용 가능하며 그렇게 전달된 `predicate`가 `true`를 반환하면 death test도 성공하게 됩니다. 즉 `statement`가 원하는 exit code와 함께 종료된 것을 의미합니다. 더불어 googletest는 일반적인 상황에서 사용가능한 `predicate`들을 기본적으로 제공하고 있습니다. + +```c++ +::testing::ExitedWithCode(exit_code) +``` + +위의 `predicate`는 `statement`의 exit status가 `exit_code`와 동일하다면 `true`입니다. + +```c++ +::testing::KilledBySignal(signal_number) // Not available on Windows. +``` + +위의 `predicate`는 `statement`가 signal을 수신했고, 해당 signal number가 `signal_number`과 동일하다면 `true` 입니다. + +`*_DEATH` macro는 사실 `*_EXIT`를 사용해서 확장한 wrapper macro이며 `statment`의 exit status가 0인지 아닌지를 확인합니다. + +정리해보면 death test assertion은 아래 3가지를 확인해서 성공,실패를 결정합니다. + +1. `statement`가 `abort` 또는 `exit` 되었나? +2. `ASSERT_EXIT` 또는 `EXPECT_EXIT`는 exit status가 `predicate`를 만족하는지 확인하고, `ASSERT_DEATH` 또는 `EXPECT_DEATH`는 exit status != 0을 만족하는지 확인합니다. +3. `statment`의 stderr출력이 `matcher`를 만족하는지? + +한가지 주의할 점은 만약 `statement`내부에 `ASSERT_*` 또는 `EXPECT_*`를 사용했다고 하더라도 death test assertion의 결과에는 영향을 미치지 않는 다는 것입니다. 당연하게도 death test는 `statement`가 어떻게 종료되는지 확인하는 것이 목적이기 때문입니다. `ASSERT_*`, `EXPECT_*`은 프로그램을 종료시키거나 하는 macro가 아니기 때문에 `statement`내부에서 발생하는 작업일 뿐이며 death test의 관심대상은 아닙니다. + +이제는 death test를 구현할 수 있을 것입니다. 간단한 예시가 아래에 있습니다. 다만, 예제코드에서 `statement`들의 세부 구현은 생략되습니다. + +```c++ +TEST(MyDeathTest, Foo) { + // This death test uses a compound statement. + ASSERT_DEATH({ + int n = 5; + Foo(&n); + }, "Error on line .* of Foo()"); +} + +TEST(MyDeathTest, NormalExit) { + EXPECT_EXIT(NormalExit(), ::testing::ExitedWithCode(0), "Success"); +} + +TEST(MyDeathTest, KillMyself) { + EXPECT_EXIT(KillMyself(), ::testing::KilledBySignal(SIGKILL), + "Sending myself unblockable signal"); +} +``` + +위 코드는 아래 3가지를 검증합니다. + +* `Foo(5)`를 호출하면 stderr로 `"Error on line .* of Foo()"`를 출력한 후에 "exit status != 0" 이 아닌 값으로 종료하는지 확인 +* `NormalExit()`를 호출하면 sdterr로 `"Success"`를 출력한 후에 "exit status == 0" 인 값으로 종료하는지 확인 +* `KillMyself()`를 호출하면 stderr로 `"Sending myself unblockable signal"`을 출력한 후에 `SIGKILL`시그널을 전달받아서 종료하는지 확인 + +`TEST()`, `TEST_F()`를 구현할 때 death test assertion과 다른 assertion 혹은 코드를 함께 사용하는 것도 물론 가능합니다. + +### Death Test 이름짓기 + +IMPORTANT: death test를 구현할 때는 **test suite**(not test case)에 `*DeathTest`라는 이름으로 작성하여 사용하기 바랍니다. 그 이유는 [Death Tests And Threads](#death-tests-and-threads)에 상세하게 설명되어 있습니다. + +만약 사용자의 test fixture class가 normal test case와 death test case를 모두 포함한다면 `using` 혹은 `typedef`를 사용하여 별칭을 만드는 것이 좋습니다. 아래 예제를 참고하세요. + +```c++ +class FooTest : public ::testing::Test { ... }; + +using FooDeathTest = FooTest; + +TEST_F(FooTest, DoesThis) { + // normal test +} + +TEST_F(FooDeathTest, DoesThat) { + // death test +} +``` + +### 정규식 문법 + +POSIX system(e.g. Linux, Cygwin, Mac) 환경에서는 [POSIX extended regular expression](http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap09.html#tag_09_04) 문법을 사용합니다. 관련 문법을 공부하려면 [Wikipedia entry](http://en.wikipedia.org/wiki/Regular_expression#POSIX_Extended_Regular_Expressions)를 참조하세요. + +Windows 환경에서는 googletest 자체적으로 간단한 문법을 구현하여 사용하고 있습니다. 따라서 기능이 약간 부족할 수도 있습니다. 예를 들어 (`"x|y"`), (`"(xy)"`), (`"[xy]"`), (`"{5,7}"`) 과 같은 문법은 지원하지 않고 있습니다. 아래 표에서 사용가능한 문법을 확인하세요. (`A`와 `.`는 문자, `\\`은 escape sequnce, `x`, `y`는 정규식을 의미합니다.) + +| Expression | Meaning | +| ---------- | ------------------------------------------------------------ | +| `c` | matches any literal character `c` | +| `\\d` | matches any decimal digit | +| `\\D` | matches any character that's not a decimal digit | +| `\\f` | matches `\f` | +| `\\n` | matches `\n` | +| `\\r` | matches `\r` | +| `\\s` | matches any ASCII whitespace, including `\n` | +| `\\S` | matches any character that's not a whitespace | +| `\\t` | matches `\t` | +| `\\v` | matches `\v` | +| `\\w` | matches any letter, `_`, or decimal digit | +| `\\W` | matches any character that `\\w` doesn't match | +| `\\c` | matches any literal character `c`, which must be a punctuation | +| `.` | matches any single character except `\n` | +| `A?` | matches 0 or 1 occurrences of `A` | +| `A*` | matches 0 or many occurrences of `A` | +| `A+` | matches 1 or many occurrences of `A` | +| `^` | matches the beginning of a string (not that of each line) | +| `$` | matches the end of a string (not that of each line) | +| `xy` | matches `x` followed by `y` | + +사용자의 환경에서 어떤 정규식 표현이 가능한지 확인하기 위한 macro를 제공하고 있습니다. `#if`를 사용해서 현재 시스템에 맞는 정규식을 구분해서 사용하세요. 관련 macro는 아래와 아래와 같습니다. + +`GTEST_USES_PCRE=1`, or `GTEST_USES_SIMPLE_RE=1` or `GTEST_USES_POSIX_RE=1` + +### Death Test 의 동작방식 + +`ASSERT_EXIT()`는 내부적으로 새로운 process를 생성하고 death test assertion으로 전달된 `statement`를 수행하도록 되어 있습니다. 다만, 그 과정에서 사용자의 시스템이 POSIX인지 Windows인지에 따라 동작이 조금씩 달라집니다. 또한, 환경변수 `::testing::GTEST_FLAG(death_test_style)`의 설정값에 따라서도 동작이 달라집니다. 이 환경변수는 test program을 실행할 때 cmd line을 통해서 설정할 수도 있습니다.(`--gtest_death_test_style`) 이 환경변수를 death test style이라고 부르는데 현재는 2가지(`fast`와 `threadsafe`) style을 지원하고 있습니다. 그럼 아래는 사용자의 시스템 환경과 death test style에 따라 death test의 동작이 어떻게 다른지에 대한 설명입니다. + +* POSIX 시스템에서는 child process를 만들기 위해 `fork()` (`clone()` on Linux) 를 사용합니다. + * 만약, death test style이 `"fast"`라면 death test의 `statement`는 즉시 수행됩니다. + * 만약, death test style이 `"threadsafe"`라면 test program 자체를 1개 더 실행시킨 후에 필요한 해당 death test를 실행합니다. +* Windows 시스템에서는 child process를 만들기 위해 `"CreateProcess()"` API를 사용합니다. 그런 후에 해당 process를 실행시켜서 death test를 수행합니다. 따라서 POSIX의 `"threadsafe"`모드일 때와 유사하게 동작합니다. + +현재 death test style의 기본설정값은 `"fast"`입니다. 또한, death test style에 지원하지 않는(`fast`, `threadsafe`가 아닌) 다른 값을 지정하면 death test는 실패하게 됩니다. 이제 마지막으로 death test의 성공조건 2가지를 다시 한 번 기억하고 넘어갑시다. + +1. child process의 exit status(code)는 `predicate`를 만족해야 합니다. +2. child process의 strerr 출력은 `matcher`와 동일해야 합니다. + +만약 death test의 `statement`가 문제없이 정상적으로 코드를 다 수행하고 종료되면 어떻게 될까요? 일단 child process는 문제없이 잘 종료될 것입니다. 즉, child process가 종료되는 건 보장됩니다. 단지 death test의 결과가 실패일 뿐입니다. + +### Death Tests 그리고 Threads + +Death test를 위해서는 test program의 child thread를 생성하는 작업이 꼭 필요합니다. 따라서 death test는 thread safety가 보장되는 상황에서 제일 안정적으로 수행될 수 있습니다. 가장 중요한 하는 것은 death test를 single-threaded 환경에서 실행하는 것인데, 만약 애초에 single-threaded환경이 불가능하면 어떻게 될까요? 예를 들어서 main function이 실행되기 전에 생성되는 (정적으로 초기화하는) thread가 있다면 어떻게 해야 될까요? 사실 thread가 일단 하나라도 생성되면 parent process를 다시 처음 상태로 되돌리는 것은 매우 어렵습니다. + +Googletest는 이런 상황을 대비해서 thread 관련 이슈를 사용자에게 최대한 전달하려고 노력합니다. + +1. Death test가 시작했을 때, 2개 이상의 thread가 실행중이라면 이에 대한 경고문을 출력합니다. +2. "*DeathTest"라는 이름을 가진 test suite을 다른 테스트들보다 먼저 실행해 줍니다. +3. Linux 환경에서는 `fork()` 대신 `clone()`을 사용해서 child process를 생성하도록 했습니다. 왜냐하면 multi-threaded 환경에서 `fork()`가 문제를 일으킬 확률이 더 크기 때문입니다. (Cygwin이나 Mac에서는 `clone()`을 지원하지 않 때문에 fork()를 그대로 사용합니다.) + +반대로 death test로 전달되는 `statement` 내부에서 child thread를 생성하는 것은 괜찮습니다. 아무런 문제가 되지 않습니다. + +### Death Test Styles + +Multi threaded 환경에서 "threadsafe"모드를 사용하면 테스트에 문제가 발생할 확률을 줄여줍니다. 테스트 실행시간을 증가시킨다는 단점은 있지만 thread safety를 위해서는 좋은 선택입니다. + +만약, 사용자가 자동화된 테스트 환경을 사용 중이라서 cmd line으로 뭔가를 전달할 수 없다면, 아래와 같이 소스코드에서 설정하는 것도 가능합니다. + +```c++ +testing::FLAGS_gtest_death_test_style="threadsafe" +``` + +위의 코드는 `main()`에 추가해도 되고 아니면 각각의 test case에 추가해도 됩니다. 단, 개별 test case에 설정한 값은 해당 test function에만 적용되며 function이 종료되면 원래 값(전역 값)으로 복구됩니다. + +```c++ +int main(int argc, char** argv) { + InitGoogle(argv[0], &argc, &argv, true); + ::testing::FLAGS_gtest_death_test_style = "fast"; + return RUN_ALL_TESTS(); +} + +TEST(MyDeathTest, TestOne) { + ::testing::FLAGS_gtest_death_test_style = "threadsafe"; + // This test is run in the "threadsafe" style: + ASSERT_DEATH(ThisShouldDie(), ""); +} + +TEST(MyDeathTest, TestTwo) { + // This test is run in the "fast" style: + ASSERT_DEATH(ThisShouldDie(), ""); +} +``` + +### Caveats + +`ASSERT_EXIT()`로 전달되는 `statement`에는 어떤 C++ statement라도 구현할 수 있습니다. 다만, `statement`가 `return`으로 끝나거나 (`exit()`이 아니라) exception를 발생시키면 해당 death test는 실패로 간주됩니다. `ASSERT_TRUE()`와 같은 대다수의 googletest macro가 `return` 으로 끝나는 것과 비교하면 확연히 구분되는 다른 점입니다. + +또한, `statement`는 child process(thread)에서 수행되기 때문에 메모리 관리에 있어서 주의가 필요합니다. 알다시피 child process에서의 잘못된 동작을 parent process에서 알 수가 없기 때문입니다. 만약 death test에서 메모리를 해제해버리면 parent process는 이를 알 수 없고, 따라서 heap checker가 이를 추적할 수 없기 때문에 테스트는 실패로 판정됩니다. 메모리관련해서는 아래처럼 하기를 권장합니다. + +1. death test statment(child process)에서 메모리를 해제하지 마세요. +2. child process에서 해제한 메모리를 parent process에서 다시 해제하지 마세요. +3. test program에서 heap checker를 사용하지 마세요. + +Death test 구현상의 특징으로 인해 소스코드 1줄에 여러개의 death test assertion을 사용할 수는 없습니다. 그렇게 구현하면 compile error가 발생합니다. + +Death test를 "threadsafe"로 설정하는 것이 thread safety를 향상시키도록 도와주긴 하지만, deadlock과 같은 thread 사용에 따른 기본적인 이슈들은 여전히 사용자가 꼼꼼하게 확인해야 합니다. 이런 이슈는`pthread_atfork(3) `핸들러가 등록되어 있다고 해도 여전히 발생할 수 있습니다. + +## Sub-routines에서 Assertion을 사용하는 방법 + +### Assertions 추적하기 + +먼저, *sub-routine*은 기본적으로 C++의 일반적인 function을 뜻하며 test function 내부에서 호출하기 때문에 sub-routine이라고 표현하고 있습니다. 그럼 test function이 동일한 sub-routine을 여러번 호출한다고 가정해 봅시다. 그리고 테스트를 수행했더니 sub-routine에 구현된 assertion이 실패했다고 합시다. 그러면 어떤 호출에서 실패한건지 어떻게 구분하는게 좋을까요? + +물론 단순하게 로그를 추가하거나 failure message를 변경함으로써 확인할 수 있지만, 그러한 방법은 테스트를 복잡하게 만들 수 있습니다. 이런 상황에서는 `SCOPED_TRACE`와 `ScopedTrace`를 사용하는 것이 괜찮습니다. + +```c++ +SCOPED_TRACE(message); +ScopedTrace trace("file_path", line_number, message); +``` + +먼저, `SCOPED_TRACE`는 현재 실행중인 file name, line number, failure message를 출력해 줍니다. 이 때, `SCOPED_TRACE`로 전달되는 argument인 `message`는 `std::ostream`으로 출력가능한 값이여야 하며 그 내용이 default failure message에 더해져서 출력되게 됩니다. 다음으로 `ScopedTrace`는 `SCOPE_TRACE`가 출력해주는 file name, line number와 같은 내용을 보다 명시적으로 전달하고 싶을 때 사용합니다. 이러한 방법은 test helper를 구현할 때 유용합니다. 마지막으로 scope을 벗어나면 적용했던 내용들도 해제됩니다. + +아래는 예제코드입니다. + +```c++ +10: void Sub1(int n) { +11: EXPECT_EQ(Bar(n), 1); +12: EXPECT_EQ(Bar(n + 1), 2); +13: } +14: +15: TEST(FooTest, Bar) { +16: { +17: SCOPED_TRACE("A"); // This trace point will be included in +18: // every failure in this scope. +19: Sub1(1); +20: } +21: // Now it won't. +22: Sub1(9); +23: } +``` + +위 예제는 아래와 같은 failure message를 만들어 냅니다. `SCOPED_TRACE`가 사용된 첫번째 실패에서 path/to/foo_test.cc:17: A 라는 출력문이 추가된 것을 확인할 수 있습니다. + +```none +path/to/foo_test.cc:11: Failure +Value of: Bar(n) +Expected: 1 + Actual: 2 + Trace: +path/to/foo_test.cc:17: A + +path/to/foo_test.cc:12: Failure +Value of: Bar(n + 1) +Expected: 2 + Actual: 3 +``` + +이러한 기능을 적용하지 않았다면 `Sub1()`이 동일한 failure message를 출력하기 때문에 어떤 호출에서 실패한 것인지 구별할 수가 없습니다. 물론 `Sub1()`에서 사용중인 assertion에 `n`도 같이 출력하도록 수정해도 되겠지만 assertion의 개수가 많아지면 해야할 일도 많아지기 때문에 불편할 것입니다. + +아래에 `SCOPED_TRACE`를 사용할 때 유용한 팁을 공유합니다. + +1. Sub-routine을 호출할 때마다 `SCOPE_TRACE`를 사용하는 것보다는 아예 sub-routine의 시작부분에 구현하는 편리할 수 있습니다. +2. 반복문에서 sub-routine을 호출한다면 `message`에 iterator를 포함시켜서 어떤 호출에서 failure가 발생했는지 구분할 수 있도록 하세요. +3. 추가적인 failure message 없이 file name, line number만 알아도 충분하다면 `message`에 빈 문자열`""`을 전달하면 됩니다. +4. 여러 개의 scope가 중첩(nested)되어도 괜찮습니다. 이런 경우에는 중첩된 scope의 failure message를 모두 출력해 줍니다. 순서는 안쪽에 있는 scope부터 출력됩니다. +5. Emacs를 사용한다면 trace dump를 확인할 수 있습니다. 해당 line number에서 `return`키를 누르면 바로 소스파일로 이동합니다. + +### Sub-routine에서 발생한 Fatal Failures를 test function(혹은 caller)에 알려주기 + +`ASSERT_*` 나 `FAIL*`가 실패했을 때, 수행중인 test case를 중단하는 것이 아니라 *현재 수행중인 function만* 종료된다는 것을 기억해야 합니다. 즉, sub-routine에서 fatal failure가 발생해도 해당 sub-routine만 종료될 뿐이지 상위 test function은 종료되지 않음을 의미합니다. 아래 예제는 이로 인해 발생할 수 있는 문제(여기서는 segfault) 중 하나를 보여줍니다. + +```c++ +void Subroutine() { + // Generates a fatal failure and aborts the current function. + ASSERT_EQ(1, 2); + + // The following won't be executed. + ... +} + +TEST(FooTest, Bar) { + Subroutine(); // The intended behavior is for the fatal failure + // in Subroutine() to abort the entire test. + + // The actual behavior: the function goes on after Subroutine() returns. + int* p = NULL; + *p = 3; // Segfault! +} +``` + +위 코드는 `Subroutine()`에서 test case자체가 abort되기를 바라고 구현한 것이지만, 실제로는 `int *p = NULL`까지 진행하게 되어 segfault가 발생하게 됩니다. 이런 문제를 해결하기 위해서 googletest는 3가지 해결방법을 제공하고 있습니다. 첫 번째는 exception, 두 번째는 `ASSERT_NO_FATAL_FAILURE / EXPECT_NO_FATAL_FAILURE` 계열의 assertion, 세 번째는 `HasFatalFailure()`입니다. 이제 각각에 대해서 자세하게 설명하겠습니다. + +#### Sub-routine의 Assertion을 Exception처럼 사용하기 + +아래와 같이 `ThrowListener`를 등록해 놓으면 fatal failure를 exception으로 변경해줍니다. + +```c++ +class ThrowListener : public testing::EmptyTestEventListener { + void OnTestPartResult(const testing::TestPartResult& result) override { + if (result.type() == testing::TestPartResult::kFatalFailure) { + throw testing::AssertionException(result); + } + } +}; +int main(int argc, char** argv) { + ... + testing::UnitTest::GetInstance()->listeners().Append(new ThrowListener); + return RUN_ALL_TESTS(); +} +``` + +주의 할 점은 다른 listener들이 모두 등록된 후에 `ThrowListener`를 등록해야 합니다. 그렇지 않으면 의도한대로 동작하지 않을 것입니다. + +#### Sub-routine의 Asserting 알아내기 + +위에서 확인했듯이 test function이 sub-routine을 호출하는 구조에서 sub-routine에서 발생한 fatal failure를 test function에서 알 수가 없습니다. `ASSERT_*`는 일반적으로 해당 test case(test function)를 종료시키기 위해서 사용하기 때문에 위와 같은 동작은 사용자들이 기대했던 동작과는 조금 달랐던것도 사실입니다. + +이런 이유로 많은 사람들이 sub-routine에서 발생한 fatal failure를 마치 exception처럼 상위 function로 전달해주는 기능이 추가되기를 요청했고, googletest는 관련 macro를 제공하게 되었습니다. + +| Fatal assertion | Nonfatal assertion | Verifies | +| ------------------------------------- | ------------------------------------- | ------------------------------------------------------------ | +| `ASSERT_NO_FATAL_FAILURE(statement);` | `EXPECT_NO_FATAL_FAILURE(statement);` | `statement` doesn't generate any new fatal failures in the current thread. | + +다만, 위의 macro를 사용한다고 해도 현재 thread에서 발생하는 fatal failure만이 고려대상입니다. 예를 들어 `statement`가 새로운 thread를 생성하고 그 thread에서 fatal failure가 발생하는 경우에는 확인할 수 없습니다. + +아래는 예제코드입니다. + +```c++ +ASSERT_NO_FATAL_FAILURE(Foo()); + +int i; +EXPECT_NO_FATAL_FAILURE({ + i = Bar(); +}); +``` + +참고사항으로 Windows 환경에서는 multiple threads에서의 assertion사용 자체를 현재는 지원하지 않고 있습니다. + +#### 현재 테스트에서 발생한 Failures를 확인하기 + +`::testing::Test` class의 `HasFatalFailure()` function은 수행중인 test case에서 fatal failure가 발생했다면 `true`를 반환해 줍니다. 이를 통해 sub-routine의 fatal failure를 확인하고 원하는 조치를 할 수 있도록 도와줍니다. + +```c++ +class Test { + public: + ... + static bool HasFatalFailure(); +}; +``` + +일반적인 사용법은 아래와 같습니다. + +```c++ +TEST(FooTest, Bar) { + Subroutine(); + // Aborts if Subroutine() had a fatal failure. + if (HasFatalFailure()) return; + + // The following won't be executed. + ... +} +``` + +만약 `TEST()`, `TEST_F()`, test fixture 외의 장소에서 `HasFatalFailure()`을 사용하려면 아래처럼 `::testing::Test::` prefix를 사용해야 합니다. + +```c++ +if (::testing::Test::HasFatalFailure()) return; +``` + +동일한 방법으로 `HasNonfatalFailure()`는 non-fatal failure가 발생한 경우에 `true`를 반환해주며 `HasFailure()`은 fatal, non-fatal 관계 없이 failure가 발생한 경우를 확인할 수 있도록 도와줍니다. + +## 추가정보 기록하기 + +이 문서의 앞 부분에서는 message 혹은 failure message를 변경하는 법에 대해서 배운 적이 있습니다. 이에 더해서 조금 다른 방법으로 추가정보를 기록하는 것도 가능합니다. 예를 들면 test program을 실행할 때 `--gtest_output="xml"`과 같은 cmd line flag를 전달해서 [XML output](#generating-an-xml-report)으로 출력하는 것도 가능합니다. 이런 경우를 위해서 제공되는 function은 `RecordProperty("key", value)`입니다. `value`에는 `string` 이나 `int` 타입을 사용할 수 있습니다. `key`는 말 그대로 `value`를 구분하는 key(구분자)로 사용됩니다. 아래에 사용예제가 있습니다. + +```c++ +TEST_F(WidgetUsageTest, MinAndMaxWidgets) { + RecordProperty("MaximumWidgets", ComputeMaxUsage()); + RecordProperty("MinimumWidgets", ComputeMinUsage()); +} +``` + +위의 테스트코드가 실행되면 XML파일에 아래와 같은 내용이 추가됩니다. + +```xml + ... + + ... +``` + +> NOTE: +> +> * `RecordProperty()`은 `Test`라는 class의 static member function입니다. 따라서 `TEST`, `TEST_F`, 혹은 test fixture class에서 사용하는 것이 아니라면 `::testing:Test::`라는 prefix를 붙여줘야만 합니다. +> * `key`는 XML attribute로서 유효한 이름으로 지정해야 합니다. 또한, googletest에서 이미 사용중인 attribute도 사용해서는 안 됩니다. (이미 사용중인 attribute 이름: `name`, `status`, `time`, `classname`, `type_param`, `value_param`) +> * `RecordProperty()`를 test case가 아니라 test suite에서 사용한다면 (즉, `SetUpTestSuite()`이 호출되고 `TearDownTestSuite()`호출되기까지의 구간) XML 결과물에도 test suite의 정보로 기록됩니다. 같은 맥락에서 test suite의 바깥에서 사용한다면 XML 결과물에도 top-level element 정보로 기록됩니다. + +## Test Suite의 자원을 Test Case간에 공유하기 + +Googletest는 개별 test case를 실행할 때마다 test fixure object를 새로 생성합니다. 그 이유는 각 test case를 독립된 환경에서 수행하면 안정성을 확보할 수 있고 동시에 문제가 발생했을 때 디버깅에도 도움이 되기 때문입니다. 이러한 방법을 one-copy-per-test model 이라고 합니다. 한 가지 단점은 자원이 많은 경우에 이를 매번 set-up 해야하는 부분이 부담이 될 수가 있습니다. + +만약 test case들이 자원을 변경하거나 하지 않는다면 여러 test case들이 동일한 자원을 공유하게 해도 문제가 없을 것입니다. 이런 이유로 per-test set-up/tear-down에 더해 per-test-suite set-up/tear-down 기능도 제공하고 있습니다. 아래와 같이 사용하면 됩니다. + +1. `FooTest`라는 test fixture class가 있다고 가정합시다. 공유해야 하는 자원(변수)을 `static`으로 선언하세요. +2. 해당 `static`변수를 초기화합니다. (C++에서 `static` 멤버변수는 class 바깥에서도 초기화해야 합니다.) +3. 이제 test fixture class에 `static void SetUpTestSuite()` 과 `static void TearDownTestSuite()` 을 구현하세요. 두 함수 내부에는 공유자원을 위한 초기화 작업, 정리 작업을 구현하면 됩니다.(`SetupTestSuite`이 아니라 대문자 `u`인 점을 유의하세요.) + +이제 필요한 것은 다 됐습니다. Googletest는 해당 test fixture(`FooTest`)의 첫번째 test case을 수행하기 전에 `SetUpTestSuite()`을 호출합니다. 그리고 마지막 test case를 수행한 후에는 `TearDownTestSuite()`을 호출할 것입니다. 이와 같은 방법을 통해 `FooTest`에 포함된 test case끼리 자원을 공유할 수 있게 됩니다. + +Test case가 수행되는 순서는 정해지지 않았음을 기억하기 바랍니다. 따라서 각각의 test case는 서로 의존성이 없어야 합니다. 또한, 공유자원을 변경해서도 안됩니다. 만약 공유자원을 꼭 변경해야만 한다면 해당 test case가 끝나기 전에 원래대로 복구시켜야 합니다. + +아래는 지금까지 설명한 per-test-suite에 대한 set-up/tear-down을 구현한 예제입니다. + +```c++ +class FooTest : public ::testing::Test { + protected: + // Per-test-suite set-up. + // Called before the first test in this test suite. + // Can be omitted if not needed. + static void SetUpTestSuite() { + shared_resource_ = new ...; + } + + // Per-test-suite tear-down. + // Called after the last test in this test suite. + // Can be omitted if not needed. + static void TearDownTestSuite() { + delete shared_resource_; + shared_resource_ = NULL; + } + + // You can define per-test set-up logic as usual. + virtual void SetUp() { ... } + + // You can define per-test tear-down logic as usual. + virtual void TearDown() { ... } + + // Some expensive resource shared by all tests. + static T* shared_resource_; +}; + +T* FooTest::shared_resource_ = NULL; + +TEST_F(FooTest, Test1) { + ... you can refer to shared_resource_ here ... +} + +TEST_F(FooTest, Test2) { + ... you can refer to shared_resource_ here ... +} +``` + +NOTE: 위의 코드는 `SetUpTestSuite()`을 protected로 선언했지만 public으로 선언해야 하는 경우도 있습니다. 예를 들면 `TEST_P`와 같은 macro를 사용하기 위해서는 public으로 선언하게 됩니다. + +## Global Set-Up, Tear-Down + +개별 test case 혹은 test suite에서 set-up 및 tear-down을 설정하는 방법에 대해서 배웠습니다. Googletest는 test program 레벨에서도 이러한 설정이 가능하도록 지원합니다. 즉, 전역적인 set-up, tear-down을 구현할 수 있습니다. + +먼저, `::testing::Environment`라는 class를 상속받아서 test envrionment를 정의해야 합니다. Test environment는 전역적인 set-up, tear-down 설정을 도와줍니다. + +```c++ +class Environment : public ::testing::Environment { + public: + virtual ~Environment() {} + + // Override this to define how to set up the environment. + void SetUp() override {} + + // Override this to define how to tear down the environment. + void TearDown() override {} +}; +``` + +다음으로 위에서 상속박은 class의 object를 생성하고 `::testing:AddGlobalTestEnvironment()`을 통해 등록합니다. + +```c++ +Environment* AddGlobalTestEnvironment(Environment* env); +``` + +이제 `RUN_ALL_TESTS()`를 호출되면 등록된 envrinonment object의 `SetUp()`이 호출 될 것입니다. `SetUp`이 호출되는 동안 fatal failure가 발생하지 않고 `GTEST_SKIP()`도 호출되지 않았다면 이제 모든 테스트들르 수행하게 됩니다. `RUN_ALL_TESTS()`가 호출되면 `TearDown()`도 무조건 호출해 줍니다. (테스트의 수행여부와 관계없이 무조건 호출됩니다.) + +Eenvironment object를 여러개 등록하는 것도 허용됩니다. 그런 경우에 `SetUp()`은 등록된 순서대로 호출되며 `TearDown()`은 그 반대 순서로 호출됩니다. + +Environment object가 등록되었다면 그 소유권은 googletest로 옮겨지므로 사용자가 **삭제할 수 없습니다.** + +`ADDGlobalTestEnvironment()`는 `RUN_ALL_TESTS()`보다 먼저 호출해야 합니다. `RUN_ALL_TESTS()`는 일반적으로 `main()`에 구현하기 때문에 만약 사용자가 `gtest_main` 라이브러리를 `main()` 대신 사용하고 있다면 이러한 수정이 어려울 것입니다. 이렇게 `gtest_main` 라이브러리를 사용하는 사용자는 전역 변수를 통해 동일한 효과를 가져올 수 있습니다. + +```c++ +::testing::Environment* const foo_env = + ::testing::AddGlobalTestEnvironment(new FooEnvironment); +``` + +위와 같은 방법이 있긴 하지만 `AddGlobalTestEnvironment()`를 사용하려는 사용자는 `main()`을 직접 구현하기를 권장합니다. 1차적인 이유는 전역변수가 소스코드 가독성을 떨어트리기 때문이며 이에 더해 environment를 여러개 사용하면서 각각의 전역변수를 여러 파일에 분산시켜 놓는다면 문제가 발생할 수 있기 떄문입니다. 알다시피 C++ compiler의 전역변수 초기화 순서는 예측할 수 없으며 그로 인해 environment 간에 의존성이 필요한 경우에 목적하는 바를 달성하기 어려워지기 때문입니다. + +## Value-Parameterized Tests + +*Value-parameterized tests*를 적용하면 하나의 테스트 코드에 데이터(parameter)만 변경해가면서 다양한 테스트를 수행할 수 있습니다. 특히 아래와 같은 경우에 유용합니다. + +- cmd line flag에 따라 동작이 달라져야 하기 때문에 각 flag마다 검증해야 할 때 +- interface(abstract class)를 구현한 implementation class가 여러개 있을 때 +- 그 외 다양한 입력들에 대해서도 문제없이 잘 동작하는지 확인하고 싶을 때 (data-driven testing이라고도 불리우는 이 방법은 꼭 필요하지 않은데도 사용하는 경우가 자주 발생하기 때문에 남용하지 않도록 유의해야 합니다.) + +### Value-Parameterized Tests를 구현하는 방법 + +Value-parameterized tests를 구현하려면 먼저 fixture class를 만들어야 합니다. 이를 위해서 `::testting::Test`과 `::testing::WithParamInterface`을 함께 상속받아야 합니다. 특히 `::testing::WithParamInterface`는 abstract class이며 `T`는 parameter로 전달되는 값의 타입을 의미합니다. 좀 복잡한가요? 사실 위의 2가지를 상속받아서 구현해 놓은`::testing::TestWithParam`도 이미 제공하고 있습니다. `T`는 복사만 가능하다면 어떤 타입이라도 괜찮습니다. 다만, raw pointer를 사용한다면 가리키는 대상의 관리도 사용자가 직접 해야하는 부분은 주의하시기 바랍니다. + +NOTE: 만약 value-parameterized tests의 test fixture class에 `SetUpTestSuite()` 또는 `TearDownTestSuite()`을 사용하려면 **protected**가 아닌 **public** 영역에 선언해야 합니다. 그래야만 `TEST_P`를 사용할 수 있습니다. + +```c++ +class FooTest : + public testing::TestWithParam { + // You can implement all the usual fixture class members here. + // To access the test parameter, call GetParam() from class + // TestWithParam. +}; + +// Or, when you want to add parameters to a pre-existing fixture class: +class BaseTest : public testing::Test { + ... +}; +class BarTest : public BaseTest, + public testing::WithParamInterface { + ... +}; +``` + +위의 코드에서 `FooTest`는 `TestWithParam`만 상속받아서 손쉽게 value-parameterized test를 위한 test fixture class를 구현했습니다. 다만, 이렇게 test fixture를 새로 만드는 것이 아니라 이미 사용중인 test fixture를 value-parameterized test로 확장하고 싶다면 `BaseTest`, `BarTest`처럼 구현하면 됩니다. 이렇게 test fixture가 준비되었다면 `TEST_P`를 사용해서 test function을 정의하면 됩니다. 여기서 `_P`는 "parameterized" 또는 "pattern"을 의미합니다. 의미만 통한다면 어느 쪽으로 생각하든 괜찮습니다. + +```c++ +TEST_P(FooTest, DoesBlah) { + // Inside a test, access the test parameter with the GetParam() method + // of the TestWithParam class: + EXPECT_TRUE(foo.Blah(GetParam())); + ... +} + +TEST_P(FooTest, HasBlahBlah) { + ... +} +``` + +이제 테스트를 위한 데이터(parameter)만 정의하면 끝입니다. 이 때에는 `INSTANTIATE_TEST_SUITE_P`를 사용합니다. Googletest는 `INSTANTIATE_TEST_SUITE_P`와 조합해서 parameter를 생성하는데 쓰이는 여러가지 function을 함께 제공합니다. 이러한 function을 *parameter generator*라고 부르며 아래는 관련 내용을 정리한 표입니다. 더불어 아래 function들은 모두 `testing` namespace에 정의되어 있습니다. + +| Parameter Generator | Behavior | +| ----------------------------------------------- | ------------------------------------------------------------ | +| `Range(begin, end [, step])` | Yields values `{begin, begin+step, begin+step+step, ...}`. The values do not include `end`. `step` defaults to 1. | +| `Values(v1, v2, ..., vN)` | Yields values `{v1, v2, ..., vN}`. | +| `ValuesIn(container)` and `ValuesIn(begin,end)` | Yields values from a C-style array, an STL-style container, or an iterator range `[begin, end)`. | +| `Bool()` | Yields sequence `{false, true}`. | +| `Combine(g1, g2, ..., gN)` | Yields all combinations (Cartesian product) as std\:\:tuples of the values generated by the `N` generators. | + +보다 자세한 내용은 해당 function의 주석을 확인해보기 바랍니다. + +아래 예제는 test suite(`FooTest`)을 위한 value-parameter 3개(`"meeny"`, `"miny"`, `"moe"`)를 초기화하고 있습니다. 이것은 곧 "`TEST_P`로 정의한 test case들이 3개의 입력데이터(parameter)에 대해서도 문제없이 동작하는지 검증하고 싶다"를 의미합니다. 결과적으로 총 6개의 서로 다른 test function이 생성됩니다. (`TEST_P` 개수 x parameter 개수) + +```c++ +INSTANTIATE_TEST_SUITE_P(InstantiationName, + FooTest, + testing::Values("meeny", "miny", "moe")); +``` + +NOTE: 위의 코드는 전역범위나 특정 namespace에 포함되어야 합니다. Function 내부에서는 사용할 수 없습니다. + +NOTE: Don't forget this step! If you do your test will silently pass, but none of its suites will ever run! + +동일한 test suite에 대해서 value parameter를 여러번 초기화할 수도 있기 때문에 이들을 구분 할 방법이 필요합니다. 이 때 사용되는 값이 `INSTANTIATE_TEST_SUITE_P`의 첫번째 argument입니다. 따라서 첫 번째 파라미터는 중복되면 안됩니다. 아래는 지금까지 만들어 본 6개의 test function을 보여줍니다. + +* `InstantiationName/FooTest.DoesBlah/0` for `"meeny"` +* `InstantiationName/FooTest.DoesBlah/1` for `"miny"` +* `InstantiationName/FooTest.DoesBlah/2` for `"moe"` +* `InstantiationName/FooTest.HasBlahBlah/0` for `"meeny"` +* `InstantiationName/FooTest.HasBlahBlah/1` for `"miny"` +* `InstantiationName/FooTest.HasBlahBlah/2` for `"moe"` + +위의 test function들에 대해서도 [`--gtest_filter`](#running-a-subset-of-the-tests)를 사용해서 원하는 테스트만 수행할 수 있습니다. + +아래 예제는 test suite(`FooTest`)을 테스트하기 위한 데이터(parameter)를 추가하고 있습니다. 이 때 기존에 생성된 test function들과의 구분을 위해 `INSTANTIATE_TEST_SUITE_P`의 첫 번째 파라미터를 다르게 했음을 확인할 수 있습니다. + +```c++ +const char* pets[] = {"cat", "dog"}; +INSTANTIATE_TEST_SUITE_P(AnotherInstantiationName, FooTest, + testing::ValuesIn(pets)); +``` + +The tests from the instantiation above will have these names: + +* `AnotherInstantiationName/FooTest.DoesBlah/0` for `"cat"` +* `AnotherInstantiationName/FooTest.DoesBlah/1` for `"dog"` +* `AnotherInstantiationName/FooTest.HasBlahBlah/0` for `"cat"` +* `AnotherInstantiationName/FooTest.HasBlahBlah/1` for `"dog"` + +지금까지 본 것처럼 `INSTANTIATE_TEST_SUITE_P`는 `TEST_P` macro를 통해 정의한 *모든* test case에 대해 각 parameter마다 별도의 test function들을 생성해줍니다. 즉 `TEST_P` x parameter 개수 만큼의 test function이 생성됩니다. 이 때, `INSTANTIATE_TEST_SUITE_P`와 `TEST_P`의 구현순서는 중요하지 않으며 어떤 순서이든 관계 없이 test function들을 생성해 줍니다. + +좀 더 자세한 예제는 [sample7_unittest.cc](), [sample8_unittest.cc]()에서 확인이 가능합니다. + +### Value-Parameterized Abstract Tests 생성하기 + +위에서 하나의 test suite(`FooTest`)을 여러 데이터(parameter)를 통해 검증하는 방법에 대해 알아봤습니다. 이러한 value-parameterized tests를 라이브러리에 포함시켜서 다른 사람들이 사용할 수 있도록 공유하는 것도 좋은 방법입니다. 또 이러한 방법론을 *abstract test*라고 부릅니다. 어떤 interface를 설계하면서 그 interface를 테스트하기 위한 코드도 제공한다면 interface 사용자들에게 도움이 될 것입니다. Interface를 구현하게 될 다양한 사용자를 아우르려면 아무래도 일반적인 관점에서 구현되어야 하기 때문에 standard suite of abstract tests를 제공해서 interface의 implementation class들을 검증할 수 있도록 해야 합니다. Standard suite of abstract tests를 구현하는 방법의 하나로는 factory function(test parameter을 전달받으면 test case를 생성해주는)을 제공하는 것도 괜찮습니다. 결론을 말하면 상위 interface의 test suite(abstract tests)을 만들어서 하나의 test suite만 가지고도 해당 interface를 구현한 implementation class들을 검증하는데 공통적으로 사용하자는 것입니다. 이렇게 설계가 된다면 implementation class만을 위한 test suite을 만들 때도 abstract test를 상속받기만 하면 쉽게 만들 수 있습니다. + +Abstract test를 정의하기 위해서는 아래와 같이 코드를 구성하면 됩니다. + +1. Abstract test를 *선언합니다.* 예를들어 `FooTest`라는 test fixture class를 `foo_param_test.h`라는 헤더파일에 저장합니다. +1. Abstract test를 *구현합니다.* 이 때는 `foo_param_test.cc`와 같은 소스파일을 만들고 헤더파일(`foo_param_test.h`)을 포함시킵니다. 내부에는 `TEST_P`를 사용해서 test case를 구현합니다. + +이렇게 abstract test의 구현이 완료되면 라이브러리로 배포합니다. 그럼 사용자는 abstract test 라이브러리를 링크하고 코드에는 `#include foo_param_test.h` 를 포함시킵니다. 그러면 끝입니다. 이제 `INSTANTIATE_TEST_SUITE_P()`를 사용해서 원하는 데이터에 대한 test function들을 생성하면 됩니다. 이를 통해 abstract test suite 라이브러리만 포함하면 자신이 필요한 value parameter를 가지고 test case를 생성할 수 있게 됩니다. 위에서도 확인한 내용이지만 `INSTANTIATE_TEST_SUITE_P()`는 첫번째 argument만 다르다면 동일한 test suite에다가 여러번 사용하는 것도 가능합니다. (소스파일이 서로 다르면 더욱 좋습니다.) + +### Value-Parameterized Test Parameters 이름짓기 + +지금까지는 `INSTANTIATE_TEST_SUITE_P()`에 3개의 argument만 사용했지만 필요하다면 4번째 argument도 사용할 수 있습니다. 4번째 argument로는 function(또는 functor)을 전달할 수 있으며 그 목적은 test function의 이름을 만들 때 parameter를 사용하기 위함입니다. (3개의 argument만 사용하면 0,1,2와 같은 숫자로 test function의 이름이 정해졌습니다.) 그리고 전달되는 function(또는 functor)은 `testing::TestParamInfo` 타입의 값을 전달받아서 `std::string`을 반환하는 타입의 function이어야 합니다. + +Googletest는 이러한 4번째 argument를 쉽게 사용할 수 있도록 `testing::PrintToStringParamName`라는 builtin test suffix generator를 제공하고 있습니다. 단, builtin test suffix generator는 `std:string`이나 C string은 지원하지 않으며 `test::PrintToString(GetParam())`이라는 return type을 통해서 `std::string`으로 변환하여 반환하게 됩니다. + +NOTE: 모든 test case(혹은 test function)의 이름은 유일해야 하고 ASCII 문자나 숫자만 포함할 수 있으며 값이 없어도 안 됩니다. 마지막으로 [should not contain underscores](https://github.com/google/googletest/blob/master/googletest/docs/faq.md#why-should-test-suite-names-and-test-names-not-contain-underscore)에 명시된 것처럼 밑줄(`_`)은 사용할 수는 있지만 최대한 지양해야 합니다. + +아래는 4번재 argument에 builtin test suffix generator를 사용한 예제입니다. + +```c++ +class MyTestSuite : public testing::TestWithParam {}; + +TEST_P(MyTestSuite, MyTest) +{ + std::cout << "Example Test Param: " << GetParam() << std::endl; +} + +INSTANTIATE_TEST_SUITE_P(MyGroup, MyTestSuite, testing::Range(0, 10), + testing::PrintToStringParamName()); +``` + +이에 반해서 아래코드처럼 custom function(functor)을 직접 구현해서 사용하면 test parameter name을 보다 세밀하게 조작할 수도 있습니다. 물론, builtin test suffix generator가 나쁜 것은 아니지만 사용자가 입력한 parameter를 `std::string`으로 자동형변환하기 때문에 사용성이 떨어지는 경우가 발생하기도 합니다. 아래 예제는 parameter, enumeration type, string을 가지고 test parameter name을 사용자가 직접 생성하는 코드를 보여줍니다. (코드를 간결하게 하기 위해 labmda를 사용했습니다.) + +```c++ +enum class MyType { MY_FOO = 0, MY_BAR = 1 }; + +class MyTestSuite : public testing::TestWithParam> { +}; + +INSTANTIATE_TEST_SUITE_P( + MyGroup, MyTestSuite, + testing::Combine( + testing::Values(MyType::VALUE_0, MyType::VALUE_1), + testing::ValuesIn("", "")), + [](const testing::TestParamInfo& info) { + string name = absl::StrCat( + std::get<0>(info.param) == MY_FOO ? "Foo" : "Bar", "_", + std::get<1>(info.param)); + absl::c_replace_if(name, [](char c) { return !std::isalnum(c); }, '_'); + return name; + }); +``` + +## Typed Tests + +동일한 요구사항을 수행하기 위한 interface가 있고 이를 위한 implementation class가 여러개 있다고 가정해 봅니다. 이 때, implementation class는 하나의 타입으로도 볼 수 있기 때문에 위의 상황은 결국 같은 동작을 여러가지 타입에 대해서 수행한다는 의미와 동일합니다. 특히, 이 경우에는 상위 interface가 동일하기 때문에 해당 interface에 대한 테스트가 다양한 타입(implementation class)을 커버할 수 있다면 좋을 것입니다. 이러한 테스트 방법을 *typed tests*라고 합니다. 위의 value-parameterized tests가 하나의 테스트를 가지고 여러가지 값(parameter)을 검증했다면, typed tests는 하나의 테스트를 가지고 여러가지 타입을 검증하게 됩니다. + +물론 `TEST` 또는 `TEST_F`를 템플릿으로 만들거나 여러가지 타입에 대해서 모두 동작하도록 여러개를 구현하는 것도 가능합니다. 단, 그렇게되면 `m`개의 test case를 `n`개의 타입에 대해서 수행하려 할 때 `m*n`개의 `TEST`를 구현하게 될 수도 있습니다. 이런 작업은 힘들고 또 유지보수하기도 힘들 것입니다. + +*Typed tests*는 이러한 내용을 보다 쉽게 구현할 수 있도록 도와줍니다. 검증할 대상타입이 무엇인지 미리 알고 있어야 한다는 단점은 있지만, 구현을 비롯한 다른 측면에서는 더 좋습니다. + +먼저, test fixture class를 구현합니다. 이 때, `::testing::Test`를 상속받는 부분은 일반적인 test fixture와 동일하지만 template class으로 선언해야 합니다. + +```c++ +template +class FooTest : public ::testing::Test { + public: + ... + typedef std::list List; + static T shared_; + T value_; +}; +``` + +이제 위에서 정의한 test fixture class와 검증하고자 하는 타입들을 서로 연결합니다. 구현방법은 아래와 같습니다. + +```c++ +using MyTypes = ::testing::Types; +TYPED_TEST_SUITE(FooTest, MyTypes); +``` + +위 코드 `TYPED_TEST_SUITE`의 2번째 argument는 여러가지 타입을 전달받고 있습니다. 이렇게 타입을 전달 할 때는 `using`이나 `typedef`를 통해서 alias를 만든 다음에 그 alias를 전달해야 합니다. 그렇지 않으면 compiler가 comma(`,`)를 제대로 해석하지 못합니다. + +이제 test case를 정의할 시간입니다. Typed tests에서는 `TEST_F()` 대신에 `TYPED_TEST()`를 사용해서 구현해야 합니다. 관련 예제코드는 아래와 같습니다. + +```c++ +TYPED_TEST(FooTest, DoesBlah) { + // Inside a test, refer to the special name TypeParam to get the type + // parameter. Since we are inside a derived class template, C++ requires + // us to visit the members of FooTest via 'this'. + TypeParam n = this->value_; + + // To visit static members of the fixture, add the 'TestFixture::' + // prefix. + n += TestFixture::shared_; + + // To refer to typedefs in the fixture, add the 'typename TestFixture::' + // prefix. The 'typename' is required to satisfy the compiler. + typename TestFixture::List values; + + values.push_back(n); + ... +} + +TYPED_TEST(FooTest, HasPropertyA) { ... } +``` + +전체 예제코드는 [sample6_unittest.cc]()에서 확인할 수 있습니다. + +## Type-Parameterized Tests + +*Type-parameterized tests* 는 typed tests와 유사하지만 필요한 타입을 미리 지정할 필요가 없다는 점이 다릅니다. 테스트만 미리 구현해 놓은 상태에서 타입은 나중에 지정해도 괜찮습니다. 원한다면 여러번 지정할 수도 있습니다. + +Typed tests에서도 이야기했지만, interface를 설계하고 있다면 해당 interface를 상속받은 다양한 타입(implementation class)을 검증하는 방법도 준비하는 것이 좋습니다. 이 때, type-parameterized tests를 사용하면 각 타입에 대한 테스트를 중복해서 구현하지 않아도 됩니다. + +먼저 typed tests에서 했던 것처럼 test fixture class를 template class으로 정의합니다. + +```c++ +template +class FooTest : public ::testing::Test { + ... +}; +``` + +다음으로 위에서 만든 template class가 type-parameterized test임을 googletest에 알려줍니다. + +```c++ +TYPED_TEST_SUITE_P(FooTest); +``` + +이제 test case를 구현합니다. 일반적인 과정은 기존과 동일합니다. 단, 이제까지 써왔던 macro(`TEST`, `TEST_F`, `TYPED_TEST`)가 아니라 `TYPED_TEST_P()`라는 macro를 사용해야 합니다. + +```c++ +TYPED_TEST_P(FooTest, DoesBlah) { + // Inside a test, refer to TypeParam to get the type parameter. + TypeParam n = 0; + ... +} + +TYPED_TEST_P(FooTest, HasPropertyA) { ... } +``` + +다음으로 위에서 구현한 test suite과 test case들을 `REGISTER_TYPED_TEST_SUITE_P`를 통해서 연결해줍니다. Macro의 첫 번째 argument는 test suite 이름이고, 그 다음부터는 각 test case들의 이름을 하나하나 작성합니다. 관련 코드는 아래와 같습니다. + +```c++ +REGISTER_TYPED_TEST_SUITE_P(FooTest, + DoesBlah, HasPropertyA); +``` + +여기까지 했으면 모든 사용준비가 끝났으며 위의 내용들을 헤더파일에 넣어서 제공한다면 재사용성 측면에서도 좋을 것입니다. 사용할 떄는 테스트하려는 타입만 초기화하면 됩니다. 아래는 예제코드입니다. + +```c++ +typedef ::testing::Types MyTypes; +INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, MyTypes); +``` + +타입 초기화를 위한 macro는 `INSTANTIATE_TYPED_TEST_SUITE_P`입니다. 여기서 macro의 첫 번째 argument는 test suite 이름을 만들 때의 prefix로 사용되기 때문에 중복해서 사용하면 안됩니다. + +마지막으로 타입이 1개뿐이라면 굳이 alias를 만들지 않고 해당 타입이름을 바로 사용해도 됩니다. + +```c++ +INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, int); +``` + +전체 예제코드는 [sample6_unittest.cc]()에서 확인할 수 있습니다. + +## Private Code 테스트하기 + +어떤 software의 interface가 아니라 내부구현만을 변경하고 있다면 interface에 대한 테스트는 변경작업에 관계없이 잘 동작해야 되는게 맞습니다. 이처럼 어떤 software를 외부사용자(client) 입장에서 테스트하는 접근방법을 **black-box testing**이라고 합니다. 예를 들어 해당 software가 class라면 public method에 대한 테스트를 주로 구현하게 될 것입니다. + +**Black-box testing** 관점에서 볼 때, public method가 아니라 내부 구현에 대한 테스트가 필요하다고 느꼈다면 **사실은 테스트에 앞서 class 설계에 문제가 없는지를 먼저 검토해야 합니다.** 예를 들어 대상 class가 너무 많은 일을 하는건 아닌지 점검하고 문제가 있다면 기존 class를 분리하는 등의 설계변경이 필요할 수도 있습니다. (구현을 담당하는 implemetation class를 새로 추가하는 것도 해결방법이 될 수가 있습니다.) + +이유야 어찌 됐건, 외부로 노출되는 interface(public method)가 아니라 내부 구현에 대한 테스트를 하고 싶다고 가정해 봅시다. 아마도 아래와 같은 function들이 그러한 테스트의 대상이 될 것입니다. + +* Static function(class의 static method를 의미하는 것이 아니라 c-style global function을 의미합니다.) 또는 unnamed namespace에 정의된 function +* Class의 private method 또는 protected method + +이들을 테스트하기 위해서, 아래와 같은 방법을 사용할 수 있습니다. + +* 기본적으로 static function과 unnamed namespace function은 어디서든 사용이 가능합니다. 따라서 이들을 테스트하려면 해당 function이 구현된 `.cc` 파일만 `#include` 하면 됩니다. 다만, `.cc` 파일을 include하는 것이 좋은 구현이라고 볼 수는 없기 때문에 꼭 필요한 경우에 테스트코드에서만 사용해야 합니다. + + 더 좋은 방법은 이들을 특정한 namespace에 모아서 `private`하게 사용하는 것입니다. 예를 들어 진행중인 프로젝트에서 일반적인 사용하는 namespace가 `foo`라면 `foo::internal`이라는 namespace를 새로 만들고 거기에서 `private`한 function들을 모두 모아서 관리하는 것입니다. 그런 다음에 `-internal.h`와 같은 헤더파일을 통해 사용하면 됩니다. 다만, 이 과정에서 주의할 점은 `foo::internal`과 `-internal.h`는 해당 프로젝트의 개발자들만 접근할 수 있도록 제한해야한다는 점입니다. 이러한 방법을 이용하면 외부에 구현을 노출시키지 않고도 테스트가 가능해집니다. + +* Class의 private method는 해당 class 혹은 friend 에서만 호출이 가능합니다. 따라서 테스트대상 class에서 test fixture class나 혹은 method를 friend로 선언하는 작업이 필요합니다. 그런데 아직도 문제가 있습니다. 왜냐하면 googletest의 test function들은 사실 내부적으로는 test fixture class를 상속받고 있기 때문에 test function에서 테스트대상의 private 영역에 바로 접근할 수가 없습니다. C++에서 base class끼리 friend라고 해도 그 관계가 dervied class로 전달되지는 않기 때문입니다.(아버지의 친구가 아들의 친구가 아니듯이) 이 문제를 해결하기 위해서 test fixture class에 테스트대상의 private 영역에 대한 accessor function도 구현해야 합니다. 그래야만 test function에서 해당 accessor를 통해 테스트대상의 private영역에 접근할 수 있습니다. + + 위에서 설명했듯이 여기서도 전체적인 리팩토링을 하는 것이 하나의 방법이 될 수 있습니다. 방법은 테스트대상의 private function들을 implementation class로 분리하는 것입니다 그 다음에 `*-internal.h`와 같이 개발자만 사용할 수 있는 헤더파일을 만들고 사용하면 됩니다. 이 헤더파일을 외부에서는 사용할 수 없도록 제한하면 마치 private function인 것처럼 사용할 수 있습니다. 이러한 방법은 상당히 널리 사용되는 방법으로서 소위 [Pimpl](https://www.gamedev.net/articles/programming/general-and-gameplay-programming/the-c-pimpl-r1794/)(Private Implementation) idiom이라고도 부릅니다. + + 다시 friend 사용법으로 돌아와서 googletest는 friend관계를 간단히 사용하기 위한 기능도 제공하고 있습니다. 테스트대상 class에서 아래와 같이 사용하면 됩니다. 별도의 accessor function을 구현할 필요가 없다는 장점이 있으며 필요한 test function별로 지정해야 합니다. + + ```c++ + FRIEND_TEST(TestSuiteName, TestName); + ``` + + 아래는 예제코드입니다. + + ```c++ + // foo.h + class Foo { + ... + private: + FRIEND_TEST(FooTest, BarReturnsZeroOnNull); + + int Bar(void* x); + }; + + // foo_test.cc + ... + TEST(FooTest, BarReturnsZeroOnNull) { + Foo foo; + EXPECT_EQ(foo.Bar(NULL), 0); // Uses Foo's private member Bar(). + } + ``` + + `FRIEND_TEST`를 사용할 때, 주의사항으로 테스트대상 class가 특정한 namespace에 선언되어 있는 경우에는 test fixture와 test function도 같은 namespace에 구현해야 한다는 것입니다. 그래야만 friend관계가 정상적으로 맺어질 것입니다. 아래 예제를 확인하세요. + + ```c++ + namespace my_namespace { + + class Foo { + friend class FooTest; + FRIEND_TEST(FooTest, Bar); + FRIEND_TEST(FooTest, Baz); + ... definition of the class Foo ... + }; + + } // namespace my_namespace + ``` + + 위의 `my_namespace::Foo` class를 테스트하기 위한 코드는 아래와 같습니다. + + ```c++ + namespace my_namespace { + + class FooTest : public ::testing::Test { + protected: + ... + }; + + TEST_F(FooTest, Bar) { ... } + TEST_F(FooTest, Baz) { ... } + + } // namespace my_namespace + ``` + +## "Catching" Failures + +Googletest를 이용해서 테스트 도구를 만든다면, 테스트 도구가 잘 동작하는지도 확인이 필요합니다. 역설적이만 그런 경우에도 googletest를 사용할 수 있습니다. + +테스트 도구의 목적은 failure를 잘 찾아내는 데에 있습니다. 따라서 그러한 테스트 도구를 검증하려면 원하는 상황에서 failure가 발생하는지 테스트하면 됩니다. 근데 만약, 테스트 도구가 failure를 알리기 위한 방법으로 exception을 던지도록 구현되어 있다면 어떻게 해야 할까요? 알다시피 googletest는 기본적으로 exception를 사용하지 않고 있습니다. + +이를 위해서는 `gunit-spi.h`라는 파일을 사용해야 합니다. 이 파일에는 exception 확인이 가능한 macro들을 제공하고 있습니다. 아래 내용을 확인하세요. + +```c++ + EXPECT_FATAL_FAILURE(statement, substring); +``` + +위 macro는 `statement`가 fatal failure를 발생시키는지 확인합니다. 또한, 해당 fatal failure의 failure message가 `substring`를 포함하는지도 확인합니다. + +```c++ + EXPECT_NONFATAL_FAILURE(statement, substring); +``` + +위 macro도 동작은 동일하며 대신 non-fatal failure를 확인하는데 사용합니다. + +위의 2가지 macro는 current thread에서 발생한 failure들만 확인할 수 있습니다. 따라서 current thread가 아닌 thread에서 failure가 발생하면 검증이 불가능합니다. 그럼 `statement`에서 생성한 thread에서 failure가 발생했다면 어떻게 확인해야 할까요? 그런 경우에는 아래 macro를 사용하기 바랍니다. + +```c++ + EXPECT_FATAL_FAILURE_ON_ALL_THREADS(statement, substring); + EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS(statement, substring); +``` + +NOTE: Windows환경에서는 위와 같이 multiple thread에서 failure를 확인하는 방법을 사용할 수 없습니다. + +위의 macro를 사용함에 있어서 아래와 같은 제약사항은 주의하시기 바랍니다. + +1. 위의 macro들은 failure message를 출력할 수 없습니다. + +1. fatal failure(`EXPECT_FATAL_FAILURE*`) macro의 `statement`에서 `this`의 non-static local variable 및 non-staic method를 참조할 수는 없습니다. + +1. fatal failure(`EXPECT_FATAL_FAILURE*`) macro의 `statement`는 값을 반환하면 안 됩니다. + +## 개별 Test를 런타임에 등록하기 + +Googletest에서 test case를 정의할 때 `*TEST*`계열 macro를 사용하는 것이 일반적입니다. 실제로도 대다수의 개발자들이 `*TEST*`계열 macro를 사용하고 있으며 이는 곧 test case들이 compile time에 등록됨을 의미합니다. 그럼 혹시 test case를 runtime에 등록하고 싶은 사용자가 있으신가요? 그러한 예로는 별도의 설정파일(.config 등)에서 개별 test case의 정보를 관리하다가 test program이 실행된 후에 설정파일을 읽어서 test case를 등록하고 싶은 경우가 있을 것입니다. Googletest는 그러한 사용자를 위해서 `::testing::RegisterTest`를 제공합니다. + +단, `::testing::RegisterTest`는 복잡도가 상당히 높기 때문에 꼭 필요한 경우에만 사용하기 바랍니다. 되도록이면 `*TEST*`를 통해서 정적으로 등록하는 것이 좋습니다. + +그럼 googletest에서 제공하는 `::testing::RegisterTest`의 정의는 아래와 같습니다. + +```c++ +template +TestInfo* RegisterTest(const char* test_suite_name, const char* test_name, + const char* type_param, const char* value_param, + const char* file, int line, Factory factory); +``` +마지막 argument인 `factory`는 test case(function)를 생성할 수 있는 factory function를 의미합니다. 이 때는 꼭 function pointer가 아니라더라도 factory역할을 할 수 있는 callable object라면 어느것이라도 괜찮습니다. 단, 어떤 걸 사용하든 그 return type은 `Factory`타입이 되어야 하며 여기서 `Factory`는 test suite class를 의미합니다. 위 내용들은 곧 첫 번째 argument이자 구분자인 `test_suite_name`에 속한 모든 test case들은 동일한 return type(test fixture class)을 가져야 함을 의미합니다. 이제 이러한 제약사항들이 모두 runtime에 점검될 것입니다. + +Googletest는 `factory`로부터 test fixture class를 추론할 수 있기 때문에 test suite class의 function(`SetUpTestSuite`, `TearDownTestSuite `등)들도 호출할 수 있게 됩니다. + +위의 모든 내용들이 `RUN_ALL_TEST()`보다 앞서 수행되어야 하며 그렇지 않다면 미정의 동작이므로 문제가 될 것입니다. 그럼 아래 예제를 확인하세요. + +```c++ +class MyFixture : public ::testing::Test { + public: + // All of these optional, just like in regular macro usage. + static void SetUpTestSuite() { ... } + static void TearDownTestSuite() { ... } + void SetUp() override { ... } + void TearDown() override { ... } +}; + +class MyTest : public MyFixture { + public: + explicit MyTest(int data) : data_(data) {} + void TestBody() override { ... } + + private: + int data_; +}; + +void RegisterMyTests(const std::vector& values) { + for (int v : values) { + ::testing::RegisterTest( + "MyFixture", ("Test" + std::to_string(v)).c_str(), nullptr, + std::to_string(v).c_str(), + __FILE__, __LINE__, + // Important to use the fixture type as the return type here. + [=]() -> MyFixture* { return new MyTest(v); }); + } +} +... +int main(int argc, char** argv) { + std::vector values_to_test = LoadValuesFromConfig(); + RegisterMyTests(values_to_test); + ... + return RUN_ALL_TESTS(); +} +``` + +## 현재 수행중인 Test의 이름 확인하기 + +테스트코드를 구현하다 보면 현재 실행중인 test case의 이름을 확인하고 싶은 경우도 있을 것입니다. 예를 들어 test case의 이름에 따라 golden file을 바꿔가며 테스트를 수행할 수도 있습니다. (golden file: 테스트의 결과를 저장하거나 비교하기 위해 생성하는 파일) Googletest의 `::testing::TestInfo` class는 이러한 정보를 가지고 있으며 사용자도 이러한 정보를 사용할 수 있습니다. + +```c++ +namespace testing { + +class TestInfo { + public: + // Returns the test suite name and the test name, respectively. + // + // Do NOT delete or free the return value - it's managed by the + // TestInfo class. + const char* test_suite_name() const; + const char* name() const; +}; + +} +``` + +이런 `TestInfo ` class를 사용하고 싶다면 `UnitTest`(singleton object)의 member function인 `current_test_info()`를 사용하면 됩니다. + +```c++ + // Gets information about the currently running test. + // Do NOT delete the returned object - it's managed by the UnitTest class. + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + + printf("We are in test %s of test suite %s.\n", + test_info->name(), + test_info->test_suite_name()); +``` + +`current_test_info()`가 호출되었는데 실행중인 test case가 없다면 null pointer를 반환하게 됩니다. 예를 들면 `TestSuiteSetUp()`, `TestSuiteTearDown()`과 같은 function들은 test case를 실행중인 상황이 아니기 때문에 null pointer가 반환됩니다. + +## Test Events를 통해 googletest 확장하기 + +Googletest는 test program의 진척상황이나 test failure를 알려주기 위한 **event listener API**라는 기능을 제공하고 있습니다. 이 때 발생하는 event들은 test program, test suite, test case 각각의 시작시점과 종료시점에서 수신해 볼 수 있습니다. 이 기능이 사용가능한 분야로는 console이나 XML로 출력되는 output을 변경하고 싶은 경우나 아니면 resource leak checker를 위한 checkpoint가 필요한 경우 등이 있습니다. + +### Event Listeners 정의하기 + +Event listener를 정의하려면 `testing::TestEventListener` 또는 `testing::EmptyTestEventListener`를 상속받는 class를 하나 만들어야 합니다. 먼저, `testing::TestEventListener`는 사용했다면 이는 interface(abstract class)이기 때문에 포함된 *pure virtual function을 모두 override 해야 합니다.* 다음으로 `testing::EmptyTestEventListener`의 member function들은 기본적인 구현(사실, 비어 있음)을 제공하기 때문에 필요한 것만 override해서 구현하면 됩니다. + +이제 상속이 끝났다면 event를 처리하기 위한 동작을 직접 구현하면 됩니다. 기본적인 동작은 event가 발생하면 handler function들이 자동으로 호출되게 됩니다. 이 때 handler function으로는 아래와 같은 타입의 argument들이 전달됩니다. + +- UnitTest는 test program 전체의 상태를 가지고 있습니다. +- TestSuite는 이름 그대로 test suite의 정보를 가지고 있습니다. Test suite은 1개 이상의 test case를 가지고 있습니다. +- TestInfo는 현재 test case의 정보를 가지고 있습니다. +- TestPartResult는 test assertion의 결과를 가지고 있습니다. + +사용자는 event handler function에서 이러한 argument를 사용할 수가 있습니다. 아래는 이에 대한 예제 코드입니다. + +```c++ + class MinimalistPrinter : public ::testing::EmptyTestEventListener { + // Called before a test starts. + virtual void OnTestStart(const ::testing::TestInfo& test_info) { + printf("*** Test %s.%s starting.\n", + test_info.test_suite_name(), test_info.name()); + } + + // Called after a failed assertion or a SUCCESS(). + virtual void OnTestPartResult(const ::testing::TestPartResult& test_part_result) { + printf("%s in %s:%d\n%s\n", + test_part_result.failed() ? "*** Failure" : "Success", + test_part_result.file_name(), + test_part_result.line_number(), + test_part_result.summary()); + } + + // Called after a test ends. + virtual void OnTestEnd(const ::testing::TestInfo& test_info) { + printf("*** Test %s.%s ending.\n", + test_info.test_suite_name(), test_info.name()); + } + }; +``` + +### Event Listeners 사용하기 + +위에서 event listener를 정의하는 방법에 대해서 배웠습니다. 다음으로 정의된 event listener를 실제로 사용하기 위해서는 해당 event listener의 instance를 생성하고 이를 googletest event listener list에 추가하는 작업이 필요합니다. `main()` function을 구현할 때, `return RUN_ALL_TESTS()`를 필수적으로 호출해야 한다고 배웠습니다. 그보다 앞 부분에서 event listener를 추가해주면 됩니다. 이 때 사용하는 class를 `TestEventListeners`라고 부릅니다. 자세히 보면 class 이름에 `s`가 붙어 있는데요, 이는 googletest에 여러개의 event listener를 등록하는 것도 가능함을 의미합니다. 설명은 끝났습니다. 그럼 아래는 예제코드입니다. + +```c++ +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + // Gets hold of the event listener list. + ::testing::TestEventListeners& listeners = + ::testing::UnitTest::GetInstance()->listeners(); + // Adds a listener to the end. googletest takes the ownership. + listeners.Append(new MinimalistPrinter); + return RUN_ALL_TESTS(); +} +``` + +아마도 위 코드를 구현한 사람은 `MinimalistPrinter`라는 event listener를 통해 console이나 XML 등으로 출력되는 테스트 결과를 최소화려고 했을 것입니다. 따라서 원하는 목적을 달성하려면 기존에 동작하고 있던 default test result printer는 해제해줘야 합니다. 이를 위한 코드는 아래와 같습니다. + +```c++ + ... + delete listeners.Release(listeners.default_result_printer()); + listeners.Append(new MinimalistPrinter); + return RUN_ALL_TESTS(); +``` + +이제 다 끝났습니다. 의도한 대로 결과가 출력되는지 확인하시기 바랍니다. 이에 대한 보다 자세한 내용은 [sample9_unittest.cc]()에서 확인하시기 바랍니다. + +앞에서 얘기한 것처럼 1개 이상의 listener를 사용하는 것도 가능합니다. 따라서 동일한 event에 대해서 listenere의 호출순서가 어떻게 되는지는 알아두는게 좋습니다. 주의할 점은 handler function의 종류에 따라서 순서가 다르다는 점입니다. 예를 들어 `On*Start()` 또는 `OnTestPartResult()`라는 event가 발생하면 list에 등록된 listener의 순서대로 호출해 줍니다. 간단히 말하면 새로 등록한 listener일수록 나중에 호출된다는 의미입니다. 반대로 `On*End()`와 같은 event는 등록순서의 *역방향*으로 listener handler를 호출해줍니다. 따라서 사용자가 결과물을 보게 되면 먼저 등록된 listener handler의 출력물이 늦게 등록된 listener handler의 출력물을 감싸는 (프레임) 형태가 됩니다. + +### Listeners에서 failure 발생시키기 + +Event를 처리할 때에도 `EXPECT_*()`, `ASSERT_*()`, `FAIL()`와 같은 assertion을 사용할 수 있습니다. 단, 아래 내용들을 주의하기 바랍니다. + +1. `OnTestPartResult()`안에서는 어떤 failure라도 발생시키면 안됩니다. 만약 `OnTestPartResult()`에서 failure가 발생하면 무한히 재귀호출하게 됩니다. +1. `OnTestPartResult()`를 처리하는 listener도 failure를 발생시키면 안 됩니다. + +만약 `OnTestPartResult()`를 처리하는 listener가 있다면 listener list에 먼저 등록해야 합니다. 그런 후에 failure가 발생할 수 있는 listener들은 등록하기 바랍니다. 그렇게 해야만 failure정보가 정상적으로 기록될 수 있습니다. + +Failure-raising listener에 대한 예제코드는 [sapmle10_unittest.cc]()를 참고하세요. + +## Test program을 실행하는 다양한 방법 + +Googletest를 사용한 test program도 일반적인 실행파일과 다르지 않습니다. 빌드된 후에는 자유롭게 실행할 수 있고, 실행시에 cmd line을 통해 flag를 전달할 수도 있습니다. 그리고 test program에서 이렇게 cmd line을 통해 전달된 flag들을 적용하기 위해서는 `RUN_ALL_TESTS()`를 호출하기 전에 `::testing::InitGoogleTest()`를 먼저 호출하면 됩니다. + +지원하는 flag 목록을 확인하고 싶다면 test program을 실행할 때, `--help`를 입력하면 됩니다. `--help`외에 `-h`, `-?`, `/?` 등도 동일한 목적으로 사용할 수 있습니다. + +만약 동일한 옵션에 대해서 2가지 방법(코드에서 직접 environment variable 변경, cmd line flag 전달)을 사용했다면 cmd line flag를 우선적으로 적용합니다. + +### 필요한 Test 선택하기 + +#### Test 목록보기 + +test program을 시작하기 전에 해당 프로그램에서 사용가능한 테스트 목록을 확인할 수 있습니다. 더불어 이렇게 목록을 확인한 후에는 filter기능을 사용해서 필요한 테스트만 선택해서 실행하는 것이 가능합니다.(밑에서 설명) 이를 위해서는 `--gtest_list_tests`라는 flag를 입력하면 됩니다. + +```none +TestSuite1. + TestName1 + TestName2 +TestSuite2. + TestName +``` + +`--gtest_list_tests` flag를 사용하면 테스트는 수행되지 않으며 단지 목록만 출력합니다. 더불어 이 옵션은 environment variable로는 설정할 수 없는 옵션입니다. + +#### 전체 Test 중에서 일부만 수행하기 + +기본적으로 googletest는 test program에 정의된 모든 테스트를 수행합니다. 그러나 `GTEST_FILTER`라는 environment variable을 사용하면 디버깅 혹은 빠른 확인을 위해 테스트 중 일부만 수행할 수도 있습니다. 동일한 옵션을 `--gtest_filter`라는 cmd line flag를 통해서도 지정할 수 있습니다. 그러면 googletest는 테스트의 이름이 filter와 매칭되는 경우에만 수행하게 됩니다. (포맷 : `TestsuiteName.TestName`) + +Filter를 사용할 때, *positive pattern*(매칭되는 테스트 수행함)은 바로 이름을 적으면 되고 *negative pattern*(매칭되는 테스트 수행안함)는 이름 앞에 '`-`' 를 적어야 합니다. Positive, negative 각각에서도 여러개의 pattern을 지정할 수 있는데 그런 경우에는 seperator '`:`'를 사용하면 됩니다. 순서로 보면 *positive pattern*을 (1개 혹은 여러개) 적은 후에, *negative pattern*을 (1개 혹은 여러개) 적습니다. 이렇게 positive pattern과 매칭되면서 negative pattern과는 매칭되지 않는 테스트만 수행됩니다. + +Pattern을 만들때는 `'*'`, `'?'`와 같은 wildcard도 사용할 수 있습니다. 이를 이용하면 아래처럼 간단한 표현이 가능해집니다. 또한, 아래와 같은 경우에는 '`*`'를 생략하는 것도 가능합니다. + + `'*-NegativePatterns'` → `'-NegativePatterns'` (둘 다 가능) + +예제를 통해 다양한 사용법을 확인해세요. + +* `./foo_test` : 모든 테스트 +* `./foo_test --gtest_filter=*` : 모든 테스트 +* `./foo_test --gtest_filter=FooTest.*` : "`FooTest`" 라는 Testsuite에 포함된 모든 테스트 +* `./foo_test --gtest_filter=*Null*:*Constructor*` : 전체 이름에 "`Null`"혹은 "`Constructor`"가 포함된 모든 테스트 +* `./foo_test --gtest_filter=-*DeathTest.*` : "`DeathTest`"가 아닌 모든 테스트 +* `./foo_test --gtest_filter=FooTest.*-FooTest.Bar` : "`FooTest`" 에서 "`FooTest.Bar`"를 제외한 모든 테스트 +* `./foo_test --gtest_filter=FooTest.*:BarTest.*-FooTest.Bar:BarTest.Foo` : "`FooTest`"에서 "`FooTest.Bar`"를 제외한 모든 테스트와 "`BarTest`"에서 "`BarTest.Foo`"를 제외한 모든 테스트 + +#### Test를 임시로 비활성화하기 + +어떤 문제로 인해 전체 테스트를 진행할 수 없다면, 문제를 일으키는 test case를 `DISABLED_` prefix를 통해 해당 비활성화 할 수 있습니다. 이 방법은 `#if 0` 으로 막는 것보다 유용할 것입니다. 왜냐하면 단지 실행만 안될 뿐이지 컴파일 대상에는 포함되기 때문에 계속해서 유지보수될 수 있기 때문입니다. + +물론, test case뿐만 아니라 test suite을 비활성화 하는 것도 가능합니다. 방법은 똑같이 "`DISALBED_`" prefix만 붙여주면 됩니다. + +예제코드는 아래와 같습니다. + +```c++ +// Tests that Foo does Abc. +TEST(FooTest, DISABLED_DoesAbc) { ... } + +class DISABLED_BarTest : public ::testing::Test { ... }; + +// Tests that Bar does Xyz. +TEST_F(DISABLED_BarTest, DoesXyz) { ... } +``` + +NOTE: 이 기능은 임시로 비활성화 할 때만 사용하는 것이 좋습니다. 문제가 있는 테스트는 언젠가 고쳐야 하며 이 시점은 빠를 수록 좋기 때문입니다. 더불어 googletest는 이렇게 비활성화된 테스트를 발견했을 때, warning message를 출력해 줍니다. + +TIP: `gsearch` 혹은 `grep`을 사용하면 비활성화된 테스트의 개수를 쉽게 파악할 수 있습니다. 이런 결과는 프로젝트의 테스트 수준을 측정하는 지표로도 사용될 수 있습니다. + +#### 비활성화된 Test를 활성화하기 + +비활성화되어 있는 테스트를 실행하는 것도 가능합니다. 이 때에는 `--gtest_also_run_disabled_test`라는 flag를 전달하거나`GTEST_ALSO_RUN_DISABLED_TESTS`라는 environment variable을 `0`이 아닌 값으로 설정하면 됩니다. 더불어 `--gtest_filter`를 사용하면 비활성화 테스트 중에서도 원하는 것만을 골라서 수행할 수 있습니다. + +### Test를 반복 수행시키기 + +가끔은 테스트의 결과가 왔다갔다 할 때가 있습니다. 재현빈도가 낮은 이슈들이 여기에 속합니다. 이러한 문제들은 혼란을 야기하기 때문에 반복적인 수행을 통해서 재현빈도와 문제점을 확인해봐야 합니다. + +이런 경우에는 `--gtest_repeat` flag를 사용하면 문제되는 테스트를 반복적으로 수행해 볼 수 있으며 디버깅에도 도움이 될 것입니다. 아래처럼 사용할 수 있습니다. + +```none +$ foo_test --gtest_repeat=1000 +Repeat foo_test 1000 times and don't stop at failures. + +$ foo_test --gtest_repeat=-1 +A negative count means repeating forever. + +$ foo_test --gtest_repeat=1000 --gtest_break_on_failure +Repeat foo_test 1000 times, stopping at the first failure. This +is especially useful when running under a debugger: when the test +fails, it will drop into the debugger and you can then inspect +variables and stacks. + +$ foo_test --gtest_repeat=1000 --gtest_filter=FooBar.* +Repeat the tests whose name matches the filter 1000 times. +``` + +만약 test program이 [global set-up/tear-down](#global-set-up-and-tear-down)을 포함하고 있다면 그것도 역시 반복적으로 수행 될 것입니다. 왜냐하면 문제가 전역코드에 있을 수도 있기 때문에 이 부분도 필요합니다. 마지막으로 `GTEST_REPEAT` environment variable를 사용해서 반복횟수를 지정하는 것도 가능합니다. + +### Test 수행 순서를 섞기 + +`--gtest_shuffle` flag 사용하거나 `GTEST_SHUFFLE` environment variable을 `1`로 설정하면 테스트들을 random 하게 수행할 수 있습니다. 이러한 수행의 장점은 테스트간의 의존성으로 인해 발생하는 문제를 확인할 수 있다는 것입니다. + +기본적으로 googletest는 현재시간(time)을 random seed로 사용하기 때문에 매 순간마다 random한 수행이 가능해집니다. 더불어 콘솔창에 random seed 값을 출력해주기 때문에 특정한 순서에서 발생하는 문제인지를 확인할 수 있습니다. 이러한 random seed를 변경하려면 `--gtest_random_seed=SEED` flag 를 사용하거나 `GTEST_RANDOM_SEED` environment variable을 변경하면 됩니다. 설정값의 범위는 0~99999 입니다. 만약, random seed = 0 이라면 googletest는 기본설정(현재시간)을 사용하게 됩니다. + +`--gtest_shuffle`과 `--gtest_repeat=N`을 함께 사용하면 반복할 때마다 새로운 random seed를 사용합니다. + +### Test Output 이모저모 + +#### Terminal Output 색상 변경하기 + +Googletest는 terminal output의 색상을 통해 중요한 내용을 강조할 수 있도록 도와줍니다. + +```bash +... +[----------] 1 test from FooTest +[ RUN ] FooTest.DoesAbc +[ OK ] FooTest.DoesAbc +[----------] 2 tests from BarTest +[ RUN ] BarTest.HasXyzProperty +[ OK ] BarTest.HasXyzProperty +[ RUN ] BarTest.ReturnsTrueOnSuccess +... +some error messages +... +[ FAILED ] BarTest.ReturnsTrueOnSuccess +... +[==========] 30 tests from 14 test suites ran. +[ PASSED ] 28 tests. +[ FAILED ] 2 tests, listed below: +[ FAILED ] BarTest.ReturnsTrueOnSuccess +[ FAILED ] AnotherTest.DoesXyz 2 FAILED TESTS +``` + +`GTEST_COLOR` environment variable 또는 `--gtest_color` flag에 `yes`, `no`, `auto` 값을 지정하면 됩니다. 기본적으로는 `auto`로 설정되어 있습니다. `auto`인 경우에 googletest의 출력은 terminal 설정에 따라 달라집니다. Windows가 아닌 경우에는 `xterm` 혹은 `xterm-color`에 의해 설정된 `TERM` environment variable로 설정된 값을 이용합니다. + +#### Test 수행시간 출력하지 않기 + +Googletest는 기본적으로 각 테스트의 수행시간을 출력하고 있습니다. 이러한 기능을 비활성화 하려면 `--gtest_print_time=0` flag를 사용하거나 `GTEST_PRINT_TIME` environment variable을 `0`으로 설정하면 됩니다. + +#### UTF-8 형식 사용하지 않기 + +테스트가 실패했을 때, googletest는 expected value와 actual value의 `string`값을 hex-encoded로 출력해 주거나 UTF-8로 출력해 줍니다. 만약, 사용자 환경에서 UTF-8을 사용할 수 없다면 이를 비활성화해야 합니다. 이를 위해서는 `--gtest_print_utf8` flag를 `0`으로 설정하거나 `GTEST_PRINT_UTF8` environment variable을 `0`으로 설정해야 합니다. + +#### XML Report 출력하기 + +Googletest는 테스트결과를 XML 형식으로 출력하는 방법도 제공합니다. XML Report는 각 테스트의 수행시간도 포함하기 때문에 느리게 수행되는 테스트를 확인하는데에 도움이 될 것입니다. 이런 XML Report를 이용하면 dashboard를 구축하는데도 도움이 될 것입니다. + +XML Rerpot를 생성하려면 `--gtest_output` flag 혹은 `GTEST_OUTPUT` environment variable에 `"xml:path_to_output_file"`을 대입하면 됩니다. 경로지정 없이 그냥 `"xml"`만 대입하면 현재 경로에 `test_detail.xml` 파일을 생성해 줍니다. + +경로를 지정할 때는 Linux에서는 `"xml:output/directory/"`와 같이 지정하고 Windows에서는 `"xml:output\directory\"`과 같이 지정하면 됩니다. 그러면 해당 경로에 XML 파일이 저장되며 파일의 이름은 test program 이름을 사용하게 됩니다. 예를 들어서 test program이 `foo_test` 혹은 `foo_test.exe` 라면 XML 파일은 `foo_test.xml` 이 됩니다. 만약 동일한 파일이 이미 존재한다면 `foo_test_1.xml`과 같은 이름으로 자동으로 변경하기 때문에 덮어쓰기는 걱정하지 않아도 됩니다. + +Googletest의 XML Rerpot 출력방식은 `junitreport` Ant task 를 모티브로 합니다. 다만, 대상 언어가 C++, Java로 서로 다르기 때문에 약간의 차이는 있습니다. 그럼 googletest의 XML Report 예시는 아래와 같습니다. + +```xml + + + + + + + + + +``` + +* ``은 전체 test program을 의미합니다. (testsuites의 's'를 유의하세요.) +* ``은 각각의 test suite을 의미합니다. +* ``는 `TEST()` 혹은 `TEST_F()`로 정의한 test case(test function)을 의미합니다. + +아래와 같은 test program이 있다고 가정해보면 + +```c++ +TEST(MathTest, Addition) { ... } +TEST(MathTest, Subtraction) { ... } +TEST(LogicTest, NonContradiction) { ... } +``` + +XML Report는 아래와 같이 출력됩니다. + +```xml + + + + + ... + ... + + + + + + + + + +``` + +몇가지 기억해야 할 점은 아래와 같습니다. + +* ``과 ``이 가지고 있는 `tests`라는 attribute은 해당 test suite에 몇 개의 test function이 있는지를 의미합니다. 반면에 `failure` attribute은 그 중에서 몇 개의 test function이 실패했는지를 보여줍니다. + +* `time`은 test suites, test suite, test case가 모두 가지고 있는 attribute이며 각각의 수행시간이 얼마인지를 알려줍니다. + +* `timestamp`은 test가 시작된 시각을 알려주는 attribute입니다. + +* `` element는 실패한 각각의 googletest assertion을 의미합니다. (`failures` attribute와는 다릅니다.) + +#### JSON Report 출력하기 + +Googletest는 테스트결과를 JSON 형식으로 출력하는 방법도 제공합니다. JSON Report를 출력하기 위해서는 `GTEST_OUTPUT` environment variable을 설정하거나 `--gtest_output` flag를 사용하면 됩니다. 사용방법은 XML Report와 유사합니다. `GTEST_OUTPUT`이나 `--gtest_output` flag에 `"json:path_to_output_file"`을 대입하면 JSON Report가 저장될 위치를 지정할 수 있습니다. 간단하게 `"json"`만 지정하면 현재 위치에 `test_detail.json` 파일을 생성해 줍니다. + +그럼 googletest의 JSON Report 예시는 아래와 같습니다. + +```json +{ + "$schema": "http://json-schema.org/schema#", + "type": "object", + "definitions": { + "TestCase": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "tests": { "type": "integer" }, + "failures": { "type": "integer" }, + "disabled": { "type": "integer" }, + "time": { "type": "string" }, + "testsuite": { + "type": "array", + "items": { + "$ref": "#/definitions/TestInfo" + } + } + } + }, + "TestInfo": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "status": { + "type": "string", + "enum": ["RUN", "NOTRUN"] + }, + "time": { "type": "string" }, + "classname": { "type": "string" }, + "failures": { + "type": "array", + "items": { + "$ref": "#/definitions/Failure" + } + } + } + }, + "Failure": { + "type": "object", + "properties": { + "failures": { "type": "string" }, + "type": { "type": "string" } + } + } + }, + "properties": { + "tests": { "type": "integer" }, + "failures": { "type": "integer" }, + "disabled": { "type": "integer" }, + "errors": { "type": "integer" }, + "timestamp": { + "type": "string", + "format": "date-time" + }, + "time": { "type": "string" }, + "name": { "type": "string" }, + "testsuites": { + "type": "array", + "items": { + "$ref": "#/definitions/TestCase" + } + } + } +} +``` + +이러한 JSON Report의 출력형식은 [JSON encoding](https://developers.google.com/protocol-buffers/docs/proto3#json)을 사용한 Proto3을 따르고 있습니다. Proto3은 아래와 같습니다. + +```proto +syntax = "proto3"; + +package googletest; + +import "google/protobuf/timestamp.proto"; +import "google/protobuf/duration.proto"; + +message UnitTest { + int32 tests = 1; + int32 failures = 2; + int32 disabled = 3; + int32 errors = 4; + google.protobuf.Timestamp timestamp = 5; + google.protobuf.Duration time = 6; + string name = 7; + repeated TestCase testsuites = 8; +} + +message TestCase { + string name = 1; + int32 tests = 2; + int32 failures = 3; + int32 disabled = 4; + int32 errors = 5; + google.protobuf.Duration time = 6; + repeated TestInfo testsuite = 7; +} + +message TestInfo { + string name = 1; + enum Status { + RUN = 0; + NOTRUN = 1; + } + Status status = 2; + google.protobuf.Duration time = 3; + string classname = 4; + message Failure { + string failures = 1; + string type = 2; + } + repeated Failure failures = 5; +} +``` + +예를 들어 아래와 같은 test program이 있다면 + +```c++ +TEST(MathTest, Addition) { ... } +TEST(MathTest, Subtraction) { ... } +TEST(LogicTest, NonContradiction) { ... } +``` + +JSON Report는 아래와 같이 출력됩니다. + +```json +{ + "tests": 3, + "failures": 1, + "errors": 0, + "time": "0.035s", + "timestamp": "2011-10-31T18:52:42Z", + "name": "AllTests", + "testsuites": [ + { + "name": "MathTest", + "tests": 2, + "failures": 1, + "errors": 0, + "time": "0.015s", + "testsuite": [ + { + "name": "Addition", + "status": "RUN", + "time": "0.007s", + "classname": "", + "failures": [ + { + "message": "Value of: add(1, 1)\n Actual: 3\nExpected: 2", + "type": "" + }, + { + "message": "Value of: add(1, -1)\n Actual: 1\nExpected: 0", + "type": "" + } + ] + }, + { + "name": "Subtraction", + "status": "RUN", + "time": "0.005s", + "classname": "" + } + ] + }, + { + "name": "LogicTest", + "tests": 1, + "failures": 0, + "errors": 0, + "time": "0.005s", + "testsuite": [ + { + "name": "NonContradiction", + "status": "RUN", + "time": "0.005s", + "classname": "" + } + ] + } + ] +} +``` + +IMPORTANT: JSON Report 형식은 변경될 수도 있습니다. + +### Failure가 발생했을 때 가능한 조작법 + +#### Assertion Failures를 Break-Points처럼 사용하기 + +디버거에서 test program을 동작시켜서 assertion이 발생했을 때 바로 interactive mode로 전환되게 하면 디버깅할 때 도움이 될 것입니다. Googletest는 그런 사용자를 위해 *break-on-failure* mode를 제공합니다. + +`GTEST_BREAK_ON_FAILURE` environment variable을 `0`이 아닌 값으로 설정하거나 `--gtest_break_on_failure` flag를 사용하면 이 기능을 활성화할 수 있습니다. + +#### Exception Catching 비활성화하기 + +기본설정의 googletest는 테스트가 C++ exception를 던진 경우에 exception을 catch하고 해당 테스트를 실패로 판정합니다. 그리고는 다음 테스트를 계속해서 진행합니다. 이런 동작방식이 기본설정인 이유는 한 번의 실행으로도 최대한 많은 테스트를 수행하기 위함입니다. 더불어 Windows와 같은 환경에서 발생할 수 있는 문제도 예방하게 됩니다. 예를 들면 uncaught exception으로 인해 발생한 pop-up이 자동화 테스트를 멈추게 하는데, googletest가 exception을 catch하고 실패로 처리함으로써 자동으로 넘어갈 수 있게 됩니다. + +만약, 사용자가 googletest의 exception 처리를 비활성화하고 싶다면 그것도 가능합니다. 그러한 경우의 예로 디버거의 exception catch기능을 사용하고 싶은 경우가 있을 것입니다. 이를 통해 exception 발생시점의 call stack 확인 등이 가능해지기 때문입니다. 이런 경우에는 `GTEST_CATCH_EXCEPTIONS` environment variable을 `0`으로 설정하거나 `--gtest_catch_exceptions=0` flag를 사용하면 됩니다. diff --git a/googletest/docs/kr/faq.md b/googletest/docs/kr/faq.md new file mode 100644 index 00000000..e635800d --- /dev/null +++ b/googletest/docs/kr/faq.md @@ -0,0 +1,496 @@ +# Googletest FAQ + +## Test Case 또는 Test Suite의 이름을 정할 때, 밑줄을 사용하면 안되는 이유가 뭔가요? + +먼저, C++ 자체적으로도 밑줄(`_`)은 특별한 의미를 갖습니다. 예를 들면, 표준 library 및 compiler 개발자가 사용할 수 있도록 아래 identifier들을 예약해 두었습니다. + +1. `_`로 시작하고 다음 글자로 대문자가 오는 identifier +2. `_`을 2개 연속(`__`)으로 사용하는 identifier + +사용자 코드에서 위와 같은 방법으로 identifier는 만드는 것은 *금지되어* 있습니다. + +이러한 제약이 `TEST`, `TEST_F`에 어떤 영향을 주는지 확인해 보겠습니다. + +사용자가 `TEST(TestSuiteName, TestName)`라고 구현하면 googletest는 `TestSuiteName_TestName_Test`라는 identifier를 생성해줍니다. 이 때, `TestSuiteName` 또는 `TestName`에 밑줄(`_`)이 포함되면 어떤 일이 발생할까요? + +1. `TestSuiteName`이 `_Foo`와 같이 `_`로 시작하면서 바로 뒤에 대문자를 포함하면 `_Foo_TestName_Test`라는 identifier가 만들어 집니다. C++에서 금지된 예약어입니다. +2. `TestSuiteName`이 `Foo_`와 같이 `_`로 끝나게 되면 `Foo__TestName_Test`라는 이름이 만들어 집니다. C++에서 금지된 예약어입니다. +3. `TestName`이 `_Bar`와 같이 `_`로 시작하면 `TestSuiteName__Bar_Test`라는 이름이 만들어집니다. C++에서 금지된 예약어입니다. +4. `TestName`이 `Bar_`와 같이 `_`로 끝나면 `TestSuiteName_Bar__Test`라는 이름이 만들어 집니다. C++에서 금지된 예약어입니다. + +위와 같은 이유로 `TestSuiteName`과 `TestName`은 `_`로 시작하거나 끝나면 안됩니다. 물론 `TestSuiteName`이 `_`로 시작하면서 바로 뒤에 소문자를 사용하면 괜찮기는 하지만 그런 예외를 생각하면서 구현하는 것이 오히려 어려울 것입니다. 간단하게 `_`는 사용하지 않는 것이 좋습니다. + +어떤 사용자는 `TestSuiteName`, `TestName`의 중간에는 `_`를 써도 괜찮다고 생각할 수 있습니다. 그러나 그것도 위험합니다. 아래 예제를 보시기 바랍니다. + +```c++ +TEST(Time, Flies_Like_An_Arrow) { ... } +TEST(Time_Flies, Like_An_Arrow) { ... } +``` + +위의 코드는 동일한 이름(`Time_Files_Like_An_Arrow_Test)`을 가진 2개의 Test를 만들려고 하기 때문에 당연히 문제가 됩니다. + +언급한 내용들을 유념하여 `TestSuiteName`, `TestName`에는 `_`를 사용하지 않기를 당부합니다. 복잡한 규칙을 기억하는 것보다는 아예 사용하지 않는 것이 편하겠지요. Googletest의 개선여지도 조금 남겨둘 수 있어서 여러모로 좋습니다. + +물론 예외상황을 잘 찾아서 구현하면 지금 당장은 문제없이 동작할 수도 있습니다. 그러나 compiler나 googletest 버전이 변경되면 언젠가는 문제가 발생할지도 모릅니다. 따라서 `_`를 사용하지 않는다는 단순한 규칙을 기억하고 지켜주시기 바랍니다. + +## 왜 `EXPECT_EQ(NULL, ptr)`, `ASSERT_EQ(NULL, ptr)`만 지원하고 `EXPECT_NE(NULL, ptr)`, `ASSERT_NE(NULL, ptr)`은 지원하지 않나요? + +설명에 앞서 `EXPECT_NE(nullptr, ptr)`, `ASSERT_NE(nullptr, ptr)`와 같이 `NULL`을 `nullptr`로 변경하면 잘 동작할 것입니다. C++ style guide에서도 확인할 수 있듯이 `nullptr`과 `NULL`은 다릅니다. 간단한 예로 `NULL`을 사용하면 발생할 수 있는 타입관련 문제들이 `nullptr`를 사용하면 발생하지 않습니다. `nullptr`이 더 안전합니다. + +그럼에도 `EXPECT_XX()`, `ASSERT_XX()`와 같은 macro에 `NULL`을 사용해야 한다면 몇 가지의 template meta programming trick이 필요합니다. 다만, 그러한 trick을 적용하기 위한 소스코드의 양이 상당히 많기 때문에 googletest에서는 꼭 필요한 곳에만 `NULL`을 사용할 수 있도록 했습니다. + +`EXPECT_EQ()`는 첫 번째 argument에는 *expected value*, 두 번째 argument에 *actual value*를 전달받습니다. 이런 맥락에서 "어떤 포인터가 `NULL`인지 확인하고 싶다"는 것도 자연스러운 생각의 확장입니다. 또 실제로도 `EXPECT_EQ(NULL, some_expression)`과 같이 사용하게 해달라는 요청이 여러번 있었기 때문에 googletest는 `EXPECT_EQ(NULL, ptr) `을 지원하게 되었습니다. + +반면에 `EXPECT_NE(NULL, ptr)`에 대한 요청은 그렇게 많지 않았습니다. 왜냐하면 assertion이 실패하면 `ptr`이 `NULL`이라는 것을 사용자가 바로 알게되기 때문에 `ptr`에 대한 추가적인 정보가 굳이 필요하지 않기 떄문입니다. 즉, `EXPECT_TRUE(ptr != NULL)`만으로도 부족함이 없었습니다. + +또한, `EXPECT_NE(NULL, ptr)`를 지원한다면 `EXPECT_NE(ptr, NULL)`도 지원해야 합니다. 왜냐하면 `EXPECT_EQ`와 다르게 `EXEPCT_NE`에는 argument 순서에 대한 규약이 없기 때문입니다. 즉, *expected value*가 첫 번째 argument일 수도 있고, 두 번째 argument일 수도 있습니다. 이런 상황에서 `NULL`을 지원하려면 template meta programming trick 코드도 2배로 사용되게 됩니다. 결론적으로 유지보수 비용은 많이 증가하는 반면에 그러한 구현으로 얻는 이득이 크지 않다고 판단되어 지원하지 않게 되었습니다. + +마지막으로 gMock의 matcher가 발전함에 따라 `EXPECT_THAT(value, matcher)`을 사용해보기를 추천합니다. 왜냐하면 `EXPECT_XX`와 같은 macro는 사용자가 원하는 방향으로 확장하기 어렵지만 matcher는 그러한 확장이 용이하기 때문입니다. 새로운 matcher를 만들수도 있고 여러 matcher들을 조합해서 사용하는 것도 가능합니다. 이런 이유로 `EXPECT_XX()`보다 `EXPECT_THAT()`과 matcher 사용을 좀 더 권장하고 있습니다. + +## Interface가 1개 있고 이를 상속받고 구현한 concrete class는 여러개가 있습니다. Interface 레벨에서 각 concrete class가 잘 동작하는지 확인하려면 어떻게 해야 하나요? + +동일한 interface에 대한 여러가지 구현을 테스트해야 한다면 typed test 혹은 value-parameterized test를 사용하면 됩니다. 두개 중에서 어떤 방법을 사용할지는 전적으로 사용자의 선택에 달려 있습니다. 아래에 상황에 따라 선택하는 방법을 간단히 가이드합니다. + +- Typed test는 concrete type들이 동일한 방법으로 instance를 생성하는 경우에 좋습니다. 예를 들어 모든 concrete class들이 public default constructor를 사용한다거나 혹은 동작방식이 같은 factory function을 사용하는 경우입니다. 전자는 모든 concrete class가 `new TypeParam` 과 같은 방법으로 생성할 수 있는 경우를 의미합니다. 그렇게 되면 `TypeParam`만 바꾸는 방식으로 typted test가 가능해집니다. 후자는 `CreateInstance()`과 같은 factory function을 사용하는 경우이며 여기서도 마찬가지로 `TypeParam`만 바꾸면 다양한 concrete type을 테스트할 수 있습니다. +- Value-parameterized test는 concrete type들이 서로 다른 방법으로 instance를 생성하는 경우에 유용합니다. 예를 들어 `Foo`와 `Boo`라는 concrete class가 있을 때, 각각의 instance 생성방식이 `new Foo`와 `new Bar(5)`처럼 다를 수 있습니다. Value-parameterized test에서는 이러한 차이를 극복하기 위해 factory function을 생성하고 해당 function의 function pointer를 test의 parameter로 넘겨주는 방식을 사용하게 됩니다. +- Typed test는 테스트가 실패하면, 해당 type을 출력해주도록 되어 있습니다. 그렇게 해야만 어떤 type이 잘못되었는지 빠르게 알 수 있기 때문입니다. 반면에 value-parameterized test는 테스트가 어떤 interation에서 멈췄는지만 알려주기 때문에 디버깅이 약간 더 어렵습니다. 이를 문제를 해결하기 위해서는 iteration name을 반환하는 function을 직접 정의하고 value-parameterized test를 초기화할 때 `INSTANTIATE_TEST_SUITE_P`의 3번째 parameter로 넘겨주면 됩니다. +- Typed test를 사용할 때, 어디까지나 interface관점에서 테스트하는 것이지 concrete type 자체를 테스트하는 것이 아님을 기억하기 바랍니다. 쉽게 말해서 `my_concrete_impl`를 테스트하는 것이 아니라 `implicit_cast(my_concrete_impl)`가 잘 동작하는지 테스트하는 것입니다. 이는 value-parameterized test를 사용할 때도 동일합니다. + +더 혼란스럽게 만든건 아닌지 모르겠습니다. 아마도 위 방법들을 직접 사용해본다면 둘 간의 미묘한 차이를 이해하는데 도움이 될 것입니다. 한 번만 해보면 다음부터는 어떤 방법을 선택해야 할지 판단할 수 있게 될 것입니다. + +## `ProtocolMessageEquals`를 사용했는데 Invalid proto descriptors와 관련된 run-time error가 발생합니다. 도와주세요! + +**Note**: 시작하기 앞서 `ProtocolMessageEquals`와 `ProtocolMessageEquiv`는 *deprecated된* 기능입니다. `EqualsProto`를 사용하는 것이 더 좋습니다. + +`ProtocolMessageEquals`와 `ProtocolMessageEquiv`가 최근에 변경되면서 invalid protocol buffer와 같은 문제에는 취약해 졌습니다. 그 결과 `foo.proto`라고 구현하면서 참조하고 있는 protocol message type 전체를 명시하지 않는다면 (예를 들어 `message`가 아니라 `message`와 같이 사용하면) 아래와 같은 run-time error가 발생하게 됩니다. + +```bash +... descriptor.cc:...] Invalid proto descriptor for file "path/to/foo.proto": +... descriptor.cc:...] blah.MyMessage.my_field: ".Bar" is not defined. +``` + +만약, 위와 동일한 error message를 봤다면 `.proto` 파일이 잘못된 것이므로 앞서 설명한 것처럼 protocol message type 전체를 명시하도록 수정하면 잘 동작할 것입니다. `ProtocolMessageEquals`, `ProtocolMessageEquiv`을 새롭게 변경하다보니 나타난 문제입니다. + +## Death test에서 어떤 상태를 변경하고 있습니다. 그러나 death test가 끝난 이후에는 변경한 상태가 유지되지 않는 것 같습니다. 왜 그런가요? + +Death test는 child process를 생성하고 거기서 실행됩니다. 알다시피 child process에서 수정된 내용은 parent process에 영향을 주지 않습니다. 최악의 경우에 child process에서 crash가 발생한다고 하더라도 parent process(test program)는 종료되지 않습니다. 당연하게도 2개의 process가 서로 다른 실행환경을 가지기 때문에 대부분의 경우에 서로간에 영향을 주지 않습니다. + +또한, parent process에서 method를 mocking한 후에 expectation을 설정했다고 하더라도 death test 내에서 해당 method를 호출한다면 parent process에서는 mock method가 호출되었는지 아닌지 알 수가 없습니다. 이런 상황에서는 해당 `EXPECT_CALL`도 death test 내부로 옮겨야 합니다. + +## Opt mode에서 `EXPECT_EQ(htonl(blah), blah_blah)` 라고 사용하면 이상한 compile error가 발생합니다. 이거 googletest bug 아닌가요? + +사실 bug는 `htonl()`에 있습니다. + +`man htonl`을 통해 확인해보면 `htonl()`은 _function_입니다. 따라서 `htonl`을 function pointer로 사용하는 것은 적법합니다. 그러나 opt mode에서 `htonl()`은 macro로 정의됩니다. 이로 인해 `htonl`을 function pointer로 전달하는 `EXPECT_EQ(htonl(blah), blah_blah)`라는 코드에서 compile error가 발생하는 것입니다. + +설상가상으로 `htonl()`의 구현에서 사용한 macro는 C++ 표준방법을 사용한 것이 아니라 `gcc`의 확장기능 중 하나를 사용했습니다. 어찌됐건 `htonl()`가 opt mode에서는 function이 아니라 macro이기 때문에 `Foo()`와 같은 코드도 구현할 수가 없습니다. (여기서 `Foo`는 정수타입의 template argument를 전달 받는 template class입니다.) + +`EXPECT_EQ(a, b)`는 내부적으로 `sizeof(... a ... )`를 사용합니다. 따라서 `a`가 `htonl()`을 포함한다면 opt mode에서 문제가 될 것입니다. 더군다나 C++표준 macro를 사용한 것도 아니기 때문에 플랫폼과 compiler에 따라 그 결과가 달라질 수 있습니다. `EXPECT_EQ`에서 `htonl()`의 bug를 바로 잡는 것은 어렵습니다. + +`//util/endian/endian.h`에 나와있는 것처럼 `htonl()`에는 다른 문제들도 더 있습니다. 이를 위한 대안으로 `ghtonl()`을 제공하고 있습니다. `ghtonl()`은 `htonl()`과 동일하면서도 위와 같은 문제점이 없습니다. 따라서 `htonl()`을 사용하고 있다면 `ghtonl()`으로 변경하기를 권장합니다. 제품코드와 테스트코드 모두 마찬가지입니다. + +`ghtonl()`, `ghtons()`를 사용하려면 `//util/endian`이 빌드될수 있도록 추가해야 합니다. 해당 library는 header file 1개만 포함하고 있기 때문에 binary 사이즈도 그렇게 증가하지는 않을 겁니다. + +## Class 내부에 static const member variable을 정의했는데도 불구하고 "undefined references"라는 문제가 발생하고 있습니다. 왜 그럴까요? + +만약, 아래와 같이 class 내부에 static data member를 정의했다면 + +```c++ +// foo.h +class Foo { + ... + static const int kBar = 100; +}; +``` + +Class 바깥에서도 정의를 해줘야 합니다. + +```c++ +const int Foo::kBar; // No initializer here. +``` + +**C++**에서 static member variable을 사용할때는 항상 이렇게 해야합니다. 이러한 문법을 지키지 않으면서 googletest 기능을 사용하면 안 됩니다. Googletest 자체도 C++을 사용하기 때문입니다. + +## 다른 test fixture로부터 상속받는 것이 가능한가요? + +가능합니다. + +설명에 앞서 googletest에서 test fixture와 test suite은 서로 동일한 이름을 사용함으로써 연결됩니다. 다시 말해서 `TEST_F()`의 첫 번째 argument(test suite)는 동일한 이름의 test fixture class가 어딘가에 정의되어 있어야 합니다. 이런식으로 test suite과 test fixture가 1:1 매칭되는 상황에서 여러개 test fixture에 공통적으로 적용해야 할 내용이 있다면 어떻게 해야 할까요? 이런 경우에는 상속을 사용하면 됩니다. + +예를 들어 GUI library를 위한 여러가지 test suite들이 이미 구현되어 있다고 가정하겠습니다. Font를 위한 test suite, brush를 위한 test suite 등 다양한 test suite들이 이미 있을 건데요. 그러한 test suite들이 공통적으로 memory leak도 확인할 수 있도록 개선하려 합니다. 이를 위해서는 먼저 memory leak 검증을 수행하기 위한 test fixture를 하나 생성합니다. 그 다음에 기존에 존재하던 font test fixture와 brush test fixture가 memory leak test fixture를 상속받게 합니다. 그렇게 되면 font test fixture와 brush test fixture는 test suite과의 1:1 관계도 그대로 유지하면서 memory test fixture라는 공통적인 검증기능도 수행할 수 있게 됩니다. + +간단한 예제코드는 아래와 같습니다. + +```c++ +// Defines a base test fixture. +class BaseTest : public ::testing::Test { + protected: + ... +}; + +// Derives a fixture FooTest from BaseTest. +class FooTest : public BaseTest { + protected: + void SetUp() override { + BaseTest::SetUp(); // Sets up the base fixture first. + ... additional set-up work ... + } + + void TearDown() override { + ... clean-up work for FooTest ... + BaseTest::TearDown(); // Remember to tear down the base fixture + // after cleaning up FooTest! + } + + ... functions and variables for FooTest ... +}; + +// Tests that use the fixture FooTest. +TEST_F(FooTest, Bar) { ... } +TEST_F(FooTest, Baz) { ... } + +... additional fixtures derived from BaseTest ... +``` + +필요하다면, base test fixture를 상속받은 test fixture를 또 다시 상속하는 것도 가능합니다. 상속의 깊이에 특별한 제한은 없습니다. + +보다 상세한 예제들은 [sample5_unittest.cc](https://github.com/google/googletest/blob/master/googletest/samples/sample5_unittest.cc)에서 확인하기 바랍니다. + +## "void value not ignored as it ought to be"라는 compile error가 발생합니다. 이게 무슨 뜻인가요? + +아마도 `ASSERT_*()`에 return type이 `void`가 아닌 function을 사용했을 확률이 큽니다. `ASSERT_*()`는 `void`를 반환하는 function에만 사용할 수 있으며 그 이유는 googletest가 expception disabled이기 때문입니다. [여기](advanced.md#assertion-placement)에서 보다 자세한 내용을 확인할 수 있습니다. + +## Hang, seg-fault 등이 발생하면서 death test가 잘 동작하지 않습니다. 어떻게 해야할까요? + +Googletest에서 death test는 기본적으로 child process에서 동작합니다. 그렇기 때문에 구현 시에도 세심한 주의가 필요합니다. Death test를 사용하려면 먼저 death test가 어떻게 동작하는지 정확히 이해해야 합니다. [이 부분](advanced.md#how-it-works)을 꼭 읽어주세요. + +Death test를 사용할 때 parent process에 여러개의 thread가 있는 경우에는 문제가 발생하기도 합니다. 혹시 `EXPECT_DEATH()`를 수행하기 전에 이미 여러개의 thread를 생성하고 있다면 이러한 부분을 한 번 제거해 보시기 바랍니다. 예를 들어 thread를 생성하는 real object를 mock이나 fake로 바꿔서 thread를 생성하지 않도록 해볼 수 있습니다. + +간혹 `main()`이 시작하기도 전에 thread를 생성버리는 library도 있기 때문에 위와 같은 방법이 항상 통하는 것은 아닙니다. 다음 방법으로는 `EXPECT_DEATH()`에서 해야할 일을 최대한 줄여보시기 바랍니다. 기본적인 것만 해도 문제가 발생하는지 확인해보고 아니면 반대로 parent process에서 해야할 모든 일을 death test로 옮겨보십시오. 여전히 문제가 발생하는지 확인해보기 바랍니다. 또는 death test style(동작방식)을 `"threadsafe"`로 변경해보는 것도 좋은 방법입니다. `"threadsafe"` 모드를 사용하면 테스트의 속도는 조금 느려지지만 안전성을 증가시켜줍니다. + +Thread-safe death test는 child process를 생성하기 전에 아예 test program(parent process)을 하나 더 실행시킵니다. 이런 방법으로 안전성을 증가시켜줍니다. 다만, 이를 위해서는 동일한 test program이 여러개 실행되어도 문제없도록 구현해야 합니다. + +마지막으로 당연한 이야기이긴 하지만, race condition, dead lock이 없는지도 확인해보기 바랍니다. 여타의 개발과 동일하게 이 부분은 사용자 스스로가 주의해서 개발해야 합니다. + +## Test fixture에서 constructor/destructor 와 `SetUp()`/`TearDown()`중 어느것을 써야하나요? + +먼저 기억해야 할 것은 googletest는 test fixture를 재사용하지 않는 다는 점입니다. 즉, 각각의 test case를 실행할때마다 test fixture object를 매번 **새로** 생성합니다. 따라서 해당 object 생성되고 삭제될 때마다 constructor/destructor 및 `SetUp()`/`TearDown()`도 새롭게 호출됩니다. + +그럼 constructor/destructor 와 `SetUp()`/`TearDown()` 중에서는 어느 것을 선택해야 할까요? 먼저 constructor/destructor를 사용할 때의 장점은 아래와 같습니다. + +- Constructor에서 member variable을 초기화함으로서 해당 변수를 `const`로 선언할 수 있게 됩니다. 아시다시피 `const`는 해당값을 변경하는 등의 행위를 막아줍니다. +- Test fixture class를 상속받아서 또 다른 test fixture class를 만든다고 가정해봅시다. C++에서는 base class의 constructor와 derived class의 constructor가 순서대로 모두 호출되도록 보장됩니다. 또한, destructor도 방향은 거꾸로지만 모두 호출되도록 보장됩니다. 하지만 `SetUp()/TearDown()`은 이러한 것이 보장되지 않으므로 실수할 확률이 있습니다. 즉, derived class에서 base class의 `SetUp()`을 호출하지 않는 등의 문제가 발생할 수 있습니다. + +다음으로 `SetUp()/TearDown()`을 사용하면 아래와 같은 장점이 있습니다. + +* C++의 constructor, destructor에서는 virtual function을 호출할 수 없습니다. 물론, 호출할 수는 있지만 그렇게 되면 derived class의 function을 동적으로 호출하는 것이 아니라 constructor/destructor가 구현된 base class의 function을 호출하게 됩니다. 이렇게 동작하는 이유는 base class의 constructor가 수행되는 시점에 derived class의 constructor는 아직 호출되지 않았을 것이고 이렇게 초기화되지 않은 derived class의 method를 호출하는 것은 매우 위험하기 때문입니다. 해당 virtual method가 초기화되지 않은 값들을 사용하기라도 한다면 큰 문제가 발생할 수 있습니다. 따라서 이렇게 overriding 관계에 있는 virtual function을 사용하려 `SetUp()/TearDown()`을 사용할 수 밖에 없습니다. +* Constructor/destructor에는 `ASSERT_xx`계열 assertion을 사용할 수 없습니다. (`EXPECT_xx`는 아닙니다.) 만약, constructor에서 이런 목적을 구현하려면 `abort`를 사용해야 하는데 이것은 test program 자체를 종료시키기 때문에 `ASSERT_xx`보다 비효율적입니다. 따라서 test fixture 레벨에서 `ASSERT_xx`계열 assertion을 사용하고 싶다면 `SetUp()`을 사용하는 것이 더 좋습니다. +* Test fixture를 정리하는 과정에서 exception을 던지고 싶을 수도 있습니다. 그러나 C++에서destructor가 exception을 던지는 것은 미정의 동작이므로 그렇게 구현하면 안됩니다. 대부분의 경우 test program을 바로 종료시킬 것입니다. 이런 경우에도 역시 `TearDown()`을 사용하면 좋습니다. 참고로 C++에서는 STL과 같은 standard library들도 예외를 발생시킬 수 있습니다. (컴파일러에 exception이 활성화 되어 있다면) +* Googletest team은 exception이 활성화되어 있는 플랫폼(Windows, Mac OS, Linux client-side)에서는 assertion도 exception을 던질 수 있도록 하는 부분에 대해 고민하고 있습니다. 그렇게 되면 subroutine에서 발생한 failure를 caller쪽으로 전달할 때, 사용자가 직접 구현할 필요가 없어집니다. 추후에 추가될지 모르는 이러한 기능을 위해서 destructor에는 googletest assertion을 사용하면 안 됩니다. + +## `ASSERT_PRED*`를 사용할 때, "no matching function to call" 이라는 compile error가 발생했습니다. 어떻게 해야 하나요? + +만약 `ASSERT_PRED*`, `EXPECT_PRED*`에 전달한 predicate function이 overloaded 혹은 template function이라면 compiler가 어떤 것을 선택해야 하는지 모호해집니다. 그런 경우에는 `ASSERT_PRED_FORMAT*`이나 `EXPECT_PRED_FORMAT*` 을 사용해야 합니다. + +네, 질문과 같은 compile error가 발생하면 `(ASSERT|EXPECT)_PRED_FORMAT*`으로 변경하는 것이 제일 좋습니다. 위의 compile error를 해결할 수 있을 뿐만 아니라 assertion이 실패했을 때의 failure message도 더 자세하게 출력해 주는 장점이 있기 때문입니다. 만약 꼭 `(ASSERT|EXPECT)_PRED*`를 사용해야 한다면 compiler에게 overloaded function, template function의 여러 버전 중에서 어떤 것을 사용할지 알려주면 됩니다. + +아래에 예제가 있습니다. + +```c++ +bool IsPositive(int n) { + return n > 0; +} + +bool IsPositive(double x) { + return x > 0; +} +``` + +위와 같은 overloaded function을 기존과 같이 사용하면 compile error가 발생합니다. + +```c++ +EXPECT_PRED1(IsPositive, 5); +``` + +이제 아래처럼 변경해서 모호함을 제거하기 바랍니다. + +```c++ +EXPECT_PRED1(static_cast(IsPositive), 5); +``` + +`static_cast` 연산자에 사용한 `bool (*)(int)`는 `bool IsPositive(int n)` 버전을 의미합니다. + +다음은 template function을 사용할때의 예제입니다. + +```c++ +template +bool IsNegative(T x) { + return x < 0; +} +``` + +이 경우에도 어떤 type을 사용할 것인지 명확하게 지정해줘야 합니다. + +```c++ +ASSERT_PRED1(IsNegative, -5); +``` + +만약, 하나 이상의 template argument가 1개 이상이라면 어떻게 해야 할까요? + +```c++ +ASSERT_PRED2(GreaterThan, 5, 0); +``` + +위의 코드는 compile에 실패합니다. C++ 전처리기에서 `ASSERT_PRED2`에 4개의 argument를 준다고 판단하기 때문입니다. 이런 경우에는 아래처럼 괄호로 감싸줘야 compile에 성공합니다. + +```c++ +ASSERT_PRED2((GreaterThan), 5, 0); +``` + +## `RUN_ALL_TESTS()`를 호출하면 "ignoring return value"라는 compile error가 발생합니다. 왜 그럴까요? + +`RUN_ALL_TEST()`을 사용할 때는 항상 아래처럼 구현해야 합니다. + +```c++ + return RUN_ALL_TESTS(); +``` + +`return` 없이 구현하는 것은 상당히 **위험하고 잘못된** 코드입니다. + +```c++ + RUN_ALL_TESTS(); +``` + +Testing service는 `RUN_ALL_TESTS()`의 반환값을 통해서 테스트의 성공이나 실패를 판단하게 됩니다. 따라서 `return`이 없다면 실제로는 assertion failure가 발생했는데도 성공했다고 여겨질 수 있으며 매우 잘못된 정보를 전달하게 될 것입니다. + +이러한 문제를 좀 더 확실하게 알려야 했기 때문에 `gcc`같은 경우 반환값이 없는 `RUN_ALL_TESTS()` 코드를 작성하면 compile error가 발생하도록 했습니다. + +물론 해결방법은 간단합니다. 질문과 같은 compile error가 발생했다면 `RUN_ALL_TESTS()`가 `main()` function의 `return`이 되도록 하면 됩니다. + +만약, 이렇게 변경했더니 테스트코드가 실패하나요? 그랬다면 원래 존재하던 문제를 이번 기회에 찾았다고 봐야합니다. 이제 문제점을 대한 개선작업을 진행하기 바랍니다. `return RUN_ALL_TESTS()`으로 변경하는 것은 테스트코드의 동작에는 아무런 영향을 주지 않으며 오히려 정상적으로 동작하도록 해주기 때문입니다. + +## Constructor 또는 destructor가 값을 반환할 수 없다는 compile error가 발생합니다. 어떻게 해야 하나요? + +Googletest에서는 assertion과 함께 streaming message를 바로 출력하는 것이 가능합니다. + +```c++ + ASSERT_EQ(1, Foo()) << "blah blah" << foo; +``` + +바로 이와 같은 기능을 지원하기 위해서 constructor/destructor에서는 `ASSERT*`, `FAIL*`를 사용할 수 없도록 했습니다. (`EXPECT*`, `ADD_FAILURE*`는 아닙니다) 이를 위한 해결방법은 constructor/destructor 대신에 private void method를 사용해서 원하는 내용을 구현하는 것입니다. 또는 `ASSERT_*()`를 `EXPECT_*()` 로 바꾸는 것도 방법입니다. 이와 관련한 자세한 내용은 [여기](advanced.md#assertion-placement)를 확인하기 바랍니다. + +## `SetUp()`이 호출되지 않습니다. 왜 그럴까요? + +C++은 대문자/소문자가 중요합니다. 혹시 `Setup()`이라고 구현하진 않았나요? + +이와 유사하게 `SetUpTestSuite()`도 실수로 인해 `SetupTestSuite()`처럼 구현하는 경우가 종종 있습니다. + + +## 동일한 test fixture logic을 사용해서 test suite을 여러개 만들고 싶습니다. Test fixture class를 새로 정의하는 것 외에는 방법이 없나요? + +Test fixture class를 새로 정의하지 않아도 됩니다. 기존에 아래와 같이 구현했다면, + +```c++ +class FooTest : public BaseTest {}; + +TEST_F(FooTest, Abc) { ... } +TEST_F(FooTest, Def) { ... } + +class BarTest : public BaseTest {}; + +TEST_F(BarTest, Abc) { ... } +TEST_F(BarTest, Def) { ... } +``` + +`typedef`를 사용하도록 변경하면 됩니다. Class를 새로 정의하지 않아도 되고 googletest의 규칙도 깨트리지 않습니다. + +```c++ +typedef BaseTest FooTest; + +TEST_F(FooTest, Abc) { ... } +TEST_F(FooTest, Def) { ... } + +typedef BaseTest BarTest; + +TEST_F(BarTest, Abc) { ... } +TEST_F(BarTest, Def) { ... } +``` + +## Googletest의 출력물이 전체 LOG에 포함되어 알아보기가 힘듭니다. 어떻게 해야 하나요? + +Googletest 출력물은 최대한 간결하게 제공하고 있습니다. 만약, 질문과 같은 상황이 발생했다면 사용자의 테스트가 자체적으로 너무 많은 LOG를 출력하는 것은 아닌지 확인해 볼 필요가 있습니다. 만약, 사용자의 LOG도 꼭 필요한 상황이라면 아래와 같은 방법을 통해 문제를 해결하기 바랍니다. + +`LOG`는 대부분의 경우 stderr로 출력되기 때문에, googletest의 출력물은 stdout으로 출력되도록 설계했습니다. 따라서 test program을 실행할 때 redirection을 사용하면 LOG를 분리할 수 있습니다. + +```shell +$ ./my_test > gtest_output.txt +``` + +## 왜 전역변수보다 test fixture를 선호해야 하나요? + +이를 위한 몇가지 이유가 있습니다. + +1. 테스트 코드에서 전역변수를 사용하고 싶을 때가 있습니다. 다만, 이러한 구현은 여러 test case가 전역변수를 공유할 때 문제를 발생시킬 수 있으며 디버깅도 어렵게 합니다. Test fixture class는 개별 test case를 실행할 때마다 자신의 object를 새롭게 만들기 때문에 test case간에 영향을 주지 않습니다.(변수나 function의 이름은 같겠지만) 따라서 각각의 test가 독립적으로 수행될 수 있게 됩니다. +2. 전역변수는 global namespace를 오염시킵니다. +3. Test fixture class는 상속에 의해 재사용될 수 있습니다. 전역변수로는 할 수 없는 일입니다. 여러개의 test suite을 대응하기 위해서 중복코드를 작성할 필요가 없습니다. + +## `ASSERT_DEATH()` 의 statement argument에는 무엇이 올 수 있나요? + +`ASSERT_DEATH(statement, regex)`와 같은 death test용 macro는 `statement`가 유효해야만 사용할 수 있습니다. 기본적으로 `statement`는 C++에서 유효한 코드이면 그대로 사용할 수 있습니다. 전역/지역변수를 참조하는 것도 가능하고 아래처럼 복잡한 내용들도 구현할 수 있습니다. + +- 간단한 function 호출 +- 복잡한 expression +- statement 조합 + +아래에 간단한 예제코드를 공유합니다. 좀 더 다양한 예제가 궁금하다면 gtest-death-test_test.cc 파일에서도 확인할 수 있습니다. + +```c++ +// A death test can be a simple function call. +TEST(MyDeathTest, FunctionCall) { + ASSERT_DEATH(Xyz(5), "Xyz failed"); +} + +// Or a complex expression that references variables and functions. +TEST(MyDeathTest, ComplexExpression) { + const bool c = Condition(); + ASSERT_DEATH((c ? Func1(0) : object2.Method("test")), + "(Func1|Method) failed"); +} + +// Death assertions can be used any where in a function. In +// particular, they can be inside a loop. +TEST(MyDeathTest, InsideLoop) { + // Verifies that Foo(0), Foo(1), ..., and Foo(4) all die. + for (int i = 0; i < 5; i++) { + EXPECT_DEATH_M(Foo(i), "Foo has \\d+ errors", + ::testing::Message() << "where i is " << i); + } +} + +// A death assertion can contain a compound statement. +TEST(MyDeathTest, CompoundStatement) { + // Verifies that at lease one of Bar(0), Bar(1), ..., and + // Bar(4) dies. + ASSERT_DEATH({ + for (int i = 0; i < 5; i++) { + Bar(i); + } + }, + "Bar has \\d+ errors"); +} +``` + +## `FooTest`라는 test fixture class가 있고 이를 사용하는 `TEST_F(FooTest, Bar)`라는 코드가 있습니다. 이 때, "no matching function for call to `FooTest::FooTest()`"라는 compile error가 발생하는데 왜 그럴까요? + +Googletest는 test fixture class의 object를 생성해서 사용하기 때문에 test fixture class는 object 생성이 가능해야만 합니다. 즉, constructor를 가지고 제공해야 합니다. 기본적으로 compiler가 생성해주겠지만, 직접 정의해서 사용할 때는 몇가지를 유의해야 합니다. + +- `FooTest`라는 test fixture class에 non-default constructor를 정의(`DISALLOW_EVIL_CONSTRUCTORS()` 를 사용해서)하고자 할 때에는 default constructor도 함께 정의해야 합니다. Default constructor에 구현할 내용이 없어도 빈 상태로라도 정의해야 합니다. +- `FooTest`라는 test fixture class가 const non-static data member를 가지고 있다면, default constructor를 생성한 후에 해당 default constructor의 initializer list에서 const member를 초기화해야만 합니다. `gcc`의 초기버전들은 이런 것을 강제화하지 않지만, `gcc 4`이후부터는 이렇게 해야합니다. + +## 왜 `ASSERT_DEATH`는 이미 join된 thread에 대해서 문제가 있다고 하나요? + +Linux pthread library를 사용할 떄, single thread에서 multiple threads로 한 번 넘어가면 원래 상태로는 절대로 돌아올 수 없습니다. 무슨 뜻이냐 하면 thread를 하나 만들면 이것을 관리하기 위한 manager thread도 기본적으로 함께 생겨나기 때문에 thread의 총 개수가 2개가 아니라 3개가 됩니다. 만약, 나중에 생겨난 thread가 main thread에 join되면 전체개수는 1 감소하지만 manager thread의 개수는 그대로 살아있습니다. 즉, join된 후에도 thread 개수는 2개가 유지됩니다. 결론적으로 multiple threads 환경으로 한 번 진입했다면 thread의 개수는 항상 2개 이상으로 유지되기 때문에 완벽하게 안전한 death test를 수행할 수는 없다는 의미입니다. + +새로운 NPTL thread library를 사용하면 이러한 문제가 없습니다. NPTL은 manager thread를 만들지 않기 때문입니다. 그러나 테스트가 실행되는 모든 환경에서 NPTL thread library를 사용할 수 있음이 보장되지 않는 한 NPTL thread library에만 의존해서 구현하면 안됩니다. + +## `ASSERT_DEATH`를 사용하기 위해 *DeathTest postfix를 사용할 때, 왜 개별 test case가 아닌 전체 test suite에 붙여야 하나요? + +Googletest는 서로 다른 test suite에 포함된 test case들의 순서를 섞지 않습니다. 즉, 하나의 test suite을 마무리하고 다음 test suite으로 넘어갑니다. 그 이유는 test suite(test case가 아니라)을 위한 set-up/tear-down 작업도 존재하기 때문입니다. 이런 상황에서 test suite이 교차되면 효율적이지 않으며 문맥자체도 깔끔하게 유지되지 않게 됩니다. + +테스트의 실행순서를 정할 때 test suite의 이름이 아니라 test case의 이름으로 정한다면 어떤 문제가 발생할까요? 아래 예제는 googletest가 `TEST_F(FooDeathTest, xxx)`가 아니라 `TEST_F(FooTest, xxxDeathTest)`와 같은 문법을 제공한다고 가정하고 작성한 코드입니다. (원래는 전자가 맞습니다.) + +```c++ +TEST_F(FooTest, AbcDeathTest) { ... } +TEST_F(FooTest, Uvw) { ... } + +TEST_F(BarTest, DefDeathTest) { ... } +TEST_F(BarTest, Xyz) { ... } +``` + +위 코드는 test case이름으로 순서를 정할 수 있음을 가정한 것이며 사용자는 `FooTest.AbcDeathTest` -> `BarTest.DefDeatTest` -> `FooTest.Uvw` -> `BarTest.Xyz` 의 순서로 호출되기를 기대하고 있습니다. 그러나 googletest에서는 test suite끼리 교차되는 것을 금지하기 때문에 `BarTest.DefDeathTest`는 절대 `FooTest.Uvw`보다 먼저 수행될 수가 없습니다. 즉, test case이름으로 실행순서를 정하게 되면 test suite끼리 교차될 수 없다는 googletest의 기본원칙과 대립되는 상황이 발생할 수 있게 됩니다. + +## Test suite 이름에 *DeathTest postfix를 붙이려고 합니다. 그런데 해당 test suite에 death test용이 아닌 test case도 포함되어 있다면 어떻게 해야 하나요? + +그런 경우에는 test suite의 별칭을 만들어서 사용하면 됩니다. 즉, `FooTest`, `FooDeathTest`와 같이 사용하면 됩니다. 아래 예제를 확인하기 바랍니다. + +```c++ +class FooTest : public ::testing::Test { ... }; + +TEST_F(FooTest, Abc) { ... } +TEST_F(FooTest, Def) { ... } + +using FooDeathTest = FooTest; + +TEST_F(FooDeathTest, Uvw) { ... EXPECT_DEATH(...) ... } +TEST_F(FooDeathTest, Xyz) { ... ASSERT_DEATH(...) ... } +``` + +## Death test의 child process 관련 LOG는 실패했을 때만 출력됩니다. 성공했을 때도 LOG를 보려면 어떻게 해야 하나요? + +`EXPECT_DEATH()`의 내부 statement에서 발생하는 LOG를 출력하는 것은 parant process의 LOG를 이해하기 어렵게 만들기 때문에 googletest는 실패했을 때만 출력하는 정책을 선택했습니다. + +만약 그래도 보고 싶다면 death test를 일부러 실패시켜야 합니다. 즉, `EXPECT_DEATH(statement, regex)`의 두 번째 argument인 `regex`를 살짝 바꿔서 일부러 실패하도록 하면됩니다. 물론, 추천할만한 방법은 아닙니다. 현재 진행중인 fork-and-exec-style death test의 개발을 완료한 후에 질문과 관련된 더 좋은 해결방방법도 찾아보겠습니다. + +## Assertion을 사용하면 "no match for `operator<<`"라는 compile error가 발생합니다. 무슨 뜻인가요? + +만약 사용자정의 타입(예: `FooType`)을 assertion에 사용했다면, `std::ostream& operator<<(std::ostream&, const FooType&)`도 제공해야만 합니다. 그래야 googletest에서 해당 타입의 정보를 출력해줄 수 있습니다. + +추가적으로 `FooType`이 특정 namespace에 선언되어 있다면 `operator<<`도 *같은* namespace에 정의해야 합니다. 이와 관련한 자세한 정보는 https://abseil.io/tips/49를 참조하세요. + +## Windows에서 memory leak message들을 안 보이게 하려면 어떻게 하면 될까요? + +Googletest singleton은 정적으로 초기화되기 데는데 이 때 heap에도 memory allocation을 하게 됩니다. 이로 인해 테스트가 종료되는 시점에 Visual C++ memory leak detector는 해제되지 않은 memory가 있다고 보고하게 됩니다. 이러한 memory leak message가 보기 싫다면 `_CrtMemCheckpoint`와 `_CrtMemDumpAllObjectsSince`를 사용하면 됩니다. 이렇게 Windows 환경에서 heap check/debug routine을 조절하는 방법은 MSDN에서 상세히 확인할 수 있습니다. + +## 현재 코드가 test 중임을 어떻게 알 수 있나요? + +혹시 테스트가 진행 중인지 확인하는 목적이 제품코드에서 테스트인지 아닌지에 따라 분기하는 코드를 구현하기 위함인가요? 그렇다면 좋은 방법이 아닙니다. 제품코드에 테스트관련 의존성이 생기기 때문입니다. 사소한 실수로 인해서 제품코드에서 테스트코드가 수행되버릴 확률도 높아집니다. [Heisenbugs](https://en.wikipedia.org/wiki/Heisenbug)(100% 재현되는게 아니라 간헐적으로 재현되는 bug)라도 발생하면 많은 시간을 소모하게 될 것입니다. Googletest에서도 그런 방법을 제공하지 않습니다. + +실행중에 테스트가 수행중인지 확인해서 분기하지 말고, 제품코드와 테스트코드에 동시에 사용할 수 있는 일반화된 코드를 개발하는 것이 중요합니다. 이를 위해서 추천하는 방법은 [Dependency Injection](https://en.wikipedia.org/wiki/Dependency_injection)을 사용하는 것입니다. 제품코드와 테스트코드의 의존성을 제거하고 구분이 필요하다면 [`testonly`](https://docs.bazel.build/versions/master/be/common-definitions.html#common.testonly)와 같은 빌드옵션을 통해서 구부하기 바랍니다. + +정말 추천하지는 않지만 실행중에 분기하는 것 외에는 정말 방법이 없다면, 제품 실행파일과 테스트 실행파일의 이름을 `_test`와 같은 postfix를 사용해서 구분하기 바랍니다. 그런 다음에 `main()`으로 전달되는 `argv[0]` 변수를 확인하면 됩니다. `argv[0]`에는 실행파일 이름이 저장되어 있기 때문에 구분이 가능할 것입니다. + +## 개별 test case를 비활성화 할 수 있을까요? + +테스트가 깨졌는데 지금 바로 고칠 수 없는 경우가 발생할 수도 있습니다. 이런 경우에는 `DISABLED_` 라는 prefix를 `TEST()`의 두 번째 argument 앞에 붙여줍니다. 이렇게 하면 테스트 실행에서 제외됩니다. 이런 비활성화 방법은 `#if 0`을 사용해서 주석처리하는 것보다 효츌적입니다. 왜냐하면 `DISABLED_` 비활성화는 컴파일 대상에는 계속 포함되기 때문에 아예 버려지는 것이 아니며 다시 활성화되기 전까지 유지보수 될 수 있습니다. + +만약, 이렇게 비활성화한 test case들도 실행해보고 싶다면 test program을 실행할 때, `--gtest_also_run_disabled_tests`라는 flag만 붙여주면 됩니다. + +## 서로 다른 namespace라면 같은이름을 가진 `TEST(Foo, Bar)`를 정의해도 되나요? + +가능합니다. + +이런 경우에 적용되는 규칙은 **동일한 test suite 이름을 사용하는 모든 test case는 동일한 test fixture class를 사용해야 한다**는 것입니다. 말이 좀 어렵운데 아래 예제를 보겠습니다. + +```c++ +namespace foo { +TEST(CoolTest, DoSomething) { + SUCCEED(); +} +} // namespace foo + +namespace bar { +TEST(CoolTest, DoSomething) { + SUCCEED(); +} +} // namespace bar +``` + +위 코드는 `CoolTest`라는 이름의 test suite가 있고, namespace별로 1개씩의 test case가 존재합니다. 먼저 2개의 test case는 각각의 full name이 namespace로 구분되기 때문에 문제가 없습니다. 다음으로 test suite이 동일하면서 test fixure class도 동일하기 때문에 또 문제가 없습니다. 이 때의 test fixture class는 `::testing::Test`를 의미하는데 왜냐하면 googletest에서 `TEST_F`가 아니라 `TEST`를 사용하면 test fixture class로서 `::testing::Test`를 공통적으로 사용하기 때문입니다. + +반면에 아래 코드는 적법하지 않으며 runtime error가 발생합니다. 왜냐하면 `CoolTest`라는 동일한 test suite에 포함된 2개의 test case가 서로 다른 test fixture class를 사용하고 있기 때문입니다. 위에 있는 것은 `foo::CoolTest`를 사용하고 아래는 `bar::CoolTest`를 사용하게 됩니다. + +```c++ +namespace foo { +class CoolTest : public ::testing::Test {}; // Fixture foo::CoolTest +TEST_F(CoolTest, DoSomething) { + SUCCEED(); +} +} // namespace foo + +namespace bar { +class CoolTest : public ::testing::Test {}; // Fixture: bar::CoolTest +TEST_F(CoolTest, DoSomething) { + SUCCEED(); +} +} // namespace bar +``` diff --git a/googletest/docs/kr/primer.md b/googletest/docs/kr/primer.md new file mode 100644 index 00000000..01de3727 --- /dev/null +++ b/googletest/docs/kr/primer.md @@ -0,0 +1,376 @@ +# Googletest Primer + +## 왜 googletest 인가? + +*Googletest*를 통해서 C++에서도 진보된 테스트코드를 구현할 수 있게 되었습니다. + +Googletest는 Google Testing Technology Team 주도로 개발이 시작되었습니다. Linux, Windows, Mac 등 *플랫폼 제한없이* 사용할 수 있으며 단위테스트 뿐만 아니라 다른 종류의 테스트에도 적용이 가능합니다. + +시작하기에 앞서, 좋은 테스트란 무엇이고 googletest가 이에 기여할 수 있는 부분을 알아보겠습니다. + +1. 테스트는 *독립적이고 반복적으로* 수행될 수 있어야 합니다. 테스트들이 서로 영향을 받는 다면 디버깅이 어려워질 것입니다. Googletest는 각각의 테스트가 서로 다른 object에서 수행될 수 있도록 도와줍니다. 따라서 테스트가 실패했을 때도 빠르게 디버깅이 가능합니다. +2. 테스트는 테스트 대상코드의 변화에 유연하게 대응할 수 있도록 구조화되어야 합니다. Googletest에서는 동일한 환경에서 실행되는 여러개의 Test를 Test Suite으로 묶어서 관리할 수 있으며(Test Suite도 여러개가 가능) 이를 통해 공통적으로 사용되는 데이터와 subroutine을 공유할 수 있습니다. 이러한 구조는 테스트의 가독성과 유지보수성을 증가시키며 테스트코드를 새로운 프로젝트에 적용할 때도 유용합니다. +3. 테스트는 *이식성 및 재사용성이 있어야 합니다.* Google에는 플랫폼 종속성이 없는 코드가 많이 존재하기 때문에 테스트코드 또한 플랫폼 종속성이 없어야 했습니다. 이를 위해 googletest는 특정 운영체제 및 컴파일러에 국한되지 않고 예외도 발생시키지 않는 방향으로 설계되었습니다. +4. 테스트는 실패했을 때, 그 문제에 대해 가능한 많은 *정보를 제공해야 합니다.* Googletest가 여러개의 Test를 수행하는 동안에는 1개의 Test가 실패하더라도 나머지 Test들은 계속해서 수행합니다. 또한, 실패한 Test가 non-fatal failure로 지정되었다면 해당 Test 자체도 중단하지 않습니다. 이러한 설계의 장점은 1번의 수행으로도 여러개의 bug를 찾아내고 개선할 수 있다는 것입니다. +5. 테스트 프레임워크는 테스트를 구현하는 사람이 실제 구현에만 집중할 수 있도록 도와줘야 합니다. Googletest는 정의된 모든 Test를 자동으로 찾아서 실행해주기 때문에 사용자가 하나하나 찾아서 실행할 필요가 없습니다. +6. 테스트는 *빨라야 합니다.* Googletest는 set-up/tear-down을 제공함으로서 여러 Test가 공유해야할 내용을 한번의 구현만으로도 재사용될 수 있도록 했습니다. + +Googletest가 xUnit architecture를 기반으로 설계되었기 때문에 JUnit, PyUnit과 같은 xUnit계열 테스트 프레임워크를 사용해본 경험이 있다면 빠르게 적응할 수 있습니다. 물론, 그러한 경험이 없더라도 기초적인 것을 배우고 구현하는 데에는 10분이면 충분합니다. + +## 용어 구분하기 + +*Note:* *Test*, *Test Case*, *Test Suite*과 같은 용어를 사용함에 있어서 googletest와 [ISTQB](http://www.istqb.org/) 간에 정의가 다르기 때문에 주의가 필요합니다. + +Googletest는 개별 테스트를 가리키는 용어로서 Test를 사용해왔고 여러 Test의 묶음을 의미하는 용어로 Test Case를 사용해왔습니다. 그러나 [ISTQB](http://www.istqb.org/)를 비롯한 표준기관에서는 여러 Test의 묶음을 의미하는 용어로 [*Test Suite*](http://glossary.istqb.org/search/test%20suite)를 채택하고 있어서 혼란이 발생하곤 했습니다. + +즉, googletest의 Test라는 용어를 ISTQB의 [*Test Case*](http://glossary.istqb.org/search/test%20case)와 동일한 의미로 사용하려고 해도 원래 googletest에서 Test 묶음을 의미하는 용어였던 Test Case와 충돌이 발생해서 문제가 되었던 것입니다. 결국 googletest는 *Test Case*라는 용어를 *Test Suite*으로 바꾸기로 결정했으며 *TestCase*로 시작하는 API들을 *TestSuite*으로 변경하는 과정에 있습니다. + +다만, 그러한 변경작업이 완료된 것은 아니기 때문에 이런 부분을 유념하여 용어를 구분하는데 문제가 없기를 바랍니다. 정리하면 개별테스트에 대해서는 Test, Test Case,`TEST()`가 사용되며 Test의 묶음에 대해서는 Test Suite을 사용하지만 일부 정리안된 Test Case가 남아있을 수도 있습니다. + +Meaning | googletest Term | ISTQB Term +------- | --------------- | ------------------------------------- +클래스 또는 단일function에 input 을 주고 그 결과를 확인하는 단위 | TEST() | Test Case +바로 위에서 표현한 단위에 대한 묶음 | Test Case → Test Suite (변경 중) | Test Suite + +## 기본 개념 + +Googletest를 사용한다고 하면 *assertion*을 쓰는 것부터 시작하게 됩니다. Assertion이란 어떤 조건이 참인지 거짓인지 검사하는 것을 의미하며 이러한 assertion의 결과는 *success*, *non-fatal failure*, *fatal failure* 중에 하나가 됩니다. 만약, Test를 진행하는 중에 fatal failure가 발생하면 해당 Test가 중단되지만, 반대로 success나 non-fatal failure가 발생하면 중단하지 않고 계속 진행합니다. + +각 Test에는 이러한 assertion을 사용해서 테스트 대상코드가 잘 동작하는지 구현하게 되며 assertion의 수행결과에 따라 Test의 *성공이나 실패도 결정됩니다.* + +Test Suite은 하나 이상의 Test를 포함합니다. Test들은 테스트 대상코드의 구조에 맞춰 여러개의 Test Suite으로 그룹화 될 수 있습니다. 또한, Test Suite에 속한 Test 간에 공유해야 할 자원이 있는 경우에는(object, function 등) *test fixture*를 사용하면 자원을 편리하게 공유할 수 있습니다. + +Test Program은 더 큰 개념으로서 여러개의 Test Suite을 포함합니다. + +이제 가장 하위에서 assertion을 직접적으로 사용하는 Test로부터 시작해서 나아가 Test Suite, Test Program을 어떻게 구현해야 하는지 설명하겠습니다. + +## Assertions + +Googletest의 assertion은 function처럼 보이기는 하지만 macro입니다. 사용자는 이러한 assertion을 통해 class나 function을 테스트하게 됩니다. Googletest는 assertion이 실패하면 해당 소스파일과 실패한 위치, 그리고 오류메시지(실패한 이유)와 같은 정보를 출력해줍니다. 그리고 이렇게 출력되는 정보를 사용자가 자유롭게 변경할 수도 있습니다. + +어떤 기능을 테스트할 때, 여러개의 assertion을 함께 사용하는 것도 가능합니다. Assertion을 상위 레벨에서 분류해보면 `ASSERT_*`계열과 `EXPECT_*`계열이 있습니다. 먼저, `ASSERT_*` 계열은 테스트가 실패하면 fatal failure를 발생시키며 현재 동작중인 function을 중단시킵니다. 다음으로 `EXPECT_*` 계열은 non-fatal failure를 발생시키며 현재 function을 중단시키지 않고 계속 진행합니다. 일반적으로는`EXPECT_*` 계열이 선호되는데 왜냐하면 어떤 Test에서 여러개의 failure가 발생한다고 가정하면 한 번의 실행만으로도 모든 failure를 검출해주기 때문입니다. 물론 failure가 발생했을 때, 계속해서 Test를 진행하는 것이 의미가 없다거나 또는 위험한 상황이라면 `ASSERT_*` 를 사용하는 것이 맞습니다. + +`ASSERT_*`는 실행중인 Test가 바로 종료되기 때문에, 메모리 해제와 같은 정리작업을 수행하지 않았을 수도 있습니다. 따라서 memory leak과 같은 문제를 야기시키기도 합니다. 사실 테스트코드에서 발생하는 memory leak은 문제가 될지 안될지 판단하기가 어렵습니다. 따라서 googletest의 heap checker를 활성화해서 assertion 오류메시지에 memory leak 관련내용을 포함시키는 것도 도움이 됩니다. + +Assertion을 사용함과 동시에 사용자정의 오류메시지를 추가하려면 아래처럼 `<<` 연산자를 사용하면 됩니다. + +```c++ +ASSERT_EQ(x.size(), y.size()) << "Vectors x and y are of unequal length"; + +for (int i = 0; i < x.size(); ++i) { + EXPECT_EQ(x[i], y[i]) << "Vectors x and y differ at index " << i; +} +``` + +Assertion에서 `ostream`을 통해 출력가능한 오류메시지는 C언어 문자열 또는 C++의 `string`입니다.혹시나 `std::wstring` 혹은 윈도우의 `UNICODE` 타입인 `wchar_t*`, `TCHAR*` 과 같은 wide string을 사용하면 UTF-8로 변환되어 출력됩니다. + +### Basic Assertions + +아래 assertion들은 true/false를 확인하는데 사용합니다. + +Fatal assertion | Nonfatal assertion | Verifies +--------------- | ------------------ | -------- +`ASSERT_TRUE(condition);` | `EXPECT_TRUE(condition);` | `condition` is true +`ASSERT_FALSE(condition);` | `EXPECT_FALSE(condition);` | `condition` is false + +다시한번 기억할 것은 fail이 발생하면 `ASSERT_*`는 fatal failure을 발생시킴과 동시에 현재 function을 중단시키지만 `EXPECT_*`는 non-fatal failure이기 때문에 중단시키지는 않고 계속 진행한다는 점입니다. 더 중요한 부분은 fatal failure든 non-fatal failure든 해당 Test의 최종결과는 fail을 의미한다는 것입니다. + +**Availability**: Linux, Windows, Mac. + +### Binary Comparison + +이 섹션에서는 2개의 값을 비교하는 assertion을 설명합니다. + +Fatal assertion | Nonfatal assertion | Verifies +--------------- | ------------------ | ------- +`ASSERT_EQ(val1, val2);` | `EXPECT_EQ(val1, val2);` | `val1 == val2` +`ASSERT_NE(val1, val2);` | `EXPECT_NE(val1, val2);` | `val1 != val2` +`ASSERT_LT(val1, val2);` | `EXPECT_LT(val1, val2);` | `val1 < val2` +`ASSERT_LE(val1, val2);` | `EXPECT_LE(val1, val2);` | `val1 <= val2` +`ASSERT_GT(val1, val2);` | `EXPECT_GT(val1, val2);` | `val1 > val2` +`ASSERT_GE(val1, val2);` | `EXPECT_GE(val1, val2);` | `val1 >= val2` + +위의 assertion을 사용하려면 `val1`, `val2`와 같은 argument들이 assertion 종류에 맞는 비교연산자(`==`, `<` 등)를 제공해야 합니다. `int`, `double`과 같은 산술타입들은 기본적으로 제공되겠지만 사용자 정의 타입이라면 그에 맞는 비교연산자를 구현해서 제공해야 합니다. 그렇지 않으면 당연하게도 compile error가 발생합니다. 이전에는 argument에 대한 정보를 출력하기 위해 `<<` 연산자도 필요했지만 현재는 그 부분은 꼭 제공하지 않아도 괜찮습니다. 만약 argument가 `<<`연산자를 지원한다면 assertion이 실패했을 때 정의된 연산자를 통해 관련정보를 출력하게 됩니다. 반대로 지원하지 않는다면 googletest가 출력할 수 있는 방법을 최대한 찾게 됩니다. 이러한 출력정보에 관련한 더 자세한 내용은 [여기](https://github.com/google/googletest/blob/master/googlemock/docs/cook_book.md#teaching-gmock-how-to-print-your-values)을 참고하세요. + +이처럼 assertion을 사용자정의 타입과 함께 사용하는 것이 가능하지만, 비교연산자도 함께 제공해야만 합니다. 다만, 이렇게 비교연산자를 제공하는 것은 Google [C++ Style Guide](https://google.github.io/styleguide/cppguide.html#Operator_Overloading)에서 권장하는 내용은 아니기 떄문에 비교연산자가 아닌 별도의 function을 통해 비교를 수행한 후에 그 결과인 `bool`값만 `ASSERT_TRUE()` 혹은 `EXPECT_TRUE()` 를 통해 검사하는 것도 괜찮습니다. + +물론 사용자 입장에서는 `ASSERT_TRUE(actual == expected)`보다 `ASSERT_EQ(actual, expected)`가 더 유용할 것입니다. 왜냐하면 후자를 사용하면 assertion에 `actual(실제값)`, `expected(기대값)` 을 모두 전달하기 때문에 실패시에 위의 값들을 출력해주기 때문입니다. 즉, 왜 실패했는지 더 빠르게 확인할 수 있게 됩니다. + +Argument가 변수형태라면 assertion코드가 수행되는 시점에 저장되어 있는 값을 사용합니다. 따라서 그 이후에는 argument를 수정해도 괜찮습니다. 또한, 일반적인 C/C++ function과 마찬가지로 argument를 판정하는 순서는 따로 정해져 있지 않습니다. 예를 들어 `ASSERT_EQ(val1, val2)`라고 구현했을 때 `val1`, `val2` 중에서 어느 것이 먼저 수행될지는 알 수 없습니다. 이러한 순서는 컴파일러마다 다를 수 있기 때문에 assertion을 구현할 때는 결과값이 그러한 순서에 따라 영향을 받지 않도록 주의하기 바랍니다. + +포인터에 대해서 `ASSERT_EQ()`를 사용하게 되면 의도한 것과 다르게 동작할 수도 있습니다. 왜냐하면 포인터가 가리키는 대상 전체가 아니라 가키리는 곳의 주소값만 비교하기 떄문입니다. 예를 들어, C언어 문자열(e.g. `const char*`) 2개를 비교한다고 가정하면 일반적으로 문자열 전체가 같은지를 비교하려는 목적일 것입니다. 그러나 여기서는 문자열을 비교하는 것이 아니라 memory location만 검사하게 됩니다. 만약, C언어 문자열의 내용을 비교하고 싶다면 `ASSERT_STREQ()`를 사용해야 합니다. 나중에 자세하게 설명하겠지만 C 문자열이 `NULL`이라는 것을 판정하려면 `ASSERT_STREQ(c_string, NULL)`를 사용하면 됩니다. C++11이 지원되는 경우에는 `ASSERT_EQ(c_string, nullptr)` 도 가능합니다. 마지막으로 2개의 string object를 비교한다면 `ASSERT_EQ`를 사용해야 합니다. + +포인터가 아무것도 가리키지 않는지 확인하고자 할 때는 `*_EQ(ptr, NULL)`, `*_NE(ptr, NULL)`가 아니라 `*_EQ(ptr, nullptr)`, `*_NE(ptr, nullptr)`와 같이 구현해야 합니다. `nullptr`을 사용해야 타입관련 문제가 발생하지 않습니다. 이와 관련한 자세한 내용은 [FAQ](faq.md)를 참조하십시오. + +만약 부동소수점 타입을 확인하고자 할 때는 반올림관련 문제가 있기 때문에 별도의 assertion을 사용해야 합니다. 자세한 내용은 [Advanced googletest Topics](advanced.md)를 참조하세요. + +이 섹션의 macro들은 narrow string(string), wide string(wstring) 양쪽 모두에 대해서 잘 동작합니다. + +**Availability**: Linux, Windows, Mac. + +**Historical note**: 2016년 2월 이전에는 `*_EQ` 계열 assertion을 `ASSERT_EQ(expected, actual)`과 같은 형태로 사용하는 규약이 있었기 때문에 그러한 소스가 많이 남아있습니다. 그러나 현재는 `expected`, `actual` 의 순서를 어떻게 하더라도 괜찮습니다. + +### String Comparison + +여기서는 C string을 비교하기 위한 assertion을 소개합니다. (만약, `string` object를 비교하고자 한다면 위에서 다룬 `EXPECT_EQ` `EXPECT_NE`를 사용하면 됩니다.) + +| Fatal assertion | Nonfatal assertion | Verifies | +| ------------------------------- | ------------------------------- | -------------------------------------------------------- | +| `ASSERT_STREQ(str1, str2);` | `EXPECT_STREQ(str1, str2);` | the two C strings have the same content | +| `ASSERT_STRNE(str1, str2);` | `EXPECT_STRNE(str1, str2);` | the two C strings have different contents | +| `ASSERT_STRCASEEQ(str1, str2);` | `EXPECT_STRCASEEQ(str1, str2);` | the two C strings have the same content, ignoring case | +| `ASSERT_STRCASENE(str1, str2);` | `EXPECT_STRCASENE(str1, str2);` | the two C strings have different contents, ignoring case | + +위의 assertion 중에서 중간에 "CASE"가 포함된 것은 대소문자는 무시하고 비교함을 의미합니다. 또한, `NULL`포인터와 empty string 문자열은 서로 *다른 것으로 간주됨을* 기억하세요. + +`*STREQ*` 및 `*STRNE*` 계열도 wide C string(`wchar_t*`)을 허용합니다. 단, 실패한 경우에 출력되는 정보는 UTF-8 narrow 문자열로 출력됩니다. + +**Availability**: Linux, Windows, Mac. + +**See also**: 기본적인 문자열 비교 외에 추가로 제공되는 기능(substring, prefix, suffix, regular expression 비교하기 등)이 궁금하다면 [Advanced googletest Guide](https://github.com/google/googletest/blob/master/googletest/docs/advanced.md)를 참조하세요. + +## 간단한 테스트 + +Test를 생성할 때에는 아래와 같은 순서로 진행합니다. + +* `TEST()` macro를 사용하여 Test의 이름을 정하고 구현합니다. `TEST()` macro는 return type이 `void`인 C++ function을 생성해줍니다. +* `TEST()` macro의 body에는 테스트에 필요한 C++ 소스코드를 구현하면 됩니다. 이 때 바로 assertion을 사용하게 되며 사용자가 확인하고 싶은 내용들을 직접 구현하면 됩니다. +* 앞에서도 얘기했듯이 어떤 Test의 수행결과는 assertion에 의해 결정됩니다. Failure의 종류에 관계없이(fatal, non-fatal) assertion 중에서 하나라도 실패하면 해당 Test의 최종결과는 실패입니다. 또는 소스코드 실행중에 crash가 발생해도 그 결과는 fail입니다. 이러한 문제들이 발생하지 않고 정상적으로 종료되었다면 당연하게도 success입니다. + +```c++ +TEST(TestSuiteName, TestName) { + ... test body ... +} +``` + +`TEST()`로 전달되는 2개의 argument 중에서 *첫 번째는* 상위 Test Suite의 이름이고 *두 번째가* Test 자신의 이름입니다. 두 이름 모두 C++ 언어에서도 유효한 식별자여야 하며 밑줄(`_`)을 포함하면 안됩니다. 이렇게 Test의 *full name*이 Test Suite의 이름과 개별 Test의 이름으로 구성된다는 것도 알게 되었습니다. (Test Suite이 서로 다르다면 Test 이름은 같아도 됩니다.) + +이제 `Factorial()`이라는 function을 테스트하는 간단한 예제를 보겠습니다. + +```c++ +int Factorial(int n); // Returns the factorial of n +``` + +아래 소스코드는 먼저 `Factorial`을 테스트하기 위한 2개의 Test가 있고, 이들이 모두 `FactorialTest`이라는 Test Suite에 포함되어 있음을 보여줍니다. + +```c++ +// Tests factorial of 0. +TEST(FactorialTest, HandlesZeroInput) { + EXPECT_EQ(Factorial(0), 1); +} + +// Tests factorial of positive numbers. +TEST(FactorialTest, HandlesPositiveInput) { + EXPECT_EQ(Factorial(1), 1); + EXPECT_EQ(Factorial(2), 2); + EXPECT_EQ(Factorial(3), 6); + EXPECT_EQ(Factorial(8), 40320); +} +``` + +Googletest가 Test Suite별로 테스트결과를 취합해 주기 때문에 Test Suite에는 논리적으로 관련이 있는 Test들을 모아두는 것이 좋습니다. 위 소스코드도 마찬가지로 `FactorialTest` 라는 Test Suite은 `Factorial()`을 테스트하기 위한 Input을 종류별로 구분해서 여러개의 Test로 나누어 놓았습니다. 좀 더 자세히 보면 `HandlesZeroInput`은 argument로 0을 전달하는 Test이고, `HandlesPositiveInput`는 argument로 0보다 큰 양수들을 전달합니다. 크게 보면 2개의 Test 모두 숫자를 전달해서 `Factorial()`을 검증한다는 공통적인 주제로 묶을 수 있는 것입니다. + +Test Suite과 Test의 이름을 만들때는 관련 규범인 [naming functions and classes](https://google.github.io/styleguide/cppguide.html#Function_Names)를 준수해야 합니다. + +**Availability**: Linux, Windows, Mac. + +## Test Fixtures: 동일한 데이터를 여러개의 테스트에 사용하기 + +유사한 데이터를 사용하는 2개 이상의 Test를 구현할 때에는 *test fixture*를 적용하는 것이 좋습니다. 이를 통해 여러개의 Test가 동일한 환경을 사용할 수 있도록 쉽게 구현할 수 있습니다. + +Fixture를 생성하는 방법 + +1. `::testing::Test` class를 상속받아서 test fixture class를 하나 만듭니다. 이제 `protected:` 영역에 필요한 내용을 구현할 것입니다. +2. test fixture class 내부에 여러 Test들이 공유하게 될 데이터(변수)를 선언합니다. +3. 필요하다면 `SetUp()`이나 default constructor를 정의함으로서 test fixture에 속한 각각의 Test들이 시작될때 공통적으로 수행할 일들(공유자원 자원할당 등)을 구현할 수 있습니다. 이 때, `SetUp()`을 **`Setup()`**으로 구현하는 대소문자 실수를 하는 경우가 많기 때문에 주의하기 바랍니다. C++11의 `override` 키워드를 사용한다면 실수를 줄일 수 있을 것입니다. +4. 필요하다면 `TearDown()`이나 destructor를 정의함으로서 개별 Test가 끝날때마다 공통적으로 수행할 일들(공유자원 자원해제 등)을 구현한 수 있습니다. `SetUp()/TearDown()`과 constructor/destructor를 어떻게 구분해서 사용해야 하는지 궁금하다면 [FAQ](faq.md)을 참조하기 바랍니다. +5. 필요하다면 각 Test가 공통적으로 수행하게 될 함수(subroutine)를 정의하는 것도 좋습니다. + +Test fixture를 사용하기 위해서는 개별 Test를 구현할 때, `TEST()` 대신에 `TEST_F()`를 사용해야 합니다. 이러한 `TEST_F`의 첫 번째 argument로는 위에서 `::testing::Test`를 상속받아 생성한 test fixture class의 이름을 전달합니다. + +```c++ +TEST_F(TestFixtureName, TestName) { + ... test body ... +} +``` + +`TEST()`와 마찬가지로 `TEST_F()`의 첫 번째 argument가 Test Suite의 이름인 것은 맞지만, 이름을 새로 만드는 것은 아니고 test fixture class 이름을 그대로 사용해야 합니다. `TEST_F()`에서 `_F`는 **f**ixture를 뜻합니다. + +`TEST`, `TEST_F`를 통합하면 좋겠지만 안타깝게도 C++에서 단일 macro를 가지고 2가지 타입을 동시에 조작하는 것은 불가능합니다. Macro를 상호간에 잘못 사용하면 compile error가 발생하니 유의하시기 바랍니다. + +또한, `TEST_F()` 를 사용하기 전에 fixture 클래스를 먼저 정의해야 하는 것은 당연합니다. 그렇지 않으면 "`virtual outside class declaration`" 과 같은 컴파일 에러가 발생합니다. + +Googletest는 `TEST_F()`로 정의된 각 Test가 실행될 때마다 해당 test fixture class의 object를 새롭게 생성합니다. 그런 후에 `SetUp()`을 통해 실행을 위한 준비작업을 하고 Test를 실행합니다. 이제 실행이 끝나면 `TearDown()`을 통해 정리작업을 하며 마지막으로 test fixture object를 삭제합니다. 이렇게 여러 Test들이 같은 Test Suite에 에 속해있다고 하더라도 별도의 test fixture object를 통해 독립적으로 수행됩니다. Googletest는 새로운 test fixture object를 생성하기 전에 항상 이전에 사용하던 object를 삭제하기 때문에 이전 Test에서 fixture를 변경시켰다고 해서 다음 fixture에 영향을 주지 않습니다. + +그럼 이제 `Queue`라는 class를 위한 Test를 한 번 구현해 보겠습니다. 먼저 `Queue` class는 아래와 같습니다. + +```c++ +template // E is the element type. +class Queue { + public: + Queue(); + void Enqueue(const E& element); + E* Dequeue(); // Returns NULL if the queue is empty. + size_t size() const; + ... +}; +``` + +이제 test fixture class를 정의합니다. 관례적으로 `Foo`라는 class 또는 function을 테스트할때는 test fixture class의 이름도 `FooTest`라고 하는 것이 일반적입니다. + +```c++ +class QueueTest : public ::testing::Test { + protected: + void SetUp() override { + q1_.Enqueue(1); + q2_.Enqueue(2); + q2_.Enqueue(3); + } + + // void TearDown() override {} + + Queue q0_; + Queue q1_; + Queue q2_; +}; +``` + +위 테스트에서는 각각의 Test를 수행한 후에 별도의 정리작업이 필요하지 않기 때문에 `TearDown()`은 굳이 정의하지 않았습니다. 기본적인 것들은 destructor를 통해 수행될 것이기 때문입니다. + +이제 `TEST_F()`를 통해 개별 Test를 구현합니다. + +```c++ +TEST_F(QueueTest, IsEmptyInitially) { + EXPECT_EQ(q0_.size(), 0); +} + +TEST_F(QueueTest, DequeueWorks) { + int* n = q0_.Dequeue(); + EXPECT_EQ(n, nullptr); + + n = q1_.Dequeue(); + ASSERT_NE(n, nullptr); + EXPECT_EQ(*n, 1); + EXPECT_EQ(q1_.size(), 0); + delete n; + + n = q2_.Dequeue(); + ASSERT_NE(n, nullptr); + EXPECT_EQ(*n, 2); + EXPECT_EQ(q2_.size(), 1); + delete n; +} +``` + +위 코드를 보면 `ASSERT_*`와 `EXPECT_*`를 모두 사용하고 있습니다. `EXPECT_*`는 failure가 발생해도 계속 진행하기 때문에 Test를 1번 실행하면서 최대한 많은 문제를 찾아내고 싶을 때 주로 사용합니다. 반면에 failure가 발생했을 때에 Test를 계속 진행하는 것이 아무런 의미가 없는 경우에는 `ASSERT_*`를 사용해서 바로 종료시키는 것이 좋습니다. 예를 들어, `DequeueWorks`의 `ASSERT_NE(n, nullptr)` 부분을 보면 `n`이 `nullptr`이라면 그 다음에 나오는 `EXPECT_EQ(*n, 1);`에서 segfault가 발생할 것이 자명하기 때문에 테스트를 진행하는 것은 의미가 없어집니다. 바로 이런 경우에는 Test를 중단시키는 것이 논리적으로 맞습니다. + +정리해보면 위의 test program을 실행하면 아래와 같은 일들이 일어나게 됩니다. + +1. googletest가 `QueueTest` object(이하 `t1`)를 새로 하나 생성합니다. +2. `t1.Setup()`에서 공유데이터를 초기화합니다. +3. 첫 번째 Test인 `IsEmptyInitially`가 `t1`을 통해 수행됩니다. +4. `IsEmptyInitially`가 종료되면 `t1.TearDown()`이 호출되어 필요한 정리작업을 수행합니다. +5. `t1`이 소멸됩니다. +6. 1~5단계를 반복합니다. 여기서는 다음 Test인 `DequeueWorks`에 대해서 동일한 작업을 진행합니다. + +**Availability**: Linux, Windows, Mac. + +## 테스트 실행하기 + +`TEST()` 및 `TEST_F()`를 통해 정의된 Test들은 googletest에 등록되어 내부적으로 관리하게 됩니다. 그렇게 등록된 Test들은 googletest가 자동으로 실행해 주기 때문에 다른 C++언어의 테스트 프레임워크와 달리 각각의 Test를 별도로 실행할 필요가 없습니다. + +Test program을 구현한 후 `main()` function에서 `RUN_ALL_TESTS()`만 호출하면 등록된 모든 Test를 실행해줍니다. ` RUN_ALL_TESTS()`는 `TEST()` 및 `TEST_F`를 사용한 모든 Test를 수행하고 성공하면 `0`을 반환하고 실패하면 `1` 또는 다른 값을 반환합니다. + +`RUN_ALL_TESTS()`을 수행하면 아래와 같은 동작들이 일어나게 됩니다. + +* 모든 googletest flag의 상태를 저장합니다. +* 제일 먼저 수행해야 할 Test를 위해서 test fixture object를 생성합니다. +* `SetUp()`을 통해 위에서 생성한 test fixture를 초기화합니다. +* Test를 수행합니다. +* `TearDown()`을 통해 text fixture를 정리합니다. +* test fixture object를 삭제합니다. +* 모든 googletest flags의 상태를 복구합니다. +* 위의 단계들을 모든 Test에 대해 반복합니다. + +주의할 점은 어떤 Test를 진행하다가 fatal failure가 발생했다면 그 다음 단계들은 수행하지 않고 다음 Test로 넘어간다는 점입니다. + +> IMPORTANT: `RUN_ALL_TESTS()`는 main() 의 맨 끝에서 호출되고 또 반환되어야만 합니다. 그렇지 않으면 compile error가 발생합니다. Googletest가 이렇게 설계된 이유는 자동화된 testing service 환경에서 test program의 성공여부를 stdout/stderr이 아닌 exit code를 통해서 자동으로 확인할 수 있도록 하기 위함입니다. 이를 위해서 `main()`은 반드시 `RUN_ALL_TESTS()`를 반환해야 합니다. +> +> 또한, `RUN_ALL_TESTS()`는 딱 한 번만 호출되어야 합니다. 이 function을 두 번 이상 호출하게 되면 몇몇 advanced googletest features(예: thread-safe [death tests](advanced.md#death-tests))와의 출동을 발생시키기 떄문에 지원하지 않고 있습니다. + +**Availability**: Linux, Windows, Mac. + +## main() function 작성하기 + +마지막으로 test program의 `main()` function을 작성하는 방법입니다. + +사용자가 구현을 시작할 때, 아래 코드를 복사해서 사용하는 것도 좋은 방법입니다. + +```c++ +#include "this/package/foo.h" +#include "gtest/gtest.h" + +namespace { + +// The fixture for testing class Foo. +class FooTest : public ::testing::Test { + protected: + // You can remove any or all of the following functions if its body + // is empty. + + FooTest() { + // You can do set-up work for each test here. + } + + ~FooTest() override { + // You can do clean-up work that doesn't throw exceptions here. + } + + // If the constructor and destructor are not enough for setting up + // and cleaning up each test, you can define the following methods: + + void SetUp() override { + // Code here will be called immediately after the constructor (right + // before each test). + } + + void TearDown() override { + // Code here will be called immediately after each test (right + // before the destructor). + } + + // Objects declared here can be used by all tests in the test suite for Foo. +}; + +// Tests that the Foo::Bar() method does Abc. +TEST_F(FooTest, MethodBarDoesAbc) { + const std::string input_filepath = "this/package/testdata/myinputfile.dat"; + const std::string output_filepath = "this/package/testdata/myoutputfile.dat"; + Foo f; + EXPECT_EQ(f.Bar(input_filepath, output_filepath), 0); +} + +// Tests that Foo does Xyz. +TEST_F(FooTest, DoesXyz) { + // Exercises the Xyz feature of Foo. +} + +} // namespace + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} +``` + +`::testing::InitGoogleTest()`는 command line으로부터 전달된 googletest flag들을 파싱하고 초기화합니다. 사용자는 flag를 통해서도 test program의 동작을 제어할 수 있습니다. Flag와 관련한 상세한 내용들은 [AdvancedGuide](advanced.md)에서 더 자세히 다루고 있습니다. 그리고 `::testing::InitGoogleTest()`는 `RUN_ALL_TESTS()`보다 먼저 호출되어야만 합니다. 그렇지 않으면 flag들이 초기화되지 않은 상태에서 Test가 수행되기 때문에 문제가 발생합니다. + +윈도우 환경에서는 `InitGoogleTest()`가 wide string에도 잘 동작되기 때문에 `UNICODE`모드에서 컴파일 된 프로그램에서도 사용할 수 있습니다. + +만약, 매번 동일한 `main()`코드를 구현하는 것이 귀찮다면 gtest\_main 이라는 library를 링크해서 사용하기를 바랍니다. 거기에는 `main()`을 포함한 기본적인 내용들이 포함되어 있습니다. + +NOTE: 예전에 사용하던 `ParseGUnitFlags()`는 `InitGoogleTest()`으로 대체되었습니다. + +## Known Limitations + +* Googletest는 thread-safe하게 설계되었습니다. 다만, 이러한 설계는 `pthreads` library를 사용할 수 있는 시스템에만 해당됩니다. 예를 들어 Windows 환경에서 2개의 thread를 생성하고 각각의 thread에서 googletest assertion을 동시에 사용한다면 문제가 발생할 수 있습니다. 물론 아주 예외적인 경우를 제외하고는 대부분 main thread에서 assertion을 수행하기 때문에 괜찮을 것입니다. 사용자의 환경에 맞는 동기화를 직접 구현하려 한다면 `gtest-port.h` 파일을 사용하시기 바랍니다.