对于数据库服务来说,在集成测试中,往往会引入H2内存数据库来模拟真实环境中的数据库服务。一般不是太特殊的SQL,都可以在H2内存数据库中运行。
对于Web Service,我暂时还没有很好的解决方案。之前有过CXF的项目经历,在测试环境中,魔改了client,从测试文件中读取XML响应体。但这么做也无法确保我们应用的对外调用参数是否输入正确。
无法保证AOP功能的正确性;
在集成测试中,整个应用服务都已经运行起来,所有AOP都是正常工作的,通过调整请求中的参数和头信息,就可以触发AOP的拦截,进而检查AOP逻辑的正确性。
重构难度大,不适合敏捷实践;
在集成测试中,所有的测试用例只在应用服务的外部检查,并不依赖内部的实现,所以如果重构时,对外的接口没有变化,无需修改测试用例,只需要完成实现的重构即可。
缺乏大局观,存在过度设计的可能;
如果我们的测试用例完整的覆盖了业务需求,那么运行过这些测试用例后,还存在着没有行覆盖到的代码,那么这些代码就是过度设计的代码,可以考虑删除或者检查测试用例是否存在缺失。
带来的挑战集成测试可以解决很多单元测试无法解决的问题,但也会带来新的挑战:
对于卡片,要拆分为前端卡与后端卡甚至更多的有着更多技术细节的子卡。在这些子卡中,BA需要清楚地认识到,想要达成业务需求,接口的格式应该是怎样,接口调用前后的数据变化。这些技术细节可以依赖团队里的TL或Sr Dev。
这样的实践,有些传统开发中概要设计的味道。虽然很多情况下,我们不会将卡片拆至如此细的粒度,但是这么做,可以更早的意识到这张卡的依赖项,同时也可以方便QA,针对这个接口设计测试用例。
由于集成测试中的测试用例可以完全来自QA,如果这些测试用例完全来自QA,可能需要QA摸索出一条新的工作节奏。如果这些测试用例完全来自开发,QA再独立写一套,那么可能会存在重复工作的现象。如果测试用例由开发编写,再由QA审核,这可能是个好实践,但我还没有尝试过。
在后端技术栈中,我们会使用数据库版本管理工具来管理数据库版本。在Java的技术栈中,通常我们会使用Flyway。但Flyway的一个局限性是就是过度依赖SQL,这使得一些DDL可以运行在真实环境中数据库,但却无法运行在H2数据库。所以在这里,我推荐Liquibase,这个框架会对数据库的更新做出自己的抽象,可以做到一个脚本运行在多种厂商的数据库,更适合集成测试的场景。
由于集成测试要启动一个真实的容器,所以自动测试时间也会更长,构建时间也会更长,不过还是在可以接受的范围内。
重申下适用范围尽管我这篇博客的主题是呼吁大家摒弃无意义的单元测试,但这是建立在我们所经历的大部分工作,都是针对接口的开发。在这样的工作中,单元测试有着很大的局限性,而集成测试有着更好的匹配度。
但如果你在开发一个类库,或者在DDD建模的早期,在这些场景中,单元测试才是更好的选择。