用gtest测试类的私有成员

用gtest测试类的私有成员

浏览次数:109次 2010年07月04日 Dbger的技术博客 字号:

使用gtest、或者cppunit之类的框架编写单元测试代码,一个最常见的问题是对类私有成员的测试与验证。理想情况下,我们希望在测试中,类中所有的数据与方法都是可以访问的;而在产品代码中,只暴露实现定义好的接口。

gtest官方文档中,也提到了对私有成员的处理,方法不外乎两种:一是使用friend关键字,骗取信任得以通行;二是重构采用Pimpl模式,公共类中只暴露接口,而实现类中暴露所有细节(public),测试时包含实现类即可。

但这两个方法都试了一下,觉不太方便。

  • 使用friend关键字
    gtest提供了一个FRIEND_TEST的宏,用来将一个test声明为产品类的友元。其缺点是显而易见的。一是需要往产品类中添加纯测试代码;二是每加一个test,需要在产品类中添加一项FRIEND_TEST。

  • 重构采用Pimpl模式
    我认可Pimpl模式,但同时我也不会对所有的类都这么做。所以如果让我只是为了支持测试而做这样的重构,我可能并不情愿。况且,手工重构很麻烦,而我手头也没有这么个自动化的工具。(当然,我相信这是可以自动化的)

其实我们想要达到的目的无非是:在产品代码中,该pubic的是public,该private的还是private;而在测试代码中,全部都是public。于是:

1#ifdef GTEST
2#define private public
3#define protected public
4#endif

将其放在每个类的声明前,或者放在一个单独的头文件如ForcePublic.h中并包含之。这样,在编译测试代码时,加上GTEST的预编译宏,就可以非常方便的使用被测试类中的任何成员了,并不会对产品代码产生任何的影响。

如果你是以纯源代码的方式使用你的产品类的,这个方法没有任何问题;但如果你是通过静态库,或者动态库的方式使用你的产品类的,在测试代码中若直接使用这些库,编译是没有问题,因为全伪装成了public,但在链接的时候,因为private的成员是没有从库中导出来的,必然会出现链接错误。此时有两个方案:一是以GTEST的方式重新编译库;二是直接将产品源代码编译进你的测试工程中去。

我使用的是动态库, 选择了将代码编译进测试工程的方法,为了解决dllexport, dllimport相关的一些编译问题,还做了如下定义:

1#ifdef TXNMGR_EXPORTS
2#define TXNMGR_API __declspec(dllexport)
3#else
4#ifdef GTEST
5#define TXNMGR_API
6#else
7#define TXNMGR_API __declspec(dllimport)
8#endif
9#endif

这样,在测试工程中,TXNMGR_API宏的定义为空,自然被忽略了,而不会影响到产品代码。

感觉着这种方法的好处在于只要在一开始做一些小小的修改,便可以一劳永逸的解决访问所有产品类所有私有成员的问题;由于我们改变的只是成员的访问级别,对类的行为应该没有什么影响。

更新:

在gtest的google group中就这个问题提出了讨论,大家指出这种方式的问题在于:

  • 使得访问私有成员过分容易,从而导致写出来的test访问私有成员的可能性增大。测试访问了私有成员,也就是依赖于实现,这让单元测试成为你重构的负担,而不是保证。
  • 万战勇同学也指出,这种方法是不标准的C++用法: 一是C++标准不允许重定义关键词,所以这种方法即使此时在你当前的编译器上是可行的,你也不能保证将来,或者在其他编译器上可行;二是public, protected private等访问修饰符可能会影响对象成员的布局,这样当你的测试是直接链接到产品代码时会有些问题。

所以,除非你对以上两点十分清楚并且可以接受,不然,还是使用官方的FRIEND_TEST要更好一些。

  • 因为每一个需要访问私有成员的test都需要在产品代码上加上一项 FRIEND_TEST,这会让你思考:我真的需要访问私有成员吗?有没有不用访问私有成员的方法? 从而帮助形成更好的设计与测试
  • 因为每一个依赖于实现的test都显式的登记在案,这样当你的实现细节有所改变时,你知道会影响哪些test

来自:http://www.cnblogs.com/baiyanhuang/archive/2010/06/28/1766820.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一个使用gtest测试框架实现快速排序的示例代码: ```c++ #include <gtest/gtest.h> void quickSort(int arr[], int left, int right) { int i = left, j = right; int tmp; int pivot = arr[(left + right) / 2]; // partition while (i <= j) { while (arr[i] < pivot) i++; while (arr[j] > pivot) j--; if (i <= j) { tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; i++; j--; } }; // recursion if (left < j) quickSort(arr, left, j); if (i < right) quickSort(arr, i, right); } TEST(QuickSortTest, PositiveNos) { int arr[] = { 5, 2, 3, 1, 4 }; int n = sizeof(arr) / sizeof(arr[0]); quickSort(arr, 0, n - 1); ASSERT_EQ(arr[0], 1); ASSERT_EQ(arr[1], 2); ASSERT_EQ(arr[2], 3); ASSERT_EQ(arr[3], 4); ASSERT_EQ(arr[4], 5); } TEST(QuickSortTest, NegativeNos) { int arr[] = { -5, -2, -3, -1, -4 }; int n = sizeof(arr) / sizeof(arr[0]); quickSort(arr, 0, n - 1); ASSERT_EQ(arr[0], -5); ASSERT_EQ(arr[1], -4); ASSERT_EQ(arr[2], -3); ASSERT_EQ(arr[3], -2); ASSERT_EQ(arr[4], -1); } TEST(QuickSortTest, MixedNos) { int arr[] = { -5, 2, -3, 1, 0 }; int n = sizeof(arr) / sizeof(arr[0]); quickSort(arr, 0, n - 1); ASSERT_EQ(arr[0], -5); ASSERT_EQ(arr[1], -3); ASSERT_EQ(arr[2], 0); ASSERT_EQ(arr[3], 1); ASSERT_EQ(arr[4], 2); } int main(int argc, char **argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } ``` 在这个示例代码中,我们使用了gtest测试框架来测试快速排序函数quickSort。我们定义了三个测试用例,分别测试了正数、负数和混合数的情况。在每个测试用例中,我们使用ASSERT_EQ宏来断言排序后数组的每个元素是否与预期值相等。如果不相等,则测试失败。 注意,这里的示例代码仅供参考,实际实现中可能需要根据具体情况进行相应修改。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值