如何使用删除的copy-ctor模拟方法返回对象?

时间:2017-02-28 09:39:54

标签: c++ unit-testing googletest gmock

如果一个接口有一个函数来创建一个删除了copy-ctor的对象,那么如何模拟这个函数呢? Gmock似乎在内部使用了对象的复制构造函数。

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());
}

编译错误说:

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));

如果方法返回std::unique_ptr<T>,则错误是相同的,因为std::unique_ptr<T>也删除了copy-ctor。

所以我的问题是:如何模拟返回删除了copy-ctors的对象的方法?

我使用googletest v1.7 ,GCC 5.3.0和Ubuntu 14.04.1。

2 个答案:

答案 0 :(得分:6)

在这里回答我自己的问题只是为了提供更新的信息。

使用googletest 版本1.8.0 或更高版本,它会引入ByMove(...)并支持本机返回仅限移动类型。

所以代码编译好了:

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

但是在运行时它抛出异常,因为gmock不知道如何返回默认值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.

通过在mock类中设置默认操作,可以轻松解决此问题:

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

注意:对于std::unique_ptr<T>,可以,因为它有默认构造函数,默认情况下会返回nullptr unique_ptr

总而言之,如果使用googletest 1.8.0 或更高版本,我们可以:

// 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))));
        }
};

参考:Mocking Methods That Use Move-Only Types

答案 1 :(得分:4)

正如Mine的评论中所提到的,Google Test 1.8似乎支持模拟此类函数(documentation)。

至于1.7我找到了解决方案here

首先,创建一个实用程序类来包装不可复制的对象:

  <asp:TextBox ID="txtarea" runat="server" Width="700px" Height="80px" TextMode="MultiLine" onkeyup="cnt()" MaxLength="1000"></asp:TextBox>
  <p style="text-align: right;">
    <asp:Label ID="lblcharcnt" runat="server" Text="1000"></asp:Label>

  </p>
  <asp:Label ID="lblcount" runat="server" Text='<%#Eval("row") %>' Visible="false"></asp:Label>


  </ItemTemplate>
</asp:Repeater>

<script type="text/javascript">
  function cnt() {
    var a = document.getElementById("txtarea").value;
    document.getElementById("lblcharcnt").innerHTML = 15 - a.length;
  }
</script>

然后创建一个proxy-mock:

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));
}