在没有接触单元测试之前我们是怎么做测试的?一般有两个方法:启动整个应用,像用户正常操作一样。点击界面按钮,调用一个 API 等。手动测试的坏处是每次测试都得启动整个应用,项目稍微一大非常慢,PHP、Nodejs 还好,尤其是 Java、C++ 这种编译型语言非常痛苦。

在代码某个地方写一个临时入口,例如 java 的 main 方法,测试某个方法或者某个类,用完留在项目中或者删除。如果不删除的话会让项目变得很乱,删除的话下次想测试又得弄个新得。

这两个方法都有一个共同得不足,没法保留测试数据的创建过程,场景、边界的覆盖基本随缘。 单元测试本质上就是方法 2,把类似 main 方法的测试代码统一放到一个地方。然后根据一些约定,让代码更加简洁。但不强制你把测试代码放到任何一个地方。

根据约定:test 代码单独放到 src/test 目录下,与 src/main 一一对应

测试类和源代码保持同名+Test 后缀

理论上不使用任何测试框架也可以完成编写单元测试,最初的单元测试也是这样。不过好在现在可以利用 xUnit 等框架更方便的运行测试。使用框架的单元测试好处有:通过 Runer 可以批量运行

使用 @Before 等钩子实现数据准备、数据清理

通过断言实现结果的验证,避免人工确定结果的正确性

通过覆盖率统计工具,统计代码测试覆盖率

通过 Mock 解决代码相互依赖的问题

当然,编写单元测试的目的除了测试方便之外,还有一个重要的用途:重构。

某些场景下,你需要改造一些遗留代码,并接近 100% 兼容原来的逻辑,没有单元测试保护的情况下,没人敢改,造成代码越来越混乱。通过单元测试对原来的业务逻辑进行覆盖,在有保护的情况开始重构,重构完成后再次运行单元测试,如果能通过测试说明基本上没有破坏性改动。