原创 | 使用JUnit、AssertJ和Mockito编写单元测试和实践TDD (八)好单元测试的特质

36 篇文章 2 订阅
36 篇文章 1 订阅

在这里插入图片描述
在这里插入图片描述

上一章讲了“CORRECT边界条件”,这一章我们讲讲“好单元测试的特质”。

好的单元测试应该具备下面的品质:

  • Automatic(自动化的)
  • Thorough(彻底的)
  • Repetable(可重复的)
  • Independent(独立的)
  • Professional(专业的)

下面分别论述

1. Automatic 自动化

单元测试应该能够自动地运行,从准备数据到执行测试到检查结果这一整个过程都不需要人工干预。

首先,调用测试的过程必须是自动化的,不需要任何人工干预步骤,例如人工输入参数值或回答YES/NO。对于测试所需要的任何预设条件(例如创建一个临时文本文件并输入指定内容)等等,都应该成为单元测试自身的一个自动化组成部分。配合Maven这样的自动化构建工具,能够做到一键执行整个项目的全部单元测试并生成测试报告。

    @Test
    void testConfig() throws Exception {
        File file = File.createTempFile("app", ".conf");
        file.deleteOnExit();
        BufferedWriter out = new BufferedWriter(new FileWriter(file));
        out.write("charset=UTF-8");
        Configuration instance = Configuration.fromFileSystem(file);
        assertThat(instance.getString("charset")).isEqualTo("UTF-8");
    }

上面的例子,为了测试用于读取配置文件的内容的Configuration类的getString()方法,必须首先存在一个包含确定内容的配置文件。我们不是在电脑上预先手工创建一个配置文件并填充内容,而是将创建文件和填充内容作为单元测试本身的一部分。这样的测试执行时就不需要人工干预了。

另一方面,检查测试结果也应该是自动化的,测试必须能够自己决定它是通过了还是失败了,而不需要人工确认。例如你要测试一个加密函数是否正确,你不应该让测试打印出加密结果,然后人工核对输出字符串和我们期望的加密后的字符串是否一样,而是由测试本身来断言二者是否相等。

    @Test
    void testEncrypt() {
        String origStr = "xxxx";
        String expectedStr = "yyyy";
        Encryptor instance = new Encryptor();
        assertThat(instance.encrypt(origStr)).isEqualTo(expectedStr);
    }

2. Thorough 彻底

好的单元测试应该覆盖了被测工作单元的每一个分支执行路径,每一个可能抛出的异常,甚至每一行代码(除了哪些getXXX()和setXXX()等极端简单,不包含逻辑的方法)。如果代码中有if…else…,你不能只是测试if这个分支,而不测试else这个分支。如果使用了switch语句,同样要测试到每一个case,以及最后的default。

3. Repetable 可重复

测试必须是可重复的,意味着:

  • 无论执行多少遍,结果都是一样的

    这意味着测试不会改变会影响它下一次运行的环境。例如一个测试往一个中央数据库插入一行数据,然后断言目前有多少行数据。这样的测试就是不可重复的,因为每次执行测试,记录行数都会加一。另外别人也可能向这个数据插入数据。解决办法是使用本地数据库(不共享),并且在每次测试之前清空数据。

  • 在任何人的电脑上执行,结果都是一样的

    这意味着测试不依赖于现有的计算机环境(操作系统,某个文件存在与否,等等)。不能只是*“在我的笔记本上能够运通过测试”*。

  • 在任何时间执行,结果都是一样的

    这意味着测试结果不受时间因素影响。如果被测代码的行为依赖于当前日期/时间(例如周末不能取款),那么在工作日和在周末执行取款测试,结果就可能不一样。这种情况下通常要重构产品代码,不要在方法内部通过

    Date now = new Date();
    

    的方式获取当前时间,而是将时间作为一个参数传递给方法:

    public void withdraw(int amount, Date withdrawTime)
    

    由客户代码负责获取当前时间并传递给Account.withdraw()方法。这样对withdraw()方法的单元测试又是可重复的了。

总之,单元测试的结果不受环境影响,也不受上次测试影响。无论如何,单元测试决不能依赖于共享资源(例如共享数据库或消息中间件),因为别人会操作这个资源,从而导致你的测试结果不确定。

4. Independent 独立

独立有两个含义:

  1. 一个测试只测试一件事情。

    每个测试都有且只有一个关注点。

  2. 测试不依赖于其他测试。也就是说,多个测试之间没有顺序依赖。

    一个测试不应该依赖于此前运行的另一个测试来为它设置测试条件(例如,第一个测试创建了文本文件并写入内容,第二个测试读取文件内容)。每个测试所需要的预设条件都应该在测试本身里面设置(第二个测试先创建文件,写入内容,再执行测试)。

5. Professional 专业

要按照编写产品代码的质量标准去编写测试代码。不要认为测试代码是二等公民,不值得为了提高它的质量而花费心力。在测试代码中,针对好设计的所有普遍规则——维护封装、采用DIY原则、降低耦合,等等等等,同样适用。

第一部分**“单元测试”就讲到这里,下一章将开始展开讲讲第二部分“测试驱动开发TDD”**,敬请关注!

在这里插入图片描述
-THE END-
原创作者 | 杨宇Yangyu
编程道与术原创内容
​转载请注明“编程道与术”出处

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值