# Mock Method Chromiumの中でテストを書く時gmockというものが使える。 [gMock](https://google.github.io/googletest/gmock_for_dummies.html)は、テストの際に本物を真似する挙動をするクラスを用意してその返答を使うことができる。 今日はその使い方や中身の確認を頑張る。 ## 使い方 以下のようにMock関数を定義することができる。 ```cpp= class Delegate { virtual void OnSomething() = 0; virtual void OnHoge(int hoge) = 0; virtual std::string GetFuga() = 0; virtual void DoConst(int c) const = 0; }; class MockDelegate : public Delegate { MOCK_METHOD0(DoSomething, void()); MOCK_METHOD1(OnHoge, void(int)); MOCK_METHOD0(GetFuga, std::string()); MOCK_CONST_METHOD1(DoConst, void(int)); } ``` 数字は引数の数を表している。 const関数は`MOCK_CONST_METHOD`とすれば良い。 他にもいくつか[マクロ](https://source.chromium.org/chromium/chromium/src/+/main:third_party/googletest/src/googlemock/include/gmock/gmock-function-mocker.h;drc=e7a3efac5cc6bb943f7943fe3ef0569b8aa77d7d;l=358)がある。 定義した関数は、呼ばれたかの確認や返り値の操作などが行える。 ```cpp= // Timesで何回呼ばれるかを確認 EXPECT_CALL(delegate_, DoSomething()).Times(3); // 呼ばれてはいけないやつは0に指定 EXPECT_CALL(delegate_, DoSomething()).Times(1); // 引数の確認も可能 EXPECT_CALL(delegate_, OnHoge(1)); // 引数がどうでも良いときは`_` EXPECT_CALL(delegate_, OnHoge(_)); // 返り値の操作もできる EXPECT_CALL(delegate_, GetFuga()).WillRepeatedly(testing::Return("hello")); ``` テスト中に、特定のオブザーバが呼ばれたかの確認をしたり、なんの引数を返しているかを確認したり、また返り値をinjectしたいときにも使える。 ## 中身 MOCK_METHOD0の`0`のような数字は引数の数を表している。 また、最初に渡している`DoSomething`のようなやつは`Method`にあたり、その先のマクロにも受け渡されていく。 ```cpp= #define MOCK_METHOD0(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 0, __VA_ARGS__) ... #define GMOCK_INTERNAL_MOCK_METHODN(constness, ct, Method, args_num, ...) \ GMOCK_INTERNAL_ASSERT_VALID_SIGNATURE( \ args_num, ::testing::internal::identity_t<__VA_ARGS__>); \ GMOCK_INTERNAL_MOCK_METHOD_IMPL( \ args_num, Method, GMOCK_PP_NARG0(constness), 0, 0, , ct, , \ (::testing::internal::identity_t<__VA_ARGS__>)) ``` まず[GMOCK_INTERNAL_ASSERT_VALID_SIGNATURE](https://source.chromium.org/chromium/chromium/src/+/main:third_party/googletest/src/googlemock/include/gmock/gmock-function-mocker.h;l=163;drc=5bd0f08e0dc2d879c40ada31a006c575c47cbc65)でvalidなシグネチャかを確認する。 実体は`std::is_function<__VA_ARGS__>::value`で関数型かの確認と、以下で変数サイズの確認 ```cpp= testing::tuple_size<typename ::testing::internal::Function< \ __VA_ARGS__>::ArgumentTuple>::value == _N ``` ここの中は副作用がなく、ただのstatic_assertたち。 [GMOCK_INTERNAL_MOCK_METHOD_IMPL](https://source.chromium.org/chromium/chromium/src/+/main:third_party/googletest/src/googlemock/include/gmock/gmock-function-mocker.h;l=177;drc=5bd0f08e0dc2d879c40ada31a006c575c47cbc65)の中で関数を生成。 いろいろやってるけど結局[GMOCK_MOCKER_](https://source.chromium.org/chromium/chromium/src/+/main:third_party/googletest/src/googlemock/include/gmock/gmock-function-mocker.h;l=516;drc=5bd0f08e0dc2d879c40ada31a006c575c47cbc65)が本体。 `gmock##constness##arity##_##Method##_`+`__LINE__`という名前の関数が作られているらしい。 つまり`gmock1OnHoge164`みたいな感じ。 EXPECT_CALLについては ```cpp= #define GMOCK_EXPECT_CALL_IMPL_(obj, call) \ ((obj).gmock_##call).InternalExpectedAt(__FILE__, __LINE__, #obj, #call) #define EXPECT_CALL(obj, call) GMOCK_EXPECT_CALL_IMPL_(obj, call) ``` のように他のExpectationと同じように[InernalExpectedAt](https://source.chromium.org/chromium/chromium/src/+/main:third_party/dawn/third_party/dxc/utils/unittest/googlemock/include/gmock/gmock-spec-builders.h;l=1268;drc=4ca3a042529b5a9791b044a13bc81ed8a10cd560)で処理される。 ### Return [testing::Return](https://source.chromium.org/chromium/chromium/src/+/main:third_party/devtools-frontend/src/extensions/cxx_debugging/third_party/llvm/src/third-party/unittest/googlemock/include/gmock/gmock-actions.h;l=1015;drc=a11cd0d94ed3cabf0998a0289aead05da94c86eb)はMock関数の返り値をinjectする。 ```cpp= template <typename R> internal::ReturnAction<R> Return(R value) { return internal::ReturnAction<R>(std::move(value)); } ``` これは[internal::ReturnAction](https://source.chromium.org/chromium/chromium/src/+/main:third_party/devtools-frontend/src/extensions/cxx_debugging/third_party/llvm/src/third-party/unittest/googlemock/include/gmock/gmock-actions.h;l=524;drc=a11cd0d94ed3cabf0998a0289aead05da94c86eb)というクラスで実装されている。 ReturnActionは、constructor時に渡された値を`value_`として保存し、[`operator Action<F>()`](https://source.chromium.org/chromium/chromium/src/+/main:third_party/devtools-frontend/src/extensions/cxx_debugging/third_party/llvm/src/third-party/unittest/googlemock/include/gmock/gmock-actions.h;l=534;drc=a11cd0d94ed3cabf0998a0289aead05da94c86eb)で返したActionクラスから[Perform](https://source.chromium.org/chromium/chromium/src/+/main:third_party/devtools-frontend/src/extensions/cxx_debugging/third_party/llvm/src/third-party/unittest/googlemock/include/gmock/gmock-actions.h;l=571;drc=a11cd0d94ed3cabf0998a0289aead05da94c86eb)で吐き出される。 このActionを例えば[WillOnce](https://source.chromium.org/chromium/chromium/src/+/main:third_party/dawn/third_party/dxc/utils/unittest/googlemock/include/gmock/gmock-spec-builders.h;l=994;drc=4ca3a042529b5a9791b044a13bc81ed8a10cd560)は受け取ることができ、一度走るexpectationとして格納する。 [`untyped_actions_`](https://source.chromium.org/chromium/chromium/src/+/main:third_party/dawn/third_party/dxc/utils/unittest/googlemock/include/gmock/gmock-spec-builders.h;l=864;drc=4ca3a042529b5a9791b044a13bc81ed8a10cd560)に格納された奴らはWillOnceの返り値であるTypedExpectationが[destruct](https://source.chromium.org/chromium/chromium/src/+/main:third_party/dawn/third_party/dxc/utils/unittest/googlemock/include/gmock/gmock-spec-builders.h;l=896;drc=4ca3a042529b5a9791b044a13bc81ed8a10cd560)とされたら走る。 Expectationが満たされた時が走るときなので、呼ばれたときに返してくれる感じになる。 ## Verify Expectations EXPECT_CALLを使う時、呼ばれるよりも先に書かないといけない。 チェックする瞬間に書かない都合上どのタイミングで呼ばれているか指定せず、想定していたタイミングと違うときのコールのせいでテストが誤って通ってしまうこともあり得る。 そんなときに[VerifyAndClearExpectations](https://source.chromium.org/chromium/chromium/src/+/main:third_party/googletest/src/googlemock/src/gmock-spec-builders.cc;l=621;drc=e7a3efac5cc6bb943f7943fe3ef0569b8aa77d7d)。 これは、それ以前のEXPECT_CALLなどのチェックを強制し、その時点でCALLが呼ばれていなかったとしたらFail扱いにしてそれ以降のテストにおいてはExpectationを参照しないようにする。