Search code examples
c++unit-testinggoogletestgooglemock

How to check if member method is called


I have a class(Let's say: Parent) that creates inside Member object.

I wonder, how I can test that member method is called on parent method call.

Let's see an example:

class Parent : public IParent
{
public:
    Member() : member{} {}
    void parent_method() override
    {
        member.member_method();
    }
private:
    Member member;
}

I would like to prepare test that would check if member_method is called on parent_method call using GTest / GMock.

Important: I would like to avoid polymorphic calls or any dynamic allocations - if it possible.

I had idea to pass some Factory in constructor and build proper Member object (real or mock) but it needs polymorphic calls and dynamic allocations. I want to avoid this overhead.

Is there any method to do it in quite clean way?

Any suggestions are welcome. Thanks in advance.


Solution

  • A solution is to do it with templates. The following is a variation of what is recommended in the google mock's documentation, mock non-virtual methods using templates:

    #include <iostream>
    #include <gtest/gtest.h>
    #include <gmock/gmock.h>
    
    using namespace std;
    
    // YOUR SUT
    class IParent
    {
    public:
        virtual ~IParent() {};
    
        virtual void parent_method() = 0;
    };
    
    class Member
    {
    public:
        void member_method()
        {
            cout << "member_method\n";
        }
    };
    
    template <class T = Member>   // make it a template
    class Parent : public IParent
    {
    public:
        Parent() : member{} {}
        void parent_method() override
        {
            member.member_method();
        }
    private:
        using member_type = T;
        member_type member; // member is now of type T (i.e. member_type)
    
        template <class U>
        friend U& GetMemberForTestMock(Parent<U>& parent); // necessary to set
                                                           // mock expectations
                                                           // for private member
    };
    
    
    // code in the test/mock file as this function is only for
    // testing with mockMembers
    template <class U>
    U& GetMemberForTestMock(Parent<U>& parent)
    {
        return parent.member;
    }
    
    // Your mock class
    class MockMember
    {
    public:
        MOCK_CONST_METHOD0(member_method, void());
    };
    
    
    // Google Test
    TEST(ParentTest, callsMemberMethodOnce)
    {
        auto parent = Parent<MockMember>{};
        EXPECT_CALL(GetMemberForTestMock(parent), member_method()).Times(1);
        parent.parent_method();
    }
    

    You could eliminate the friend statement all together by modifying Parent::Parent() so that you can pass a pointer to Member or MockMember:

    template <class T = Member>   // make it a template
    class Parent : public IParent
    {
    public:
        using member_type = T;
    
        Parent(member_type* m) : member{m} {}
        void parent_method() override
        {
            member->member_method();
        }
    private:
        member_type* member; // member is now of type T (i.e. member_type)
    };
    

    Then you can test like this:

    TEST(ParentTest, callsMemberMethodOnceAgain)
    {
        auto member = MockMember{};
        EXPECT_CALL(member, member_method()).Times(1);
    
        auto parent = Parent<MockMember>{&member};
        parent.parent_method();
    }
    

    Modifying Parent's interface to provide a way to specify which Member to pass (or to "inject" a test Mock) is probably a good idea since it increases modularity (i.e. decreases the dependency between the two objects). It enables you to provide Parent with different Members providing different functionality. This brings me to your concern about having several template parameters if you have more class members.

    You shouldn't need to mock all members, not even "many" members. Most "good" size classes with one responsibility have few dependencies. You normally only have to mock other classes that have orthogonal responsibilities and that your class depends on. A good design tries to minimize these dependencies. If you find that you have many classes which have many dependencies on other classes, it may be a sign that your design can be improved.

    That leaves us with performance since providing this modularity through polymorphism/inheritance comes at a cost due to the extra level of indirection. This is not the case, with templates since the type is selected at compile type. However, you may not want to make all of the classes with dependencies templates, so you have to reach a balance. It is difficult to "guess" what is going to significantly affect the performance of the overall system and by how much. In fact, there is no reliable way other than measuring. Therefore, I suggest that you take your best guess but concentrate on a good design and on your code being readable and working correctly. Then, after measuring, if there are performance problems, fix them. A lot depends on your domain, but in my experience, this kind of indirection is rarely the cause of performance problems.

    NOTE: I tested the code presented using gcc version 7.2.0 (with the C++17 flag) and googletest 1.8.0.