1

Suppose I have a class

class MinorClass{
  virtual void NestedFunction(){...};
};

that is used to as following in another class

class MajorClass{
  public:
   MajorClass(unique_ptr<MinorClass>&& input)
     : minor_class{std::move(input)}{};
   ...
   std::unique_ptr<MinorClass> minor_class;
   void foo(){minor_class->NestedFunction();};
}

In gtest I want to create a test that tests if the NestedFunction() is called within foo(). For this I implement a mock Class in gtest:

class MockMinorClass : public MinorClass
{
public:
    MOCK_METHOD(void, NestedFunction, (), (override));
};

and then I implement a test such as:

TEST(MajorClassTest, TestingNestedCalls)
{
    // given
    auto minor_class = std::make_unique<MinorClass>(new MockMinorClass());

    auto major_class = MajorClass(std::move(minor_class));
    
    // then
    EXPECT_CALL(*(major_class.minor_class), NestedFunction()).Times(1);
    major_class.Init();
}

But I am getting the error MinorClass has no member named 'gmock_NestedFunction`.

How to test if an instance method is called from another class instance through a smart pointer to it?

EDIT

This a minimum verifiable working example that uses the current answer. In this example, the test is failing because the function is not called in reality.

A difference with the answer: the destructor must be defined virtual, otherwise memory leak of the mock object happens with the error:

ERROR: this mock object (used in test MajorClassTest.TestingNestedCalls) should be deleted but never is.

#include <gtest/gtest.h>
#include <gmock/gmock.h>
class MinorClass{
  public:
    virtual void NestedFunction(){
    std::cerr<<"Invoked MinorClass::NestedFunction()\n";
  };
  virtual ~MinorClass()=default;
};

class MajorClass{
  public:
   MajorClass(std::unique_ptr<MinorClass>&& input)
     : minor_class{std::move(input)}{};
   std::unique_ptr<MinorClass> minor_class;
   void foo(){
     std::cerr<<"Invoked MajorClass::foo()\n";
     minor_class->NestedFunction();
   };
};

class MockMinorClass : public MinorClass
{
public:
    MOCK_METHOD(void, NestedFunction, (), (override));
};


TEST(MajorClassTest, TestingNestedCalls)
{
    auto minor_class = std::make_unique<MockMinorClass>();

    // Keep the reference to the instance of `MockMinorClass`.
    auto* mock_minor = minor_class.get();

    auto major_class = MajorClass(std::move(minor_class));

    EXPECT_CALL(*mock_minor, NestedFunction());
}
roschach
  • 8,390
  • 14
  • 74
  • 124

1 Answers1

2

'MinorClass' has no member named 'gmock_NestedFunction' is a very correct error. std::unique_ptr<MinorClass> knows nothing about the actually pointed child class MockMinorClass.

Change the test case

TEST(MajorClassTest, TestingNestedCalls)
{
    auto minor_class = std::make_unique<MockMinorClass>();
    
    // Keep the reference to the instance of `MockMinorClass`.
    auto* mock_minor = minor_class.get();

    auto major_class = MajorClass(std::move(minor_class));
    
    EXPECT_CALL(*mock_minor, NestedFunction());
}

You don't need .Times(1), this is the default expected call count.

273K
  • 29,503
  • 10
  • 41
  • 64
  • Note one thing: the destructor of the mocked class must be declared virtual to avoid memory leak (see the edit) – roschach Oct 06 '22 at 08:26
  • As consequence of the rule of 3 or 5, also the copy constr and copy assignment operator should be redefined or deleted. – roschach Oct 06 '22 at 09:56
  • One more thing: in my real code I notice something that does not appear in the above example. I get the error `static assertion failed: result type must be constructible from value type of input range` related to the major class. If I delete the copy constr and the copy assignment operator, the error does not appear, but the test tries to call the copy constructor which now is undefined. However, if inside the test I use another `unique_ptr` to MajorClass instead of an instance, then the test passes (I guess because it is calling the copy constructor of the smart pointer). Need to investigate. – roschach Oct 06 '22 at 10:03
  • All the comments are unrelated to the original question. If you have a new question, please create a new post, don't edit the original one. – 273K Oct 06 '22 at 14:31