c++ 模拟函数引发异常,但实际函数调用计数与EXPECT_CALL不匹配

siv3szwd  于 2022-12-01  发布在  其他
关注(0)|答案(1)|浏览(247)

我想模拟下面的方法Ctrl::Do(const Handler& handler)抛出一个异常。
Ctrl存储指向Handler的弱指针

class Ctrl
{  
  std::weak_ptr<Handler> _handler;
  // ...
};

的引用传递给Ctrl::Do()

struct CtrlMock : public Ctrl
{
    CtrlMock(std::weak_ptr<Handler> handler) : Ctrl(handler) {}
    MOCK_METHOD(void, Do, (const Handler& handler), ());
    // ...

    void Handling()
    {
        if (std::shared_ptr<Handler> handlerSptr = _handler.lock())
        {
            try
            {
                Do(*handlerSptr);  // mock this method to throw an exception 
            }
            catch (const std::exception& e)
            {
                std::cout << e.what() << std::endl;
            }
        }
    }
};

但我得到错误,表明由于函数不匹配,调用从未发生。

GMOCK WARNING:
Uninteresting mock function call - returning directly.
    Function call: Action()
NOTE: You can safely ignore the above warning unless this call should not happen.  Do not suppress it by blindly adding an EXPECT_CALL() if you don't mean to enforce the call.  See https://github.com/google/googletest/blob/main/docs/gmock_cook_book.md#knowing-when-to-expect for details.
example.cpp:90: Failure
Actual function call count doesn't match EXPECT_CALL(ctrlMock, Do(HandlerMatcher(std::ref(*handlerMock))))...
         Expected: to be called once
           Actual: never called - unsatisfied and active
  • HandlerMockis a Handler,并且_handler确实存储了对HandlerMock的弱引用。除非在引擎盖下发生了对象切片,导致函数不匹配。这里是否遗漏了什么?
  • 不确定MATCHER是否按预期工作,因为Handler中的运算符未被调用。
  • 最后,如果我从Handler中删除operator==,MATCHER将失败。我是否可以将其仅放在HandlerMock中?我希望删除它,因为添加它只是为了测试目的。
    完整代码(sample
// --------- Handler ------------
struct Handler
{
    // Ideally wouldn't want to have an comparison operator within the 
    // main class (given it's solely for test purposes!), and rather in HandlerMock
    bool operator==(const Handler& other) const 
    {
        std::cout << "== operator called\n";   // doesn't get printed
        return true;
    }
    virtual void Action() const = 0;
};

struct HandlerMock : public Handler
{
    // bool operator==(const HandlerMock& other) const  // this fails!
    // {
    //     return true;
    // }
    MOCK_METHOD(void, Action, (), (const override));
};

// --------- Ctrl ------------
class Ctrl
{   
    std::weak_ptr<Handler> _handler;

    public:
    Ctrl(std::weak_ptr<Handler> handler) : _handler(handler) {}

    void Handling()
    {
        if (std::shared_ptr<Handler> handlerSptr = _handler.lock())
        {
            try
            {
                Do(*handlerSptr);  // mock this method to throw an exception
            }
            catch (const std::exception& e)
            {
                std::cout << e.what() << std::endl;
            }
        }
    }

    void Do(const Handler& handler)     // mock this method to throw an exception
    {
        std::cout << ">>>> Handler.Action <<<<\n";
        handler.Action();   // should not enter here since we mock'd Do() function
    }
};

struct CtrlMock : public Ctrl
{
    CtrlMock(std::weak_ptr<Handler> handler) : Ctrl(handler) {}
    MOCK_METHOD(void, Do, (const Handler& handler), ());
};

// --------- TEST STUFF ------------
MATCHER_P(HandlerMatcher, handler, "")
{
    return arg == handler;
}

ACTION(MyThrowException)    
{
  throw std::invalid_argument("Some exception thrown!");
}

TEST(UtClass, Test)
{
    auto handlerMock = std::make_shared<HandlerMock>();
    CtrlMock ctrlMock {handlerMock};

    // mocking CtrlMock::Do() to throw an exception 
    // however the mock function call doesn't happen!
    EXPECT_CALL(ctrlMock, Do(HandlerMatcher(std::ref(*handlerMock))))
                .Times(1)
                .WillRepeatedly(MyThrowException());

    ctrlMock.Handling();
}
w80xi6nr

w80xi6nr1#

您需要将Ctrl::Do()标记为virtual,这样当Ctrl::Handling()调用Ctrl::Do()时,它实际上会调用CtrlMock::Do()

class Ctrl
{   
    // ...

    virtual void Do(const Handler& handler)     // mock this method to throw an exception
    {
        std::cout << ">>>> Handler.Action <<<<\n";
        handler.Action();   // should not enter here since we mock'd Do() function
    }
};

关于operator==:这取决于你想做什么。从代码中我猜你真正想要的是通过地址匹配handlerMock。为此,你可以简单地使用testing::Ref

EXPECT_CALL(ctrlMock, Do(testing::Ref(*handlerMock)))
  .Times(1)
  .WillRepeatedly(MyThrowException());

并且完全删除HandlerMatcher。完整的例子是godbolt。否则,如果你不想比较地址,而是想比较对象的值,我认为不能比较Handler而可以比较HandlerMock会很奇怪;也就是说,乍看起来,将operator==Handler结合起来确实有意义。当然,在任何情况下,您都可以在测试代码中定义比较operator==outside of Handler

相关问题