使用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
相关推荐
在gtest中,测试类成员函数的方法如下: 1. **创建测试类(Test Fixture)**:测试类是gtest用来组织测试用例的一个基类,它可以包含一些初始化和清理工作。通常,我们将需要测试的类作为测试类的成员变量,以便在...
这使得测试类可以直接访问私有成员,而无需在主类中暴露过多接口。 ```cpp #include "gtest/gtest.h" class MyClass { private: void privateFunc() { /*...*/ } FRIEND_TEST(MyClassTest, PrivateFunc); //...
标题中的“gtest_test1111111111111111.zip”表明这是一个使用Google Test(gtest)框架编写的测试项目,并且可能包含一系列的测试用例。Google Test是Google开发的一个开源的C++测试库,它为C++程序员提供了一种...
4. **测试代码(Test Code)**:如单元测试,用以验证各个模块的功能是否正常,通常使用GTest等测试框架。 5. **文档(Documentation)**:可能包含README文件,介绍系统的使用方法、安装步骤、API文档等,帮助用户...
可以使用GTest等框架进行测试。 10. **版本控制**:使用Git等版本控制系统可以帮助管理代码的不同版本,协同开发,以及回滚到以前的稳定状态。 通过这个项目,学生不仅可以深化对C++语言的理解,还能学习到数据库...
可以使用GTest等框架进行测试,确保所有基本操作的结果符合预期。 10. **代码可读性和可维护性**: - 遵循良好的编程规范,如添加注释解释代码功能,使用有意义的变量名,保持函数短小且单一职责,都有助于提高...
7. **Mock protected、private 方法**:Google Mock 提供了工具可以模拟类的保护(protected)和私有(private)成员函数,这在测试需要内部行为时非常有用。 Google Mock 的使用通常包含以下步骤: - 引入必要的库...
可以使用gtest库编写测试用例,确保代码的正确性。同时,良好的调试技巧也是确保代码质量的关键。 通过以上知识点的综合运用,我们可以构建出一个功能完善的C++面向对象的通讯录系统,既能满足基本的联系人管理需求...
6. **调试和测试**:项目可能包含单元测试,使用像GTest这样的库来验证代码功能。同时,调试信息(如gdb的配置)也可能包含在内,便于开发者进行问题定位。 7. **标准库的使用**:C++标准库提供了丰富的功能,如...
- **封装**:通过类的私有成员变量和公共接口来隐藏内部实现细节,保护数据安全。 - **继承**:可能会使用继承来创建通用类(如`资源`)的子类,以处理不同类型资源的特定需求(如`计算机`、`打印机`等)。 - **...
9. **测试与调试**:开发过程中,单元测试(如使用`gtest`库)和调试(如使用GDB)是非常重要的环节,以确保系统功能的正确性和稳定性。 这个C++实现的ATM系统虽然文件较小,但它涵盖了众多的编程和系统设计概念,...
使用GTest等测试框架可以帮助我们自动化测试过程,确保代码质量。 总结来说,这个C++职工管理系统课程设计涵盖了C++的基本语法、面向对象编程、文件操作、异常处理等多个核心知识点。通过实际项目实践,不仅能提升...
这个类可能包含表示立方体边长的私有成员变量,以及公共方法来访问和修改这些属性,比如`getEdgeLength()`和`setEdgeLength()`。 2. **几何运算**:立方体的计算可能包括面积、体积的计算,以及与其它几何体的交互...
2. **成员函数**:类中可能会有公共方法(如`calculateAverage()`)来计算平均值,以及可能的私有方法(如`validateInput()`)来验证输入数据的有效性。 3. **模板和泛型编程**:如果这个模块设计成可以处理不同...
在Boat项目中,可能会有一个名为`Boat`的类,包含私有成员变量(如船的长度、宽度、位置等)和公有成员函数(如`startEngine()`、`stopEngine()`、`navigate()`)。为了实现船只的行为,可以使用继承和多态性。例如...
`Game`类可能包含私有(private)成员变量,如`std::string title`(游戏标题),`std::string developer`(开发者),`double price`(价格),以及公共(public)成员函数,如`void purchase()`(购买游戏)和`...