微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

通过setargreferee传递矢量时,gtest中的EXPECT_CALL未实现

如何解决通过setargreferee传递矢量时,gtest中的EXPECT_CALL未实现

#include <gtest/gtest.h>
#include <gmock/gmock.h>

enum class InfoState : uint8_t
{
    OFF     = 0,ON      = 1,};

class MyInfo
{
public:
    MyInfo(){};
    MyInfo(const MyInfo&){};
    MyInfo& operator=(const MyInfo&){}
    MOCK_METHOD0(getState,InfoState(void));
};

class ServiceClient
{
public:
    MOCK_METHOD1(getInfo,bool(std::vector<MyInfo> &myInfoList));
};

class MyClassA
{
public:
    ServiceClient m_serviceClient;

    void updateStatus()
    {
        std::vector<MyInfo> myInfoList;
        if (m_serviceClient.getInfo(myInfoList))
        {
            for (auto& info: myInfoList)
            {
                if (InfoState::ON == info.getState())
                {
                    //notifyObservers(true);
                    break;
                }
            }
        }
    }
};

TEST(infoTest,test1)
{
    MyClassA testObj;

    std::vector<MyInfo> myTestInfoList(1);

    EXPECT_CALL(myTestInfoList[0],getState()).WillOnce(::testing::Return(InfoState::ON));

    EXPECT_CALL(testObj.m_serviceClient,getInfo(::testing::_))
         .WillOnce(::testing::DoAll(::testing::SetArgReferee<0(myTestInfoList),::testing::Return(true)));

    testObj.updateStatus();
}

我想测试MyClassA :: updateStatus方法在这种情况下,我想在myTestInfoList EXPECT_CALL内的MyInfo对象上设置返回值InfoState :: ON。 然后,我将列表放到SetArgReferee进行测试的方法中。在执行info.getState()时,它返回InfoState :: OFF,并且gtest说“无趣的模拟函数调用-返回认值。”。 为什么会出现此问题以及如何进行测试?

解决方法

正如Quarra所言,主要问题是复制构造函数。我不是Google Test专家,但我找到了解决您问题的方法。

根据GMock,我发现模拟对象无法复制-这是Google Test实现者​​的设计原则和决定。 Here this decision has been justified早在2009年。

因此,通过在模拟对象内部未定义复制构造函数,它将被删除(live code)。这是错误代码

/opt/compiler-explorer/libs/googletest/release-1.10.0/googlemock/include/gmock/gmock-spec-builders.h:1483:3: note: 'FunctionMocker' has been explicitly marked deleted here

  FunctionMocker(const FunctionMocker&) = delete;

这里的主要问题实际上是复制构造函数的实际需求,这是由两个因素引起的:

  1. 正如我在GMock之前所述,设计会删除复制结构,即使您创建自己的实现,也不会复制所有EXPECT_CALL和其他GMock基本功能!这正是您遇到的问题。因此,您已经创建了一个自定义副本构造函数,所有GMock功能都消失了。尽管该构造函数为空,但无论如何都无法使用。
MyInfo(const MyInfo&){};
  1. 第二个问题是std::vector,您正在使用自己的需求(在此用例中),该类型满足CopyConstructible概念。因此,这将不起作用。

让我担心的是,EXPECT_CALL正在使用复制而不是移动语义或传递引用。尽管您已明确设置该参数应为引用(而不是副本!)SetArgReferee<0>(myTestInfoList),但仍可以这样做。此外,根据设计,GMock对象是不可复制的。对我来说,这看起来像是设计缺陷或错误,但我不是Google Test专家。我将对此进行更多研究,并可能向GTest实现者​​提出错误报告/问题。

好吧,但是要找到解决方案,我们需要首先在GMock API中找到一个不使用副本的方法,然后再使用std::vector功能,即不调用副本构造函数的方法。

第一个问题将通过将EXPECT_CALL更改为ON_CALL并打开调用std::vector功能的可能性来解决,我们还将使用Invoke中的GMock API。 (Live code

TEST(infoTest,test1)
{
    MyClassA testObj;

    std::vector<MyInfo> myTestInfoList(1);

    ON_CALL(myTestInfoList[0],getState()).WillByDefault(::testing::Invoke(
            []()
            {
                return InfoState::ON;
            }));

    ON_CALL(testObj.m_serviceClient,getInfo(::testing::_))
         .WillByDefault(::testing::Invoke(
            [](std::vector<MyInfo> &myInfoList)
            {
                return true;
            }));

    testObj.updateStatus();
}

这适用于明确删除的copy constructor-> MyInfo(const MyInfo&) = delete;,但逻辑也已删除。

要克服第二个问题,即STL副本,我们可以简单地使用std::swap并填充传递的std::vector&。交换值不会复制数据-因此我们很高兴。

    ON_CALL(testObj.m_serviceClient,getInfo(::testing::_))
         .WillByDefault(::testing::Invoke(
            [&myTestInfoList](std::vector<MyInfo> &myInfoList)
            {
                std::swap(myInfoList,myTestInfoList);
                return true;
            }));

这是工作solution

如前所述,我将尝试调查GMock EXPECT_CALL为何强制复制。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。