通过 setargreferee 传递矢量时,gtest 中未实现 EXPECT_CALL
Unfulfilled EXPECT_CALL in gtest when passing vector by setargreferee
#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 对象上设置 return 值 InfoState::ON。
然后我将我的列表放入 SetArgReferee 的测试方法中。当涉及到 info.getState() 的执行时,它 returns InfoState::OFF 并且 gtest 说“无趣的模拟函数调用 - returning 默认值。”。
为什么会出现此问题以及如何测试它?
如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;
然而这里的主要问题实际上是拷贝构造函数的实际需要,这是由两个因素造成的:
- 正如我在 GMock 设计删除复制构造之前所说的那样,即使您将创建自己的实现 - 所有
EXPECT_CALL
和其他 GMock 基本功能也不会被复制!这正是您遇到的问题。所以您已经创建了一个自定义复制构造函数,所有 GMock 功能都消失了。尽管这个构造函数是空的 - 它无论如何都不会开箱即用。
MyInfo(const MyInfo&){};
- 第二个问题是您正在使用的
std::vector
要求您(在此 use-case)类型满足 CopyConstructible 概念。因此这将不起作用。
让我担心的是 EXPECT_CALL
正在使用复制而不是移动语义或传递引用。尽管您明确设置该参数应为引用(而不是副本!)SetArgReferee<0>(myTestInfoList)
,但仍会执行此操作。此外,GMock
对象是不可复制的。这对我来说看起来像是设计缺陷或错误,但我不是 Google Test
专家。我将对此进行更多研究,并可能会向 GTest
实施者提出错误 report/question。
好的,但要找到解决方案,我们需要首先在 GMock API
中找到一个不使用副本的方法,然后使用 std::vector
不调用副本构造函数的功能。
第一个问题将通过将 EXPECT_CALL
更改为 ON_CALL
并打开调用 std::vector
功能的可能性来解决,我们还将使用 Invoke
来自 [=15] =] 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
强制复制。
#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 对象上设置 return 值 InfoState::ON。 然后我将我的列表放入 SetArgReferee 的测试方法中。当涉及到 info.getState() 的执行时,它 returns InfoState::OFF 并且 gtest 说“无趣的模拟函数调用 - returning 默认值。”。 为什么会出现此问题以及如何测试它?
如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;
然而这里的主要问题实际上是拷贝构造函数的实际需要,这是由两个因素造成的:
- 正如我在 GMock 设计删除复制构造之前所说的那样,即使您将创建自己的实现 - 所有
EXPECT_CALL
和其他 GMock 基本功能也不会被复制!这正是您遇到的问题。所以您已经创建了一个自定义复制构造函数,所有 GMock 功能都消失了。尽管这个构造函数是空的 - 它无论如何都不会开箱即用。
MyInfo(const MyInfo&){};
- 第二个问题是您正在使用的
std::vector
要求您(在此 use-case)类型满足 CopyConstructible 概念。因此这将不起作用。
让我担心的是 EXPECT_CALL
正在使用复制而不是移动语义或传递引用。尽管您明确设置该参数应为引用(而不是副本!)SetArgReferee<0>(myTestInfoList)
,但仍会执行此操作。此外,GMock
对象是不可复制的。这对我来说看起来像是设计缺陷或错误,但我不是 Google Test
专家。我将对此进行更多研究,并可能会向 GTest
实施者提出错误 report/question。
好的,但要找到解决方案,我们需要首先在 GMock API
中找到一个不使用副本的方法,然后使用 std::vector
不调用副本构造函数的功能。
第一个问题将通过将 EXPECT_CALL
更改为 ON_CALL
并打开调用 std::vector
功能的可能性来解决,我们还将使用 Invoke
来自 [=15] =] 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
强制复制。