Google Mock:可以使用全局模拟对象吗?
Google Mock: Is it ok to use global mock objects?
在所有关于 gmock 的文档中,我总能找到要在测试中实例化的模拟对象,就像这样:
TEST(Bim, Bam)
{
MyMockClass myMockObj;
EXPECT_CALL(MyMockObj, foo(_));
...
}
因此,每个测试都会创建和销毁对象。我相信根据 test fixture 创建和销毁对象也完全没问题。但我想知道是否也可以拥有模拟对象的文件全局实例,就像这样:
MyMockClass myMockObj;
TEST(Bim, Bam)
{
EXPECT_CALL(MyMockObj, foo(_))
...
}
我试过了,到目前为止我完全没有问题,一切似乎都很好。但也许我应该知道什么?只是因为我偶然发现 this question,其中唯一的答案是:
... the problem is that you're instantiating a global instance of FooMock. Googlemock/googletest expect the mock to be defined either within the body of the test, or within a test fixture class.
但我在文档或其他任何地方都找不到任何可以证实这一点的内容(我忽略了吗?)。
谢谢,乔治
PS:我需要使用全局模拟实例的原因将是另一个讨论的主题(参见我的this posting)。
可以,但这不是一个好主意。
这样做违反了UT
的隔离原则。
此违规行为可能会导致您的测试出现意外 failure/pass。
Gtest
使用假对象的析构函数来验证预期是否发生,这就是预期每个假对象将在测试主体或测试中创建和释放的原因灯具 class。
如果您将假对象设置为全局对象,那么它不会在每个 UT
结束时释放,那么验证将不会执行,即使它应该失败,测试也会通过。当您一起执行所有测试时,您的一些 UT
可能会 fass/fail;在一个测试中,您希望方法 x
不会调用,而在另一个测试中,您希望该方法会调用;在一个 UT 中,您期望方法 x 将调用 3 次,但该方法在测试中调用了两次 + 在其他测试中调用了一次(测试应该失败但不会...)
所以最重要的是你永远不应该使用全局模拟,除非这个全局模拟只是为了防止空指针(你没有设置行为..)
只是在追查与我的模拟对象相关的错误时偶然发现了这个问题。在我的例子中,问题是模拟对象的构造函数在 InitGoogleMock
之前被调用,这似乎把事情搞砸了。
注意:我正在使用 Google 模拟 CppUnitTestFramework。
失败:
MockObject mock;
TEST_MODULE_INITIALIZE(ModuleInitialize)
{
InitGoogleMock(argc, argv);
}
获胜:
MockObject *mock = nullptr;
TEST_MODULE_INITIALIZE(ModuleInitialize)
{
InitGoogleMock(argc, argv);
mock = new MockObject;
}
TEST_MODULE_CLEANUP(ModuleCleanup)
{
delete mock;
}
并不是说这是最佳实践或其他任何东西,但如果您需要全局模拟对象,我会说要注意何时调用构造函数。
除了接受的答案之外,如果您使用的是 GTest,则全局变量在执行测试用例后未被销毁时也会被标记为泄漏。
泄漏背后的想法在这个参考中:https://google.github.io/googletest/gmock_cook_book.html#forcing-a-verification
如果您不想手动验证,最接近的解决方案是将模拟对象作为您的夹具的成员 class。如果由于某种原因你需要动态分配模拟,你可以有一个指针和 create/destroy SetUp 和 TearDown 上的实例(与@Chris Olsen 的回答相同的概念)。或者,如果您使用的是 C++11,则可以使用 shared_ptr:
class Fixture : public ::testing::Test
{
std::shared_ptr<ObjT> mPtr;
...
void SetUp()
{
mPtr = std::make_shared<ObjT>();
}
...
}
在所有关于 gmock 的文档中,我总能找到要在测试中实例化的模拟对象,就像这样:
TEST(Bim, Bam)
{
MyMockClass myMockObj;
EXPECT_CALL(MyMockObj, foo(_));
...
}
因此,每个测试都会创建和销毁对象。我相信根据 test fixture 创建和销毁对象也完全没问题。但我想知道是否也可以拥有模拟对象的文件全局实例,就像这样:
MyMockClass myMockObj;
TEST(Bim, Bam)
{
EXPECT_CALL(MyMockObj, foo(_))
...
}
我试过了,到目前为止我完全没有问题,一切似乎都很好。但也许我应该知道什么?只是因为我偶然发现 this question,其中唯一的答案是:
... the problem is that you're instantiating a global instance of FooMock. Googlemock/googletest expect the mock to be defined either within the body of the test, or within a test fixture class.
但我在文档或其他任何地方都找不到任何可以证实这一点的内容(我忽略了吗?)。
谢谢,乔治
PS:我需要使用全局模拟实例的原因将是另一个讨论的主题(参见我的this posting)。
可以,但这不是一个好主意。
这样做违反了UT
的隔离原则。
此违规行为可能会导致您的测试出现意外 failure/pass。
Gtest
使用假对象的析构函数来验证预期是否发生,这就是预期每个假对象将在测试主体或测试中创建和释放的原因灯具 class。
如果您将假对象设置为全局对象,那么它不会在每个 UT
结束时释放,那么验证将不会执行,即使它应该失败,测试也会通过。当您一起执行所有测试时,您的一些 UT
可能会 fass/fail;在一个测试中,您希望方法 x
不会调用,而在另一个测试中,您希望该方法会调用;在一个 UT 中,您期望方法 x 将调用 3 次,但该方法在测试中调用了两次 + 在其他测试中调用了一次(测试应该失败但不会...)
所以最重要的是你永远不应该使用全局模拟,除非这个全局模拟只是为了防止空指针(你没有设置行为..)
只是在追查与我的模拟对象相关的错误时偶然发现了这个问题。在我的例子中,问题是模拟对象的构造函数在 InitGoogleMock
之前被调用,这似乎把事情搞砸了。
注意:我正在使用 Google 模拟 CppUnitTestFramework。
失败:
MockObject mock;
TEST_MODULE_INITIALIZE(ModuleInitialize)
{
InitGoogleMock(argc, argv);
}
获胜:
MockObject *mock = nullptr;
TEST_MODULE_INITIALIZE(ModuleInitialize)
{
InitGoogleMock(argc, argv);
mock = new MockObject;
}
TEST_MODULE_CLEANUP(ModuleCleanup)
{
delete mock;
}
并不是说这是最佳实践或其他任何东西,但如果您需要全局模拟对象,我会说要注意何时调用构造函数。
除了接受的答案之外,如果您使用的是 GTest,则全局变量在执行测试用例后未被销毁时也会被标记为泄漏。 泄漏背后的想法在这个参考中:https://google.github.io/googletest/gmock_cook_book.html#forcing-a-verification
如果您不想手动验证,最接近的解决方案是将模拟对象作为您的夹具的成员 class。如果由于某种原因你需要动态分配模拟,你可以有一个指针和 create/destroy SetUp 和 TearDown 上的实例(与@Chris Olsen 的回答相同的概念)。或者,如果您使用的是 C++11,则可以使用 shared_ptr:
class Fixture : public ::testing::Test
{
std::shared_ptr<ObjT> mPtr;
...
void SetUp()
{
mPtr = std::make_shared<ObjT>();
}
...
}