gtest深入学习笔记二
背景
继续深入学习gtest,记录一下学习笔记
基于异常的测试
一般来说,工程师应该知道代码什么时候出现哪些错误,也应当知道什么时候抛出异常,什么时候需要用try-catch块来保护程序。唯有对代码执行路径了如指掌,方能知道该在什么时候处理异常。
(1)ASSERT_ANY_THROW宏、 ASSERT_THROW宏捕获异常
直接用try-catch捕获异常
如果宏内的表达式不抛出异常,那么测试失败
失败案例如下:
源码代码片段如下
单元测试代码段如下
运行测试程序后,得到如下结果
为了得到通过单元测试的结果,可以修改上面的源码代码段如下
单元测试代码不用改,再次运行测试程序,结果如下
(2)如果必须要验证异常抛出后的条件,那么可以使用try-catch块
例如,想要验证异常对象的文本信息
源码片段如下
单元测试代码如下
测试结果
探查私有成员
Tell-Don’t-Ask设计理念,你应该告诉一个对象去做一些工作,然后让它自主完成任务。如果在这期间频繁地过问对象,那么就违反了Tell-Don’t-Ask原则。
如果一个系统包含大量的对象查询,它是纠缠不清且极度复杂的。
两个问题
(1)可以对私有成员变量编写测试吗?
(2)可以对私有成员函数编写测试吗?
回答:
从技术层面来说,为那些非公用接口的成员提供访问通道是可以接受的。可以将测试声明为待测试类的友元,但最好不要这么做!!也可以将数据暴露为公有的,但如果仅仅只是为了测试的话,这也是不太可以接受的。一般来说,如果要对私有成员进行测试,这有可能是代码设计坏味。无论什么时候,如果暴露数据仅仅是为了测试,那么就需要考虑用验证行为的方式来代替。
探查私有行为
做测试驱动开发时,所有东西都来自于类的公共接口设计。为了添加详细的行为,做测试驱动开发这个行为时,就好像你的测试就是此类的产品客户。随着细节越来越多,代码越来越复杂,自然的就需要考虑到重构来优化代码。
大多数时候,当你觉得需要测试私有行为时,可以尝试将代码重构,将代码移动到另一个类,或者为之创建一个新类。
(1)问题:如果这样编写程序的话,最后会不会得到数以千计的特定功能的类?
答:当然会多出来一些类,但不会到数以千计这么夸张。每个类都会非常小,这样也易于理解/测试/维护,编译也很快。
(2)问题:我不倡议创建更多的类怎么办?
答:这时就可以真正的利用OO(Object-Oriented,面向对象)了。当你创建更多单一目标的类,并且每个类包含一些单一责任的方法时,你会发现更多的重用契机。重用一个庞杂的类是不可能的。相反,遵循单一责任原则的类会让你看到减少代码量的可能。
(3)问题:如果面对的是遗留代码呢?
假如要先从一个有几十个成员函数的类下手,其中许多还是私有成员。这跟私有行为类似,可以先简单的弱化访问关系,并为一些私有函数添加相应测试。(也就是重构部分代码,再次提醒,不用担心类的泛滥,这不会发生。)将函数移至更好的场所。
测试和测试驱动
(1)测试驱动
测试驱动开发中虽然有测试两个字,但是它更多的是和设计有关,而非测试。
虽然在这个TDD(测试驱动开发,Test-Driven Development)过程中,会编写单元测试,但它们基本就是一些副产品。TDD的真正目的是让你一直保持设计整洁,这样在引入新的行为或者改变现有行为时,会更加从容,不会太费力。
从测试驱动的角度来说,你写测试的目的是为了保证代码能够符合预定规范。
从TDD的角度来说,如果一旦对所构建的东西有足够的信心,就可以停止TDD了。
可以在测试驱动开发时编写额外的事后测试。但通常而言,一旦认定有一个正确且干净的实现,并且这个实现能覆盖你要支持的情形,那么就可以立即停止TDD。也就是说,在你想不出可以写出不能通过的测试时,就可以停止。
(2)测试
而从测试的角度来看,寻求的是创建具有高覆盖率的测试。
所写的测试有五类情形:无(zero)、有(one),多(many),边界(boundary)和异常(exceptional)。
与TDD相反,优秀的测试人员会竭尽所能的去覆盖上面所说的五类情形。
参数化测试(了解即可,尽量少用)
对于更复杂的代码,或许有着覆盖一大堆情形的测试会让我们更有信心。
如果你需要测试大量的简单数据,那么参数化测试可能是一个很好的选择。
以下是参数化测试的例子
源码及单元测试代码如下:
测试结果如下: