尽管对于某些部分的重构来说,我们只是移动一下代码的位置 —— 如分层架构的调整,它不需要我们编写对应的测试。但是呢,出于流程完整性的考虑,这一步步往往流程比较长,毕竟它可以减少系统中 bug 的出现,降低重构的风险。与此同时,这是可以由团队一起协作完成的工作,特别适合于多人的协同重构方式。
防护网策略
为了保证对外暴露的 API 好的 ,即我的重构不影响 API 的使用方,我们需要设计一个合适的防护策略。
在设计的时候,我们采用的是测试金字塔来帮助我们搭建测策略。我们会从下(单元测试)向上(集成测试)一步步搭建测试策略。
而当我们重构的时候,我们则是自顶向下设计防护策略。常见的测试策略有
- 框架/模式库测试。xUnit,xMock,如 Java 语言里的 JUnit, Mockito;JavaScript 中的 Jest
- 端到端 API 测试。JMeter,Postman,Rest Assured,Karate
- UI 集成测试。Protractor
考虑到测试即文档,在实现实现的时候,会配合一些支持自然语言描述的框架,如:
- 文档式测试,Gauge (主流语言),Concordion(Java)
- BDD 测试,Cucumber(主流语言)
- ATDD 测试,Robot Framework(Python 语言)
为了与运行客户端一配合,我们还需要有底层 API 来控制浏览器、客户端应用:
- Appium。移动 APP 和桌面应用,支持主流语言
- Selenium。Web 浏览器,支持主流语言
- Puppeteer。Node.js API 操作 Chrome 浏览器
关于 APP 测试方案,可以参考我之前写的《【架构拾集】移动应用的自动化测试(BDD 方式)》
根据现有的 E2E(端到端)/集成测试框架的架构,我画了一个大致的测试策略分层架构图:
选择适合你们团队的测试架构,然后编写你的第一个测试。
第一个测试
这个就简单了:
- 选择方案,然后 Google
- 寻找最简单的情形,编写测试
- 只需要有了第一个,剩下的就是时间问题。
Done!
持续集成重构
如果你还没有持续集成环境的话,那么请搭建它。
考虑到这是一个体力活,而且这方面的资料已经足够的多,我就不浪费大家的时间了。
顺带一提,如果你的分支比较多,而且构建比较多,那么你可以考虑 pipeline as pipeline
的方式进行构建。
检视测试
某次代码重构中,我发现代码的测试覆盖率很高,过程中出了一些错误,重构手法不正确是一个问题。但是在重构的过程中,发现有些测试都是没有意义的,所以这让我意思到在构建防护网的时候,有必要审视一遍测试,查找测试代码中的坏味道。
测试代码坏味道,是指单元测试代码中的不良编程实践(例如,测试用例的组织方式,实现方式以及彼此之间的交互方式),它们表明测试源代码中潜在的设计问题。
常见的测试坏味道有:
- 空的测试。测试是生成的,但是没有内容。
- 忽略的测试。即测试被 Ignore
- 没有断言的测试。为了测试覆盖率而出现的测试
- 多余的 Println。调试时留下的讯息。
- 多重断言。每个测试函数只应该测试一个概念。
- ……
有兴趣进一步了解的话,可以阅读《测试代码的坏味道》。也可以 coca tbs
来查找测试中的坏味道:
TYPE | FILENAME | LINE |
---|---|---|
DuplicateAssertTest | app/test/cc/arduino/i18n/ExternalProcessOutputParserTest.java | 107 |
DuplicateAssertTest | app/test/cc/arduino/i18n/ExternalProcessOutputParserTest.java | 41 |
DuplicateAssertTest | app/test/cc/arduino/i18n/ExternalProcessOutputParserTest.java | 63 |
RedundantPrintTest | app/test/cc/arduino/i18n/I18NTest.java | 71 |
RedundantPrintTest | app/test/cc/arduino/i18n/I18NTest.java | 72 |
RedundantPrintTest | app/test/cc/arduino/i18n/I18NTest.java | 77 |
DuplicateAssertTest | app/test/cc/arduino/net/PACSupportMethodsTest.java | 19 |
DuplicateAssertTest | app/test/processing/app/macosx/SystemProfilerParserTest.java | 51 |
DuplicateAssertTest | app/test/processing/app/syntax/PdeKeywordsTest.java | 41 |
DuplicateAssertTest | app/test/processing/app/tools/ZipDeflaterTest.java | 57 |
DuplicateAssertTest | app/test/processing/app/tools/ZipDeflaterTest.java | 83 |
DuplicateAssertTest | app/test/processing/app/tools/ZipDeflaterTest.java | 109 |
下一节:架构将大问题分解为容易处理的小问题。——《架构师修炼之道 》