Update gmock_faq.md
This commit is contained in:
parent
26a29135fb
commit
5a016256c2
@ -8,11 +8,11 @@ Class method를 mocking하기 위해서는 기본적으로 *virtual*로 선언
|
||||
|
||||
흔히 (`...`) 형태의 argument로 대변되는 variadic function을 gMock에서 직접 mocking할 수는 없습니다.
|
||||
|
||||
1차적인 문제는 mock object는 variadic function이 몇 개의 argument가 전달할지 *알 수 없다는 것입니다.* 물론 argument type도 마찬가지 알 수 없습니다. 이것은 *base class를 직접 구현하는 사람만이 알고 있을 것입니다.*
|
||||
1차적인 문제는 googletest는 variadic function에 몇 개의 argument가 사용될지 *알 수 없다는 것입니다.* 물론 argument type도 마찬가지로 알 수가 없습니다. 이것은 *base class를 직접 구현하는 사람만이 알고 있을 것입니다.*
|
||||
|
||||
이런 variadic function을 mocking하려면 mock object에게 몇 개의 argument가 어떤 타입으로 전달될지를 알려줘야 한다는 것입니다. 이를 위한 한 가지 방법은 overloaded function을 제공하는 것입니다.
|
||||
이런 variadic function을 mocking하려면 mock object에게 몇 개의 argument가 어떤 타입으로 전달될지를 알려줘야만 합니다. 이를 위한 한 가지 방법은 overloaded function을 제공하는 것입니다.
|
||||
|
||||
사실(`...`) 형태의 argument는 C언어의 기능이며 C++에서 새로 생겨난 것은 아닙니다. 그렇기 때문에 C++의 기능들과 함께 사용할 때 100% 안전하지는 않습니다. 되도록이면 variadic function은 지양하기를 권장합니다.
|
||||
사실(`...`) 형태의 argument는 C언어의 기능이며 C++에서 새로 생겨난 것은 아닙니다. 그렇기 때문에 C++의 기능들과 함께 사용할 때 100% 안전하지는 않습니다. 되도록이면 variadic function 사용은 지양하기를 권장합니다.
|
||||
|
||||
### MSVC에서 const parameter를 전달받는 mock method를 compile하면 C4301, C4373과 같은 warning이 발생합니다. 왜 그런건가요?
|
||||
|
||||
@ -53,7 +53,7 @@ class Foo {
|
||||
|
||||
이렇게 method parameter에 `const`를 사용하는 것이 의미가 없기 때문에 MSVC에서 warning을 보기 싫다면 `Foo`와 `MockFoo`에서 `const`를 아예 제거하는 것도 괜찮습니다.
|
||||
|
||||
단, 현재 얘기하고 있는 `const`는 *top-level* `const`만 해당한다는 것을 주의하기 바랍니다. (쉽게 생각해서 value type 변수만) 만약, method parameter가 pointer나 reference라면 `const`는 여전히 의미가 있습니다. 예를 들어 아래코드의 `Bar()` 들을 서로 다릅니다.
|
||||
단, 현재 얘기하고 있는 `const`는 *top-level* `const`만 해당한다는 것을 주의하기 바랍니다. (쉽게 생각해서 value type 변수만) 만약, method parameter가 pointer나 reference라면 `const`는 여전히 의미가 있습니다. 예를 들어 아래코드의 `Bar()` 2개는 서로 다른 의미를 가집니다.
|
||||
|
||||
```c++
|
||||
void Bar(int* p); // Neither p nor *p is const.
|
||||
@ -62,17 +62,17 @@ void Bar(const int* p); // p is not const, but *p is.
|
||||
|
||||
### Expectation을 만족하지 못해서 테스트가 실패했는데, 뭐가 문제인지 잘 모르겠습니다. 어떻게 해야 할까요?
|
||||
|
||||
`--gmock_verbose=info`라는 flag와 함께 테스트를 수행하면, mock function의 trace까지 포함해서 최대한 많은 정보를 출력해 줍니다. 이러한 trace를 확인해보면 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](cheat_sheet.md#default-action-설정하기)을 적용해보기 바랍니다. 내부적인 이슈로 인해서 default action이 없는 상태에서 발생한 unexpected call에 대해서는 자세한 정보(actual argument와 expected argument 비교 등)는 출력하지 않고 있으며 위와 같은 message만 출력하고 있습니다.
|
||||
혹시, "The mock function has no default action set, and its return type has no default value set."이라는 message가 출력되었다면 [adding a default action](cheat_sheet.md#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`가 관련 정보를 출력해 주는 것입니다.
|
||||
테스트가 실패하면 failure signal handler는 최대한 많은 정보를 수집하려고 합니다. (stack trace, address map 등을 포함해서) 복잡한 환경일수록 정보도 많습니다. 예를 들어 여러 thread와 그들의 stack으로부터도 정보가 계속 수집됩니다. 그러다가 문제가 발생하는 시점에 `ScopeMockLog`이 관련 정보를 출력해 주는 것입니다.
|
||||
|
||||
물론, 이러한 error를 출력하지 않는 방법도 제공을 하고 있습니다. 아니면 아래 코드와 같이 구현해서 관심대상이 아닌 내용을 구분하도록 해도 됩니다.
|
||||
물론, 이러한 error를 출력하지 않는 방법도 제공을 하고 있습니다. 아니면 아래 코드와 같이 관심대상이 아닌 내용을 구분하도록 구현해도 됩니다.
|
||||
|
||||
```c++
|
||||
using ::testing::AnyNumber;
|
||||
@ -85,6 +85,8 @@ using ::testing::Not;
|
||||
|
||||
### Mock function이 호출되지 않기를 바란다면 어떻게 구현해야 하나요?
|
||||
|
||||
아래처럼 구현하면 됩니다.
|
||||
|
||||
```c++
|
||||
using ::testing::_;
|
||||
...
|
||||
@ -127,7 +129,7 @@ class Derived : public Base {
|
||||
// - value_ is leaked.
|
||||
```
|
||||
|
||||
주석에 설명한 것처럼 위 코드에서는 `delete p`를 해도 `Derived` class의 destructor가 호출되지 않습니다. 해결방법은`~Base()`를 `virtual ~Base()`로 변경하는 것입니다. 그러면 heap checker의 결과도 문제가 없을 것입니다.
|
||||
주석에 설명한 것처럼 위 코드에서는 `delete p`를 해도 `Derived` class의 destructor가 호출되지 않습니다. 해결방법은 `~Base()`를 `virtual ~Base()`로 변경하는 것입니다. 그러면 heap checker의 결과도 문제가 없을 것입니다.
|
||||
|
||||
### "newer expectations override older ones"라는 규칙이 이상해 보입니다. gMock은 왜 이런 정책을 선택한건가요? ###
|
||||
|
||||
@ -147,7 +149,7 @@ using ::testing::Return;
|
||||
.RetiresOnSaturation();
|
||||
```
|
||||
|
||||
위의 코드를 구현하면서 (주석에 쓰여있는) 불평을 하고 있다면 과연 적절한 방법으로 구현한 것인지 고민해 볼 필요가 있습니다.
|
||||
위의 코드를 구현하면서 (주석에 쓰여있는 것과 같은) 불평을 하고 있다면 과연 적절한 방법으로 구현한 것인지 고민해 볼 필요가 있습니다.
|
||||
|
||||
gMock의 기본적으로 expectation간에 *어떠한 호출순서도 부여하지 않습니다.* (탐색순서만 있습니다.) 이것은 gMock 그리고 jMock의 기본철학입니다. 이러한 규칙은 사용자가 테스트를 너무 과하게 지정하는 실수를 줄이기 위한 것입니다. 만약, expectation간에 호출순서를 부여하고 싶다면 사용자가 직접 명시적으로 표현해야 합니다.
|
||||
|
||||
@ -183,24 +185,15 @@ using ::testing::Return;
|
||||
.RetiresOnSaturation();
|
||||
```
|
||||
|
||||
그럼 왜 gMock은 expectation들의 호출순서는 강제하지 않으면서 탐색할 때는 밑에서부터 탐색할까요? 왜냐하면 이렇게 해야 mock의 동작을 일반적인 내용부터 시작해서 상세한 동작으로 점진적으로 지정할 수 있기 때문입니다. 즉, mock의 set-up 시점에 일반적인 expectation을 지정하고, 각각의 test case에서는 좀 더 상세한 설정을 지정하는 것입니다. 만약, gMock이 위에서부터 아래 방향으로 expectation을 검색하면 위와 같은 패턴을 적용할 수 없게 됩니다.
|
||||
그럼 왜 gMock은 expectation들의 호출순서는 강제하지 않으면서 탐색할 때는 밑에서부터 탐색할까요? 왜냐하면 이렇게 해야 mock의 동작을 일반적인 내용부터 시작해서 상세한 동작으로 점진적으로 지정할 수 있기 때문입니다. 즉, mock의 set-up 시점에 일반적인 expectation을 지정하고, 각각의 test case에서는 좀 더 상세한 expectation을 지정하는 것입니다. 만약, 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을 통해 알려주는 것입니다.
|
||||
사람들은 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 발생을 막을 수는 없습니다.
|
||||
예제를 통해 확인해보면, 아래처럼 `ON_CALL`을 통해서 default behavior를 변경했다고 해도 warning은 계속해서 출력됩니다.
|
||||
|
||||
```cpp
|
||||
using ::testing::_;
|
||||
@ -209,13 +202,22 @@ using ::testing::_;
|
||||
.WillByDefault(...);
|
||||
```
|
||||
|
||||
발생한 호출이 문제가 없어서 warning이 불필요하다면 아래처럼 구현하기만 하면 됩니다.
|
||||
|
||||
```cpp
|
||||
using ::testing::_;
|
||||
...
|
||||
EXPECT_CALL(foo, Bar(_))
|
||||
.WillRepeatedly(...);
|
||||
```
|
||||
|
||||
이제 gMock은 해당 method가 호출되어도 warning을 출력하지 않을 것입니다.
|
||||
|
||||
더불어 gMock이 출력하는 내용을 조절하는 것도 가능합니다. 디버깅을 해야하는데 너무 많은 정보가 출력되어 어려움을 겪고 있다면 `--gmock_verbose` flag의 레벨을 조절하시기 바랍니다.
|
||||
|
||||
### Action에서 mock function으로 전달되는 argument를 삭제(delete)하려면 어떻게 해야 하나요? ###
|
||||
|
||||
Mock function이 전달받는 pointer argument를 삭제하려고 하는 건가요? 그런 경우라면 `testing::DeleteArg()`를 사용해서 N번째(0부터 시작) argument를 삭제 할 수 잇습니다.
|
||||
Mock function이 전달받는 pointer argument를 삭제하려고 하는 건가요? 그런 경우라면 `testing::DeleteArg()`를 사용해서 N번째(0부터 시작) argument를 삭제할 수 잇습니다.
|
||||
|
||||
```c++
|
||||
using ::testing::_;
|
||||
@ -254,17 +256,17 @@ using ::testing::Invoke;
|
||||
|
||||
gMock이라는 도구를 통해 C++에서도 간단하게 mock을 사용할 수 있게 되었지만 여전히 gMock조차도 어려워하는 사람들이 많이 있습니다. 무엇이 문제일까요?
|
||||
|
||||
먼저, mock이 없이 테스트코드를 구현한다고 가정해 봅시다. 이 때에는 어떤 변수나 시스템의 상태(state)를 검증하게 됩니다. 여기서 상태는 어떤 동작이 완료된 후에 변화된 결과를 의미하며 이렇게 상태를 기반으로 검증하는 방법을 "state-based testing" 이라고 부르기도 합니다.
|
||||
먼저, mock이 없이 테스트코드를 구현한다고 가정해 봅시다. 이 때에는 어떤 변수나 시스템의 상태(state)를 검증하게 됩니다. 여기서 상태는 어떤 동작이 완료된 후에 변화된 결과를 의미하며 이렇게 상태를 기반으로 검증하는 방법을 "state-based testing"이라고 부르기도 합니다.
|
||||
|
||||
반면에 mock을 사용하면 "interaction-based testing"이라는 방법을 사용하게 됩니다. 즉, 시스템의 상태를 검증하는 것이 아니라 상태를 변화시키기 위한 일련의 동작들이 원하는 방향으로 진행되고 있는지 확인하고 그 결과를 바로 알려줍니다. 그 과정중에 발생한 문제가 있다면 해당상황에 대한 정보도 제공해줍니다. 많은 경우에 "interaction-based testing" 접근방법이 "state-based testing" 보다 효과적이고 경제적으로 여겨집니다.
|
||||
반면에 mock을 사용하면 "interaction-based testing"이라는 방법을 사용하게 됩니다. 즉, 시스템의 상태를 검증하는 것이 아니라 상태를 변화시키기 위한 일련의 동작들이 원하는 방향으로 진행되고 있는지 확인하고 그 결과를 바로 알려줍니다. 그 과정에서 발생한 문제가 있다면 해당 상황에 대한 정보도 제공해줍니다. 많은 경우에 "interaction-based testing" 접근방법이 "state-based testing"보다 효과적이고 경제적으로 여겨집니다.
|
||||
|
||||
물론, mock이 아닌 다른 test double(fake 등) + "state-based testing"로도 충분하고 그렇게 하는 것이 적절한 상황이라면 당연히 그렇게 구현해야 합니다. Mock을 적용하는 것도 노력이 필요한 일이기 때문에 꼭 필요한 곳에만 적용하기 바랍니다. 즉, 사용자의 환경에 mock을 적용하기가 어렵다면 그 상황에 더 알맞은 다른 도구를 검토해 볼 필요도 있다는 것입니다. 아니면 gMock을 사용하면서 어려웠던 부분을 저희와 함께 개선해보면 어떨까요?
|
||||
물론, mock이 아닌 다른 test double(fake 등) + "state-based testing"으로도 충분하고 그렇게 하는 것이 적절한 상황이라면 당연히 그렇게 구현해야 합니다. Mock을 적용하는 것도 노력이 필요한 일이기 때문에 꼭 필요한 곳에만 적용하기 바랍니다. 즉, 사용자의 환경에 mock을 적용하기가 어렵다면 그 상황에 더 알맞은 다른 도구를 검토해 볼 필요도 있다는 것입니다. 아니면 gMock을 사용하면서 어려웠던 부분을 저희와 함께 개선해보면 어떨까요?
|
||||
|
||||
### "Uninteresting function call encountered - default action taken.." 이라는 warning이 발생했습니다. 큰 문제인가요? ###
|
||||
### "Uninteresting function call encountered - default action taken.." 이라는 warning이 발생했습니다. 큰 문제인가요? ###
|
||||
|
||||
당연히 아닙니다, 단순히 정보를 전달하기 위한 목적입니다.
|
||||
|
||||
해당 warning은 어떤 mock function에 아무런 expectation을 지정하지 않았는데도 호출되었음을 의미합니다. 이러한 경우에 gMock은 expectation이 없기 때문에 몇 번 호출되든 상관없다고 판단합니다. "호출되면 안 된다"라고 지정한 것이 아니기 때문에 warning외에 다른 문제는 없을 것입니다.
|
||||
해당 warning은 어떤 mock function에 아무런 expectation을 지정하지 않았는데도 호출되었음을 의미합니다. 이러한 경우에 gMock은 expectation이 없기 때문에 몇 번 호출되든 상관없다고 판단합니다. "호출되면 안 된다"라고 지정한 것이 아니기 때문에 warning 외에 다른 문제는 없을 것입니다.
|
||||
|
||||
gMock은 warning을 통해서 잠재적인 문제에 대한 정보를 사용자에게 알려줍니다. 즉, warning이 발생했다면 관련 내용을 검토해 볼 필요가 있습니다. 예를 들어 원래는 "호출되면 안 된다"라고 하고 싶었는데 깜박했을 수도 있습니다. 즉, `EXPECT_CALL(foo, Bar()).Times(0)`이라는 구현을 빼먹은 경우입니다.
|
||||
|
||||
@ -274,13 +276,13 @@ gMock은 warning을 통해서 잠재적인 문제에 대한 정보를 사용자
|
||||
|
||||
어느 것을 사용해도 괜찮습니다. 상황에 맞는 것을 고르면 됩니다.
|
||||
|
||||
보통, action이 특정타입에 대해서만 수행된다면 `Invoke()`를 사용해서 구현하는게 쉽습니다. 반대로 `Return(value)`의 동작방식 처럼 같이 여러가지 타입에 두루두루 사용할 수 있는 action을 원한다면 `MakePolymorphicAction()`를 사용하는 것이 좋습니다. 후자의 경우에는 `ActionInteface`를 통해서 구조를 좀 더 간결하게 할 수도 있습니다. 관련 예제로 `include/gmock/gmck-actions.h` 파일의 `Return()` 구현부를 보면 도움이 될 것입니다.
|
||||
일반적으로 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()`을 둘 다 수행할 수 있도록 연결해주시기 바랍니다.
|
||||
Mock method가 호출되었지만 어떤 값을 반환해야 할지 모를 때에 위와 같은 에러가 발생합니다. `SetArgPointee()`는 argument에 값을 저장하는 동작만 수행하는 action입니다. 이런 문제가 발생하는 이유는 mock method에 대한 `Return()` 동작을 지정하지 않았기 때문입니다. `DoAll()`을 사용해서 `SetArgPointee()`와 `Return()`을 둘 다 수행할 수 있도록 연결해주시기 바랍니다.
|
||||
|
||||
[여기](cook_book.md#mocking에서-side-effects부수효과-사용하기)에 자세한 설명과 예제코드가 있습니다.
|
||||
[여기](cook_book.md#action-조합하기)의 예제를 참조하기 바랍니다.
|
||||
|
||||
### Microsoft Visual C++에서 out out memory라고 하며 compile에 실패합니다. 어떻게 해야하나요? ###
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user