TDD中单元测试测试覆盖范围问题
1、问题
TDD要求测试反馈速度非常快,如果不快就没有办法支持小步快走的的三个标准步骤。如果进行重量级单元测试启动本地环境连接内存数据库或者文件IO等,无法达到秒级反馈。
所以只能采用测试替身(mocks或者stub)屏蔽掉被测对象的细节,那么每个单测覆盖的边界在哪里呢?
2、二分法
是做极端重视单元的隔离性的Mock主义者,还是做小型集成测试SUT(System Under Test)主义者?
1)Mock主义者写出的单测:测试范围小;测试准确性依赖于约定;测试支持由外向内的方法;设计风格进化是Mock主义者关键动力。
2)小型集成测试SUT主义者写出的单测:测试范围较大;使用真实对象或者替身作为模拟;将功能点作为测试目标,单测覆盖多个class;运行了第一个测试,对模拟的期望就作为下一步的规范和测试的起点,逐步逐步遍历系统层次,节奏可控。
3、综合
通常对于修改遗留代码、做ATDD,采取小型集成测试比较方便,提测前精神舒爽;
对于完全新增的代码,不妨试试重视隔离的单测。
无论选哪种TDD风格,提测前都需要再运行断言到数据库的重量级单元测试。
4、小型集成测试示例
描述:针对类链式调用场景 AFacade->BDoaminService->CMapper
环境:spring、jmockit
单测:将中间层依赖BDoaminService初始化,而不是直接使用@Injectable的Mock,再通过反射注入到AFacade中,那么测试范围可以覆盖到BDoaminService中。
@Test
void should_get_ok( @Tested(fullyInitialized = true) AFacadeImpl aFacade, @Tested(fullyInitialized = true) BDoaminServiceImpl bDoaminServiceImplInitialized,@Injectable CMapper cMapper,@Injectable BDoaminServiceImpl bDoaminServiceImpl) {
//设置期望
ReflectionTestUtils.setField(aFacade,"bDoaminService", bDoaminServiceImplInitialized);
new Expectations(){{
cMapper.update();
result = "ok";
times=1;
}};
//运行和断言
assertEquals("ok",aFacade.execute());
}
5、参考:
1、Martin Fowler 《Mocks Aren’t Stubs》[https://martinfowler.com/articles/mocksArentStubs.html]
2、肖鹏《Mock 七宗罪》[https://gitbook.cn/m/mazi/article/58eb77690ff0430e0255a8c4?isLogArticle=no&readArticle=yes&sut=23aab950c5ac11ebbd1ca9e03e8acc15]