软件开发工程师谈测试金字塔实践

网站建设4年前发布
49 0 0

测试金字塔是对测试的分层描述,在不同层次做不同类型的测试。测试金字塔如何运用到工程实践,是一件困难的事情。「原文作者是一位德国Thoughtworks的软件开发工程师」,本文将回顾传统的测试金字塔,并结合实例,进行一次有深度的探秘实践。,20230306015308613fc11410a52d9264927724c399fdf6037cbd871,软件上线前都是要经过测试的,随着测试技术发展,相比于传统的手工测试,如今的自动化测试越来越重要,它能够将成天上周的测试工作缩减到分钟秒级,提高测试效率,更快发现缺陷。尤其是在敏捷开发、持续交付、DevOps文化中,自动化已经成为了对测试的基本要求。比如持续交付,使用build pipeline自动测试和部署,随时能发包到测试环境和生产环境。,20230306095538f5240a484dddd8d33ca037c3683d29ecf6c1ab103,测试金字塔是Mike Cohn在他的书籍《Succeeding with Agile》中提出的概念:,2023030601530941d8f8694295da0f7c9629af308eb9296e1101396,测试金字塔描绘了不同层次的测试,以及应该在各个层次投入多少测试。由底向上包括3层:,这是最原始的测试金字塔,从现代视角来看,这个金字塔显得过于简单了,并且可能造成误导。比如service test不太能定义清楚。比如在react, angular, ember.js等单页应用中,UI测试并不一定在最顶层,而是可以写单元测试来测试UI。,但它有2点启示:,作者在GitHub上传了开源项目(795star):,https://github.com/hamvocke/spring-testing,包含了遵循测试金字塔的分层测试的SpringBoot微服务应用。,功能,它提供了3个接口:,GET /hello 返回”Hello World“,GET /hello/{lastname} 返回"Hello {Firstname} {Lastname}",GET /weather 返回德国柏林的天气(作者住在这),整体结构,202303060153105378d75473b68b0e27f372af1c14fab07f3ef0359,Spring Service从数据库取数据,对外提供API返回JSON数据,非常标准的简单应用。,内部结构,202303060153116108276314b91d212820446d1d57e64f78c796334,该应用支持CRUD,使用Spring Data访问数据库,数据库用的也是内存数据库,并且设计上省略掉了Service层,一切都为了简单,方便测试。,202303060153116276b9a3130ebf8ffaf761d7c3f0e88bf306fc418,什么是单元?,不同人对单元有不同理解,所谓单元,通常指某个函数,单元测试就是使用不同参数来调用函数,验证是否满足预期结果。在面向对象语言中,单元,可以是单个方法,也可以是整个类。,Mock和Stub,Test Double是“测试复制品“的意思,用来统称模拟真实对象的假对象:,2023030601531108346ae11db9cbad444608d9d2c0d02906c32a822,Mock和Stub都是用来模拟的,它们的区别在于:,202303060955387559b1e39b09d73408f959fe8c2655a002a5e4441,Stub只负责模拟,Mock还包括了验证。,以上是晦涩难懂且无关紧要的理论概念。实际点的,拿本文用到的Mockito和WireMock来说,Mockito用于单元测试mock依赖,WireMock用于集成测试stub外部服务,本质上都是「模拟」。,测什么,单元测试什么都能测,这就是单元测试的好处。,编写单元测试要遵循原则:一个production class对应一个test class。public要尽可能覆盖,private无法覆盖,protected或者package-private可覆盖可不覆盖,建议别覆盖。并且要保证分支覆盖,包括正常分支和边界场景。,但是并不是所有的public都需要编写单元测试,而是要「避免琐碎的测试」,比如getters或setters就不要测了,比如一些没有任何逻辑条件的也不需要测。,测试结构,这是所有测试的良好结构设计,不只是单元测试。这三步还有其他叫法:"Arrange, Act, Assert",或者"given", "when", "then"。,实现单元测试,对于以下ExampleController:,编写单元测试:,单元测试使用了JUnit,PersonRepository使用了Mockito模拟数据。第一个测试是验证入参存在的名字会返回Hello。第二个测试是验证入参不存在的名字会返回Who。,单元测试是模块内测试,针对模块之间,就要做集成测试。还有其他部分,比如数据库、文件系统、远程调用其他应用等,这些在单元测试中会忽略或者mock掉,也都需要做集成测试。集成测试也有多种理解,可以理解为全部集成的测试。而作者的想法是单独集成,一次只集成一个,比如集成测试数据库,那么其他部分仍然使用mock:,20230306015312a57d7bc08961e96583444623a252c9c409224c165,比如集成测试其他服务:,2023030609553981f59f283f82a634243524c88dade0798e39be466,实现数据库集成,PersonRepository:,PersonRepository继承了CrudRepository,借助于Spring Data自动实现了增删改查,比如findOne, findAll, save, update, delete等方法,对于findByLastName方法,Spring Data也会根据返回类型、方法名称自动判断进行适配处理。,示例,保存Person到数据库中,并根据lastName查询:,实现独立服务集成,使用Wiremock模拟darksky.net服务:,怎么才能访问mock的这个服务呢?答案是在application.properties文件中配置:,以及WeatherClient实现:,在集成测试darksky.net服务时,采用的是Wiremock,mock了darksky.net服务,如何验证mock的服务和真实的服务之间有无差异呢,就要进行契约测试。,在微服务架构体系中,应用被拆分成了多个独立的松耦合的服务,彼此之间通过接口通信:,每个接口包含2部分:provider和consumer:,20230306015314a9806662469ad57269e392b415eb3c2a15348f856,比如在HTTPS中,provider提供接口,consumer调用接口;比如在消息队列中,provider发布消息,consumer订阅消息。,所谓契约,就是接口之间相互约定好的定义。传统的契约过程是这样的:,而在CDC(Consumer-Driven Contract tests)中,第5、6步已经被自动化测试取代:,20230306015315457a0b113a0a71174aa58640e3a65d48b748f6465,consumer编写并发布契约测试,provider获取并执行契约测试,当provider把所有契约测试都实现以后,自然就满足consumer了。provider会把契约测试放入持续集成中,确保所有契约测试都能始终保持通过,假如consumer发布了新的契约,契约测试就会失败,从而提醒provider更新实现。,Consumer Test,使用Pact工具实现契约测试。,build.gradle:,WeatherClientConsumerTest:,每次运行都会生成一个pact文件,target/pacts/&pact-name>.json,这个文件就可以拿给provider实现契约,通常做法是让provider在仓库中取最新版本文件。,Provider Test,provider加载pact文件并实现契约:,UI测试主要验证应用界面是否正确:,20230306015316736bdd3443439f2cf60814069f7a6714f12d56999,用户输入,触发程序,数据展示给用户,状态变更正确。,UI自动化主要基于Selenium来做,由于前端变化大、控件识别难等问题,导致UI自动化失败率比较高,可以考虑采用截图的方式,把前后截图进行对比,来做断言,当然Selenium已经支持截图对比了。,端到端测试,通常是指从用户界面进行测试:,20230306015316a4e4b0c6661bdf412695270c0c88688cfda5ab227,如果没有用户界面,也可以指对接口进行测试。,UI端到端测试,使用Selenium和WebDriver实现:,build.gradle,HelloE2ESeleniumTest,接口端到端测试,使用REST-assured实现:,build.gradle,HelloE2ERestTest,在测试金字塔的位置越高,就越会站在用户角度进行测试。验收测试就是完全从用户角度出发,看系统是否能满足用户需求。,简单示例:,探索测试是一种手工测试方法,充分发挥了测试人员的自由和创造力。,20230306015316437298d82257b7436f411750316c22bdfaf569329,探索测试发现缺陷以后,可以补充到自动化测试中,以避免将来出现这个问题。,单元测试、集成测试、端到端测试、验收测试,每个人都有自己的不同理解,现在的软件测试行业,也没有统一的测试术语,将这些测试类型的边界明确区分开来。只要我们在公司内部、团队内部,能对术语达成一致,顺畅沟通就可以了。,Thoughtworks研发博客 https://martinfowler.com/articles/practical-test-pyramid.html,Test Double http://xunitpatterns.com/Test%20Double.html,WireMock和Mockito区别 https://geek-docs.com/mockito/mockito-ask-answer/wiremock-vs-mockito.html,Pact官方文档 https://docs.pact.io/

© 版权声明

相关文章