0

I have a library class that I need to mock in one of my tests, and the object that accepts it takes a unique_ptr to one. After reading this answer, I assumed I could just do something like this.

class LibraryClassMock : public LibraryClass {
 public:
    MOCK_METHOD0(do, void());
};

TEST(ProxyServiceTest, RequestMade) {
    auto mock = std::make_unique<LibraryClassMock>();
    auto mockPtr = mock.get();

    // Setup mock
    EXPECT_CALL(*mockPtr, do()).Times(1);
   
    // Signature of constructor is MySerivce(std::unique_ptr<LibraryClass>)
    MyService service{std::move(mock)};
    proxyService.runCommand("cmd");
}

Unfortunately, LibraryClass does not have a virtual destructor (nor can I add one), and because MyService holds a pointer of the form unique_ptr<LibraryClass>, this scope ending causes a leak (as the mock's destructor is not called). Without reworking Service to accept a shared_ptr, is there a way to work around this? In my application, service should be controlling the lifetime of the object passed in, but I need to mock it for my test.

Quarra
  • 2,527
  • 1
  • 19
  • 27
ollien
  • 4,418
  • 9
  • 35
  • 58
  • 1
    If you cannot change the `LibraryClass`' destructor, you should have it as a member instead of inheriting it. – 김선달 Mar 30 '21 at 01:32
  • Can you not change LibraryClass? If it has virtual methods already, I believe making its destructor virtual is the most straightforward way. – Dave S Mar 30 '21 at 01:33
  • I can not change `LibraryClass`; sorry, I forgot to mention. @김선달 Is it possible to create a mock with the mocked class as a member? I've only ever seen it as inheritance in gmock. – ollien Mar 30 '21 at 01:34
  • You can't change `LibraryClass`, but can you change `MyService` to take in something with a virtual destructor that has `LibraryClass` as a member? – selbie Mar 30 '21 at 01:39
  • I can, but I'd prefer to avoid it if at all possible. – ollien Mar 30 '21 at 01:40
  • Personally, I'd change MyService to take a shared_ptr. Existing users can still provide a unique_ptr, and the shared_ptr will be directly constructed from that. I'm not sure there is a cleaner solution than that, given your constraints. – Dave S Mar 30 '21 at 01:50
  • Yeah, this might be the way to go. I was just wondering if there was perhaps some obvious way to work around it :) – ollien Mar 30 '21 at 01:51

1 Answers1

0

You could introduce some stub classes and wrapper that don't need a destructor. Then you put the g-mock methods on the stub class.

class TestLibraryClass {
public:
    virtual void do() {};
}

class LibraryClassMock : public TestLibraryClass {
public:
    MOCK_METHOD0(do, void());
};

class LibraryClassMockWrapper : public LibraryClass {
public:
    LibraryClassMock* pMock;

    void do() override {
       pMock->do();
    }
};


TEST(ProxyServiceTest, RequestMade) {

    auto mock = new LibraryClassMock();

    // Setup mock
    EXPECT_CALL(mock, do()).Times(1);

    // setup unique_ptr
    std::unique_ptr<LibraryClass> upWrapper(new LibraryClassMockWrapper()); // upWrapper owns mockWrapper lifetime, but not mock
    upWrapper->pMock = mock;
  
    // Signature of constructor is MySerivce(std::unique_ptr<LibraryClass>)
    MyService service{std::move(upWrapper)};
    proxyService.runCommand("cmd");

    delete mock;
}
selbie
  • 100,020
  • 15
  • 103
  • 173
  • 1
    Isn't that undefined behavior, when the upWrapper is deleted in the service scope, since there is no virtual destructor? – Dave S Mar 30 '21 at 02:03
  • @DaveS - I'm not a language lawyer, so I don't know if it's truly undefined behavior. But I wouldn't let something that pedantic cause me grief for writing *test code* to work around a mocking conundrum. – selbie Mar 30 '21 at 02:18
  • 1
    True, but since the original problem the OP is trying to solve is the same problem (deleting a base class without a virtual destructor), this isn't much of a solution. – Dave S Mar 30 '21 at 02:26