How to mock methods return object with deleted copy-ctor?
Asked Answered
S

2

15

If an interface has a function to create an object with deleted copy-ctor, how to mock this function? Gmock seems to use the object's copy constructor internally.

E.g.

// The object with deleted copy-ctor and copy-assignment
class TTest
{
public:
    TTest() = delete;
    TTest(const TTest&) = delete;
    TTest& operator=(const TTest&) = delete;
    TTest(TTest&&) = default;
    TTest& operator=(TTest&&) = default;

    explicit TTest(int) {
    }
};

// My interface to mock
class MyInterface
{
    public:
        virtual ~MyInterface() {}
        virtual TTest GetUniqueTest() = 0;
};

// The mock
class MockMyInterface: public MyInterface{
    public:
        MOCK_METHOD0(GetUniqueTest, TTest());
}

The compile error says:

gmock/gmock-spec-builders.h:1330:20: error: use of deleted function 'TTest::TTest(const TTest&)'
     T retval(value_);
...
gmock/gmock-actions.h:190:52: error: use of deleted function 'TTest::TTest(const TTest&)'
         internal::BuiltInDefaultValue<T>::Get() : *value_;
...
gmock/internal/gmock-internal-utils.h:371:71: error: use of deleted function 'TTest::TTest(const TTest&)'
       *static_cast<volatile typename remove_reference<T>::type*>(NULL));

If the method returns std::unique_ptr<T>, the error is the same since std::unique_ptr<T> has deleted copy-ctor as well.

So my question is: how to mock such methods that return objects with deleted copy-ctors?

I'm using googletest v1.7, GCC 5.3.0, and Ubuntu 14.04.1.

Shopper answered 28/2, 2017 at 9:39 Comment(0)
P
5

As mentioned in the comments by Mine, Google Test 1.8 seems to support mocking such functions (documentation).

As for 1.7 I've found a solution here.

First, create an utility class to wrap non-copyable objects:

template <typename T>
class Mover
{
public:
    Mover(T&& object)
      : object(std::move(object)),
        valid(true)
    {
    }

    Mover(const Mover<T>& other)
      : object(const_cast<T&&>(other.object)),
        valid(true)
    {
        assert(other.valid);
        other.valid = false;
    }

    Mover& operator=(const Mover& other)
    {
        assert(other.valid);
        object = const_cast<T&&>(other.object);
        other.valid = false;
        valid = true;
    }

    T& get()
    {
        assert(valid);
        return object;
    }

    const T& get() const
    {
        assert(valid);
        return *object;
    }

private:
    T object;
    mutable bool valid;
};

template <typename T>
inline Mover<T> Movable(T&& object)
{
    return Mover<T>(std::move(object));
}

and then create a proxy-mock:

class MockMyInterface : public MyInterface
{
public:
    MOCK_METHOD0(GetUniqueTest_, Mover<TTest>());
    TTest GetUniqueTest()
    {
        return std::move(GetUniqueTest_().get());
    }
}
Playroom answered 28/2, 2017 at 10:24 Comment(4)
It comes to me that google test 1.8 seems to support mocking such functions: github.com/google/googletest/blob/master/googlemock/docs/… It it may be a good idea to update to googletest v1.8.Shopper
@Shopper Yes, I've also found this later, but didn't have time to verify :) Perhaps when you confirm you can also self-answer ;)Playroom
well I did not verify it yet, but I trust the document. And anyway my question specifies the gtest version is 1.7, which does not have this support yet.Shopper
Yup, I verified that release 1.8.0 does support move-only types.Shopper
S
16

Answer my own question here just to provide updated information.

With googletest release 1.8.0 or above, it introduces ByMove(...) and supports return move-only types natively.

So the code compiles OK:

class MockMyInterface: public MyInterface{
    public:
        MOCK_METHOD0(GetUniqueTest, TTest());
}

But in run time it throws exception because gmock does not know how to return default TTest:

C++ exception with description "Uninteresting mock function call - returning default value.
    Function call: GetUniqueTest()
    The mock function has no default action set, and its return type has no default value set." thrown in the test body.

This can be easily workaround by setting a default action in the mock class:

ON_CALL(*this, GetUniqueTest()).WillByDefault(Return(ByMove(TTest(0))));

Note: For std::unique_ptr<T> it's OK because it has default constructor, a nullptr unique_ptr is returned by default.

So putting all together, if using googletest 1.8.0 or above, we can do:

// My interface to mock
class MyInterface
{
    public:
        virtual ~MyInterface() {}
        virtual TTest GetUniqueTest() = 0;
        virtual std::unique_ptr<int> GetUniqueInt() = 0;
};

// The mock
class MockMyInterface: public MyInterface{
    public:
        MOCK_METHOD0(GetUniqueTest, TTest());
        MOCK_METHOD0(GetUniqueInt, std::unique_ptr<int>());
        MockMyInterface() {
            ON_CALL(*this, GetUniqueTest())
                .WillByDefault(Return(ByMove(TTest(0))));
        }
};

Reference: [Mocking Methods That Use Move-Only Types] (https://github.com/google/googletest/blob/master/googlemock/docs/cook_book.md#mocking-methods-that-use-move-only-types)

Shopper answered 1/3, 2017 at 9:40 Comment(1)
What if we expect to return TTest(1)? .WillByDefault(Return(ByMove(TTest(1)))); will return 1 then 0s right?Erbes
P
5

As mentioned in the comments by Mine, Google Test 1.8 seems to support mocking such functions (documentation).

As for 1.7 I've found a solution here.

First, create an utility class to wrap non-copyable objects:

template <typename T>
class Mover
{
public:
    Mover(T&& object)
      : object(std::move(object)),
        valid(true)
    {
    }

    Mover(const Mover<T>& other)
      : object(const_cast<T&&>(other.object)),
        valid(true)
    {
        assert(other.valid);
        other.valid = false;
    }

    Mover& operator=(const Mover& other)
    {
        assert(other.valid);
        object = const_cast<T&&>(other.object);
        other.valid = false;
        valid = true;
    }

    T& get()
    {
        assert(valid);
        return object;
    }

    const T& get() const
    {
        assert(valid);
        return *object;
    }

private:
    T object;
    mutable bool valid;
};

template <typename T>
inline Mover<T> Movable(T&& object)
{
    return Mover<T>(std::move(object));
}

and then create a proxy-mock:

class MockMyInterface : public MyInterface
{
public:
    MOCK_METHOD0(GetUniqueTest_, Mover<TTest>());
    TTest GetUniqueTest()
    {
        return std::move(GetUniqueTest_().get());
    }
}
Playroom answered 28/2, 2017 at 10:24 Comment(4)
It comes to me that google test 1.8 seems to support mocking such functions: github.com/google/googletest/blob/master/googlemock/docs/… It it may be a good idea to update to googletest v1.8.Shopper
@Shopper Yes, I've also found this later, but didn't have time to verify :) Perhaps when you confirm you can also self-answer ;)Playroom
well I did not verify it yet, but I trust the document. And anyway my question specifies the gtest version is 1.7, which does not have this support yet.Shopper
Yup, I verified that release 1.8.0 does support move-only types.Shopper

© 2022 - 2024 — McMap. All rights reserved.