如何在GMOCK中为多参数方法Mock并抛出异常?
Handler::Foo 方法并抛出异常时参数匹配错误 我尝试Mock Handler::Foo 方法,使其抛出异常,该方法接收两个参数:SomeStruct 和 SomeClass。其中SomeStruct是在Source::Bar中动态创建的,SomeClass则从外部传入。
我使用了MATCHER,但它返回单个对象,与Foo所需的两个参数不匹配,因此出现如下错误:
no matching function for call to 'MockHandler::gmock_Foo(MatchMatcherP2<SomeStruct, SomeClass>)'
测试代码片段:
ACTION(MyThrowException) { throw std::invalid_argument("Some exception thrown!"); } TEST(UtClass, TestSourceBar) { auto mockHandler = std::make_shared<MockHandler>(); SomeStruct someStruct = {.a = 10}; SomeClass someClass; EXPECT_CALL(*mockHandler, Foo(Match(someStruct, someClass))) .Times(1) .WillRepeatedly(MyThrowException()); // ERROR }
是否有办法正确Mock Handler::Foo使其抛出异常?
完整代码示例:
struct SomeStruct { int a; }; class SomeClass { public: // some methods... }; class Handler { public: virtual void Foo(const SomeStruct&, SomeClass) { // stuff... } }; class Source { std::weak_ptr<Handler> _handler; public: Source(std::weak_ptr<Handler> handler) : _handler(handler) {} void Bar(SomeClass someClass) { SomeStruct s = {.a = 10}; if (auto sp = _handler.lock()) { sp->Foo(s, someClass); // simulate this call } } }; class MockHandler : public Handler { public: MOCK_METHOD(void, Foo, (const SomeStruct&, SomeClass), (override)); }; MATCHER_P2(Match, someStruct, someClass, "") { return arg.a == someStruct.a && arg.someClass == someClass; } ACTION(MyThrowException) { throw std::invalid_argument("Some exception thrown!"); } TEST(UtClass, TestSourceBar) { auto mockHandler = std::make_shared<MockHandler>(); SomeStruct someStruct = {.a = 10}; SomeClass someClass; EXPECT_CALL(*mockHandler, Foo(Match(someStruct, someClass))) .Times(1) .WillRepeatedly(MyThrowException()); // ERROR Source source(mockHandler); source.Bar(someClass); // should throw an exception... }
问题核心是MATCHER_P2的使用逻辑错误:MATCHER_P2是用来匹配单个参数的(参数为arg),你试图用它同时匹配Foo的两个参数,不符合Google Mock的规则。以下是几种正确的实现方式:
方法1:使用内置匹配器直接匹配参数
Google Mock提供了Eq()匹配器,只要参数类型支持==运算符即可使用。首先给SomeStruct和SomeClass添加默认的相等运算符:
struct SomeStruct { int a; bool operator==(const SomeStruct& other) const = default; }; class SomeClass { public: // some methods... bool operator==(const SomeClass&) const = default; };
然后修改EXPECT_CALL,同时使用Throw内置动作(无需自定义ACTION):
EXPECT_CALL(*mockHandler, Foo(Eq(someStruct), Eq(someClass))) .Times(1) .WillOnce(Throw(std::invalid_argument("Some exception thrown!")));
注:单次调用场景用WillOnce比WillRepeatedly更合适。
方法2:用With()结合Lambda校验多参数
如果需要自定义复杂的参数校验逻辑,可通过With()传入Lambda同时校验两个参数:
EXPECT_CALL(*mockHandler, Foo(_, _)) .Times(1) .With([&](const SomeStruct& s, const SomeClass& c) { return s.a == someStruct.a && c == someClass; }) .WillOnce(Throw(std::invalid_argument("Some exception thrown!")));
_表示匹配任意参数,Lambda中可自由编写校验逻辑。
方法3:为单个参数分别定义MATCHER
如果坚持使用自定义MATCHER,需要为每个参数单独编写匹配器:
// 匹配SomeStruct的自定义MATCHER MATCHER_P(MatchStruct, expected, "") { return arg.a == expected.a; } // 匹配SomeClass的自定义MATCHER MATCHER_P(MatchClass, expected, "") { return arg == expected; }
调用时分别传入对应参数的匹配器:
EXPECT_CALL(*mockHandler, Foo(MatchStruct(someStruct), MatchClass(someClass))) .Times(1) .WillOnce(Throw(std::invalid_argument("Some exception thrown!")));
最终可运行测试代码
#include <gmock/gmock.h> #include <gtest/gtest.h> #include <memory> #include <stdexcept> struct SomeStruct { int a; bool operator==(const SomeStruct& other) const = default; }; class SomeClass { public: // some methods... bool operator==(const SomeClass&) const = default; }; class Handler { public: virtual void Foo(const SomeStruct&, SomeClass) { // stuff... } }; class Source { std::weak_ptr<Handler> _handler; public: Source(std::weak_ptr<Handler> handler) : _handler(handler) {} void Bar(SomeClass someClass) { SomeStruct s = {.a = 10}; if (auto sp = _handler.lock()) { sp->Foo(s, someClass); // simulate this call } } }; class MockHandler : public Handler { public: MOCK_METHOD(void, Foo, (const SomeStruct&, SomeClass), (override)); }; TEST(UtClass, TestSourceBar) { auto mockHandler = std::make_shared<MockHandler>(); SomeStruct someStruct = {.a = 10}; SomeClass someClass; EXPECT_CALL(*mockHandler, Foo(Eq(someStruct), Eq(someClass))) .Times(1) .WillOnce(Throw(std::invalid_argument("Some exception thrown!"))); Source source(mockHandler); EXPECT_THROW(source.Bar(someClass), std::invalid_argument); } int main(int argc, char **argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }
内容的提问来源于stack exchange,提问作者xyf

