GMOCK EXPECT_CALL on unique_ptr
Asked Answered
F

1

8

One of the benefits of dependency injection is ease of testing, as mocked classes can be injected. Clazz takes raw pointer for that purpose and moves it to unique pointer, to signal that it owns InjectedObj object:

Clazz::Clazz(InjectedObj *injectedObj)  : injectedObjPtr(injectedObj) { }

where injectedObjPtr is member:

std::unique_ptr<InjectedObj> injectedObjPtr;

doSth method calls executeSth by calling smart pointer:

//doSth method
int CLazz::doSth() {
return injectedObjPtr->executeSth();
}

I would like to test Clazz by setting some expectations on injected object, so my test looks similar to this:

TEST_F(testFixture, exeuteTest)
{
  //before
  InjectedObj* injectedObj = new InjectedObj();
  EXPECT_CALL(*injectedObj, executeSth())
    .Times(1)
    .WillOnce(Return(100));

  //when
  Clazz clazz(injectedObj);

  //then
  ASSERT_DOUBLE_EQ(clazz->doSth(), 100);
}

So in this simplified scenario clazz->doSth() is calling injectedObj->executeSth which should return 100, but it behaves like I would never set my expectation and always gets 0. Obviously if I will call my mocked object without smart pointer: injectedObj->executeSth it returns 100, but not when calling it using unique_ptr. Is there any way of telling gmock to set correct expectations when mocked object is managed by smart pointer? Thanks

Fullerton answered 23/7, 2018 at 7:43 Comment(5)
I do not quite understand what you are trying to do with: 1. Clazz::Clazz() takes a raw pointer and store it into std::unique_ptr<>; 2. std::shared_ptr<Clazz> clazz(injectedObj); directly pass the raw pointer to std::shared_ptr<>.Velvavelvet
Yes Clazz takes raw pointer to enable mocks dependency injection, and stores it as unique pointer as it owns it. 2. I also sore Clazz as shared_ptr to take care of cleanup. So basically I wan't to set expectation on injected mock, which becomes unique_ptr in Clazz constructor.Fullerton
Still do not get it. Please provide a Minimal, Complete, and Verifiable exampleVelvavelvet
I Too have the same exact Issue, The question states to find a way to test/mock shared_ptr of an object. In My case, the shared_ptr is of Clazz which is mock Of Claz main object. Bookmarking this issue, I am stuck on the same exact problem.Trivandrum
Do you have operator-> in Clazz? What it returns?Peritoneum
C
1

Given:

struct InjectedObj {
    MOCK_METHOD0(executeSth, int());
};

struct Clazz {
    Clazz(InjectedObj* injectedObj)
            : injectedObjPtr(injectedObj) {}

    int doSth() {
        return injectedObjPtr->executeSth();
    }

    std::unique_ptr<InjectedObj> injectedObjPtr;
};

TEST(testFixture, exeuteTest) {
    // before
    InjectedObj* injectedObj = new InjectedObj();
    EXPECT_CALL(*injectedObj, executeSth()).Times(1).WillOnce(testing::Return(100));

    // when
    Clazz clazz(injectedObj);

    // then
    ASSERT_DOUBLE_EQ(clazz.doSth(), 100);
}

I got:

[----------] 1 test from testFixture
[ RUN      ] testFixture.exeuteTest
[       OK ] testFixture.exeuteTest (0 ms)
[----------] 1 test from testFixture (0 ms total)

My bet: you've forgot to mark executeSth in the base class for InjectedObj a virtual method. This way, the gmock generated gmock_executeSth method that you've set expectations for, but in the test executeSth from the base class (without dynamic dispatch) is used, hence the test fails.

// Example, bad
// THE executeSth METHOD SHALL BE MARKED WITH virtual!!!!!
struct InjectedObj {
    virtual ~InjectedObj() = default;
    int executeSth() {
        return 0;
    }
};

struct InjectedObjMock : public InjectedObj {
    MOCK_METHOD0(executeSth, int());
};

struct Clazz {
    Clazz(InjectedObj* injectedObj)
            : injectedObjPtr(injectedObj) {}

    int doSth() {
        return injectedObjPtr->executeSth();
    }

    std::unique_ptr<InjectedObj> injectedObjPtr;
};

TEST(testFixture, exeuteTest) {
    // before
    InjectedObjMock* injectedObj = new InjectedObjMock();
    EXPECT_CALL(*injectedObj, executeSth()).Times(1).WillOnce(testing::Return(100));

    // when
    Clazz clazz(injectedObj);

    // then
    ASSERT_DOUBLE_EQ(clazz.doSth(), 100);
}

Output:

[ RUN      ] testFixture.exeuteTest
/home/dchrzanowski/gitrepos/priv/gtestgmock/so.cpp:41: Failure
      Expected: clazz.doSth()
      Which is: 0
To be equal to: 100
/home/dchrzanowski/gitrepos/priv/gtestgmock/so.cpp:35: Failure
Actual function call count doesn't match EXPECT_CALL(*injectedObj, executeSth())...
         Expected: to be called once
           Actual: never called - unsatisfied and active
[  FAILED  ] testFixture.exeuteTest (0 ms)
[----------] 1 test from testFixture (0 ms total)

Best way to avoid this: always make the base class pure virtual iface.

Compony answered 18/6, 2021 at 10:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.