实践
以可测试性为目标来编写代码
即使我们不采用测试驱动开发(TDD)的方式开发产品,但我们也应该将可测试性这条准则铭记在心。你编写的代码无法自动化测试,或难以自动化测试就必须依靠人工手动测试。人工测试会有疏漏,而且重复两次的测试可能有些少许的不同,都会疏漏一些重要的问题,或者难以重现bug。
比如我们要尽量避免与一些难以测试的部分紧耦合,比如连接数据库部分,向屏幕打印输出的部分,发送短信的部分等。比如你在一个类里,需要发送邮件,现在你需要测试这个类里的逻辑,这个时候你并不是为了测试发送邮件的客户端。如果你这个时候直接使用发送邮件客户端的静态方法,这就是一种非常强的紧耦合,如果我们想自动化测试这个类,我们就无法用花费更小,运行更快的单元测试甚至是集成测试来测试这段代码,我们就必须在功能测试这个层次来测试。
测试应该可以重复运行
第一个测试运行完毕后,修改了第二个测试所依赖的一个关键数据,测试运行不了了,必须人工干涉测试才能继续进行。这种自动化测试也是没有任何价值的。
幸好,我们有事务这个好东西,在事务开始时我们准备数据,然后运行测试。在测试完毕后,我们回滚事务,没有污染数据库。就拿基于Spring的集成测试为例:
Integration.java @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations="classpath:applicationContext-test.xml") @TransactionConfiguration(transactionManager = "txManager") @Transactional public abstract class IntegrationTest { } |
我们添加@Transactional标签,让我们的测试在事务中运行。为了避免所有的测试类,都要带上那堆标签,为此提取一个基类。基类设为抽象的是因为,一个没有任何测试的测试类,maven运行这类测试的时候会报错误。
如果在某一个测试中我们不想让事务回滚,我们可以添加@Rollback(false)标签(这一般在调试时很有用)。
AgentServiceIntegrationTest.java public class AgentServiceIntegrationTest extends IntegrationTest{ @Before public void setUp(){ } @Test public void should_find_agent_by_id(){ } @Rollback(false) @Test public void we_want_to_store_data_in_database(){ } @After public void tearDown(){ } } |
何时添加自动化测试
调试
当你打开调试器,一步一步的调试代码来找出那个纠结的问题时,为什么不添加一个自动化测试来帮你定位问题呢。很多时候,这类调试甚至要启动一个tomcat才能进行。
打印输出
我在有些代码里看到过System.out.println("test"); 我估计是因为我们在用这种方式来寻找bug,这个时候也是添加自动化测试的良机,不要错过。
发现bug的时候
每个bug的发现,就说明你开发这段代码的时候忽视了某些问题,这个时候我们就应该添加一个测试复现这个bug,然后修复bug。而不是仅仅修复了事。