xyf
xyf

Reputation: 714

Mock a function throw an exception but Actual function call count doesn't match EXPECT_CALL

I want to mock the following method Ctrl::Do(const Handler& handler) to throw an exception.

Ctrl stores a weak pointer to Handler

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

And a reference of which gets passed to 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;
            }
        }
    }
};

But I get the error indicating the call never happens due to function mismatch.

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

Full code (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();
}

Upvotes: 1

Views: 652

Answers (1)

Sedenion
Sedenion

Reputation: 6142

You need to mark Ctrl::Do() as virtual, so that when Ctrl::Handling() is calling Ctrl::Do() it actually ends up calling CtrlMock::Do(). Also compare the documentation on how to mock methods.

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
    }
};

Regarding the operator==: It really depends on what you want to do. From the code I guess what you really want is to match the handlerMock by its address. For this, you can simply use testing::Ref:

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

and remove the HandlerMatcher completely. Full example on godbolt. Otherwise, if you do not want to compare addresses but rather the objects' values, I think it would be weird that a Handler cannot be compared but a HandlerMock can; i.e. at first sight it does make sense to have operator== coupled with Handler. In any case, you can of course always define the comparison operator== outside of Handler in your test code.

Upvotes: 2

Related Questions