Can Google Mock a method with a smart pointer return type?
Asked Answered
V

5

53

I have a factory that returns a smart pointer. Regardless of what smart pointer I use, I can't get Google Mock to mock the factory method.

The mock object is the implementation of a pure abstract interface where all methods are virtual. I have a prototype:

MOCK_METHOD0(Create, std::unique_ptr<IMyObjectThing>());

And I get:

"...gmock/gmock-spec-builders.h(1314): error C2248: 'std::unique_ptr<_Ty>::unique_ptr' : cannot access private member declared in class 'std::unique_ptr<_Ty>'"

The type pointed to in the smart pointer is defined.

And I get it's trying to access one of the constructors declared private, but I don't understand why. When this was an std::auto_ptr, the error said there was no copy constructor, which confuses me.

Anyway, is there a way to Mock a method that returns a smart pointer? Or is there a better way to build a factory? Is my only resolve to return a raw pointer (blech...)?

My environment is Visual Studio 2010 Ultimate and Windows 7. I'm not using CLI.

Vide answered 30/9, 2011 at 21:54 Comment(1)
Since this question was asked and answered google test/mock has been changed. See this answer.Zooplankton
U
-6

Google Mock requires parameters and return values of mocked methods to be copyable, in most cases. Per boost's documentation, unique_ptr is not copyable. You have an option of returning one of the smart pointer classes that use shared ownership (shared_ptr, linked_ptr, etc.) and are thus copyable. Or you can use a raw pointer. Since the method in question is apparently the method constructing an object, I see no inherent problem with returning a raw pointer. As long as you assign the result to some shared pointer at every call site, you are going to be fine.

Ugric answered 1/10, 2011 at 16:33 Comment(4)
You should not need to do any changes in your classes' interfaces just to make them working with your mocking framwork. Often enough this isn't possible at all. This is not an acceptable solution for me!Leaden
It does not seem acceptable to me for a factory class to return a raw pointer. In this case a unique_ptr makes the most sense. It was designed in part to solve the problem of "assigning the result to some shared pointer at every call site."Heartland
I don't think this is the correct answer, changing everything to raw or shared ownership is not a solution just to get things tested!Marc
It is possible now, see Return(ByMove(...))Spectacles
W
100

A feasible workaround for google mock framework's problems with non (const) copyable function arguments and retun values is to use proxy mock methods.

Suppose you have the following interface definition (if it's good style to use std::unique_ptr in this way seems to be more or less a philosophical question, I personally like it to enforce transfer of ownership):

class IFooInterface {
public:
    virtual void nonCopyableParam(std::unique_ptr<IMyObjectThing> uPtr) = 0;
    virtual std::unique_ptr<IMyObjectThing> nonCopyableReturn() = 0;
    virtual ~IFooInterface() {}
};

The appropriate mock class could be defined like this:

class FooInterfaceMock
: public IFooInterface {
public:
    FooInterfaceMock() {}
    virtual ~FooInterfaceMock() {}

    virtual void nonCopyableParam(std::unique_ptr<IMyObjectThing> uPtr) {
        nonCopyableParamProxy(uPtr.get());
    }
    virtual std::unique_ptr<IMyObjectThing> nonCopyableReturn() {
        return std::unique_ptr<IMyObjectThing>(nonCopyableReturnProxy());
    }


    MOCK_METHOD1(nonCopyableParamProxy,void (IMyObjectThing*));
    MOCK_METHOD0(nonCopyableReturnProxy,IMyObjectThing* ());
};

You just need to take care, that configurations (Actions taken) for the nonCopyableReturnProxy() method return either NULL or an instance allocated dynamically on the heap.


There's a google-mock user forum thread discussing this topic where one of the maintainers states that the google-mock framework won't be changed to support this in future arguing that their policies strongly discourage the usage std::auto_ptr parameters. As mentioned this is IMHO a philosophical point of view, and the capabilities of the mocking framework shouldn't steer what kind of interfaces you want to design or you can use from 3rd party APIs.

As said the answer describes a feasible workaround.

Wreckfish answered 18/7, 2012 at 18:41 Comment(10)
The thread in question you linked to talks mainly about auto_ptr by-value is a bad plan (and oh wow, is it, as are most things surrounding auto_ptr): but the question above is about unique_ptr, no?Scofflaw
@Yakk That's because I've been using std::auto_ptr in my actual testcases, but the same principle works for std::unique_ptr as well. What do you think, should I edit the question accordingly?Leaden
Just this bit: " this kind of using smart pointer parameters" -- the strong discouragement is about auto_ptr parameters, written in 2010 prior to the existence of unique_ptr. Modifying the framework so that auto_ptr be able to be ill-used is a pretty acceptable position: doing so after unique_ptr is on the scene is a different kettle of fish. If you examine the reasons why they dismiss auto_ptr as a reasonable parameter type, they do not apply to unique_ptr.Scofflaw
"... they do not apply to unique_ptr." You mean because the std::auto_ptr flaws were fixed with move construction?Leaden
The complaint was that by-value auto_ptr<X> silently steals state. Hence the argument you should make it a X* and explicitly .release() at the point of use. In C++11, unique_ptr<X> does not silently steal state (except when the value is expiring (temporary, or local return)), and requires an explicit move at point of transfer, which documents the movement of state to the call site. So the issue with auto_ptr<X> parameters that the googler highlighted explicitly does not apply to unique_ptr<X> arguments. There may be other arguments not to support it, but none in evidence.Scofflaw
Basically, I read your post, and was like "really, google doesn't think that unique_ptr should be in interfaces? What justification could they have?!", and discovered that the link contained no such justification, (partly) because it was from before unique_ptr existed. It is just that the pivot from unique_ptr in question, to auto_ptr in answer, muddies that issue. Change your answer to be about unique_ptr, and the link to the message board thread becomes a more obvious disconnect from the OP's issue. It describes why it was not supported in 2010: but says little about today.Scofflaw
@Yakk THX for your advice and input. I have edited the question accordingly.Leaden
This workaround can also help when trying to mock a function that returns a std::future.Fiberboard
@YodanTauber I don't actually know if that's also frequently asked meanwhile. But maybe worth it's own canonical. Feel free to write one and take the same technique.Leaden
For completeness, a link on how to solve the return type of the proxy by setting a Factory, to avoid generating nullptr unique_ptr onlyChantell
U
35

I know this post was from a long time ago, so you've probably discovered the answer by now.

gmock previously did not support mock functions that returned any movable type, including smart pointers. However, in April 2017, gmock introduced a new Action modifier ByMove.

EXPECT_CALL(*foo_, Bar(_, )).WillOnce(Return(ByMove(some_move_only_object)));

where some_move_only_object can be e.g. a std::unique_ptr.

So yes, now gmock can mock a function that takes a smart pointer.

Useful answered 31/1, 2019 at 17:1 Comment(4)
This answer points in the right direction, thank you! Here is a link to the current documentation on this topic: github.comPhotoemission
The syntax would actually be ByMove(std::move(some_move_only_object)). Also note that returning a nullptr also requires ByMove(nullptr).Aoudad
@Aoudad An additional std::move is only necessary if you're value isn't already an R-value.Merchandise
the github link from @Photoemission moved to: google.github.io/googletest/…Rodge
U
4

in the mock class put same as you want

MOCK_METHOD0(Create, std::unique_ptr());


and in the test as below

EXPECT_CALL(<mock-obj>, Create())
.WillOnce([]()->std::unique_ptr<IMyObjectThing>{
    return std::make_unique<IMyObjectThing>();
});

if vs 2010 not support for lambda, you can use a functor

Unskillful answered 15/8, 2019 at 4:0 Comment(0)
M
3

I have recently discovered that returning smart pointers by mocked functions is still not very user friendly. Yes, the new action ByMove had been introduced, however it turns out it may be used only once during the test. So imagine you have a factory class to test that repeatedly returns unique_ptr to the newly created object.

Any attempt to .WillRepeatedly(Return(ByMove) or multiple ON_CALL with .WillByDefault(Return(ByMove) will result in the following error:

[ FATAL ] Condition !performed_ failed. A ByMove() action should only be performed once.

This is also clearly stated in the GMock Cookbook in the paragraph "Mocking Methods That Use Move-Only Types".

Marashio answered 23/9, 2019 at 12:54 Comment(1)
You could consider using the Invoke action for multiple calls. eg. ON_CALL(mockFactory, makeX()).WillRepeatedly(Invoke([]{ return std::make_unique<MockX>();}));Rosinarosinante
U
-6

Google Mock requires parameters and return values of mocked methods to be copyable, in most cases. Per boost's documentation, unique_ptr is not copyable. You have an option of returning one of the smart pointer classes that use shared ownership (shared_ptr, linked_ptr, etc.) and are thus copyable. Or you can use a raw pointer. Since the method in question is apparently the method constructing an object, I see no inherent problem with returning a raw pointer. As long as you assign the result to some shared pointer at every call site, you are going to be fine.

Ugric answered 1/10, 2011 at 16:33 Comment(4)
You should not need to do any changes in your classes' interfaces just to make them working with your mocking framwork. Often enough this isn't possible at all. This is not an acceptable solution for me!Leaden
It does not seem acceptable to me for a factory class to return a raw pointer. In this case a unique_ptr makes the most sense. It was designed in part to solve the problem of "assigning the result to some shared pointer at every call site."Heartland
I don't think this is the correct answer, changing everything to raw or shared ownership is not a solution just to get things tested!Marc
It is possible now, see Return(ByMove(...))Spectacles

© 2022 - 2024 — McMap. All rights reserved.