第一步:防护网

尽管对于某些部分的重构来说,我们只是移动一下代码的位置 —— 如分层架构的调整,它不需要我们编写对应的测试。但是呢,出于流程完整性的考虑,这一步步往往流程比较长,毕竟它可以减少系统中 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(端到端)/集成测试框架的架构,我画了一个大致的测试策略分层架构图:

测试架构

选择适合你们团队的测试架构,然后编写你的第一个测试。

第一个测试

这个就简单了:

  1. 选择方案,然后 Google
  2. 寻找最简单的情形,编写测试
  3. 只需要有了第一个,剩下的就是时间问题。

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
下一节:架构将大问题分解为容易处理的小问题。——《架构师修炼之道 》