我听说可以将 google-test TestCase 类的朋友启用到我的类中,从而使测试能够访问我的私有/受保护的成员。
如何实现?
试试这个(直接来自 Google 测试文档...):
FRIEND_TEST(TestCaseName, TestName);
例如:
// foo.h
#include <gtest/gtest_prod.h>
// Defines FRIEND_TEST.
class Foo {
...
private:
FRIEND_TEST(FooTest, BarReturnsZeroOnNull);
int Bar(void* x);
};
// foo_test.cc
...
TEST(FooTest, BarReturnsZeroOnNull) {
Foo foo;
EXPECT_EQ(0, foo.Bar(NULL));
// Uses Foo's private member Bar().
}
我知道这已经很旧了,但我今天正在寻找相同的答案。 “gtest_prod.h”只是引入了一个简单的宏来引用测试类。
#define FRIEND_TEST(test_case_name, test_name)\
friend class test_case_name##_##test_name##_Test
所以
FRIEND_TEST(FooTest, BarReturnsZeroOnNull);
相当于:
friend class FooTest_BarReturnsZeroOnNull_Test;
这是有效的,因为每个测试都是它自己的类,如前面的答案中提到的。
更好的策略是在单元测试中不允许进行朋友测试。
允许好友测试访问私有成员将导致代码库难以维护。每当重构组件的内部实现细节时就会中断的测试不是您想要的。如果付出额外的努力来获得可以通过其公共接口测试组件的设计,那么您将获得只需要在组件的公共接口更新时就更新的测试。
依赖于
gtest/gtest_prod.h
的测试应该被视为设计不佳的标志。
当您的测试类和您的测试类位于不同的命名空间中时(例如您的测试位于全局命名空间中),您可能需要前向声明您的测试类并在
FRIEND_TEST
中添加您的命名空间前缀:
// foo.h
#include <gtest/gtest_prod.h>
// forward-declaration of test class
class FooTest_BarReturnsZeroOnNull_Test;
// Defines FRIEND_TEST.
class my_namespace::Foo {
...
private:
// Specify the global namespace (`::`) in the `FRIEND_TEST()` usage
FRIEND_TEST(::FooTest, BarReturnsZeroOnNull);
int Bar(void* x);
};
// forward-declaration of this namespace from foo_test.cc
using namespace my_namespace;
...
TEST(FooTest, BarReturnsZeroOnNull) {
Foo foo;
EXPECT_EQ(0, foo.Bar(NULL));
// Uses Foo's private member Bar().
}
我知道朋友单元测试(或一般 C++ 中的友好性)和白盒测试是一个有争议的主题,但是当您研究复杂的科学算法时,您需要测试和验证其中的每一步,但是您不想暴露在公共(甚至受保护)接口中,朋友测试在我看来是一个简单而实用的解决方案,特别是在测试驱动的开发方法中。如果使用友好性或白盒测试违背了自己的宗教信仰,那么以后总是可以重构代码(或完全删除白盒测试)。
如果您不想使用
FRIEND_TEST
单独列出每个测试用例,另一种选择是定义一个测试夹具类,然后改为友元:
class Foo
{
...
private:
friend class FooTest;
};
...
class FooTest : public ::testing::Test
{
protected:
int AccessPrivateFooThing(Foo& foo) { ... }
...
};
...
TEST_F(FooTest, PrivateThing)
{
...
int result = AccessPrivateFooThing(foo);
...
}
然而,需要注意的是,只有在测试装置本身定义的方法才能访问
Foo
的私有成员;测试本身不会。您必须在需要访问的固定装置中定义访问器、保镖或其他辅助方法。与其他一些解决方案相比,这需要更多的样板,但也更明确,这有望阻止过多地触及内部。
如果您遇到命名空间问题,只需确保测试装置与相关类位于同一命名空间中即可;无论如何,这是一个很好的做法。
仍然强烈鼓励在合理可行的情况下仅测试公共成员,尽管我确实同意有时这样做更有意义。