【学习笔记】如何写一个优雅且完善的单元测试

前言,发现了一个b站的宝藏UPYimcode,搬运一些外国的java视频,很适合初级程序员学习,本篇文章内容总结自你真的会写单元测试吗

单元测试

一直听大厂的程序员在提到的东西,但是以我目前的水平比较难接触,不过作为程序员总是要向上看才对,,就通过这个视频来总结一些自己的收获吧。视频的思路总结如下:

  • 介绍单元测试及其所需的依赖junit
  • 介绍如何快速创建测试类,并介绍了正确的测试类命名规则(之一)
  • 介绍并强调如何使用断言Assert
  • 强调需要多个测试方法保证代码符合预期
  • 介绍如何提高测试覆盖率
  • 介绍如何适用于断言来测试异常的抛出情况

一、认识单元测试

  • 单元测试是对单个代码或者单元进行测试的一种软件测试
  • 我们需要隔离一段代码,并验证它是否正确运行
  • 这个段代码可以是类或者类中的方法
  • 所以,单元测试就是要测试你用Java写的代码,并验证它是否做了它应该做的事情

unit testing unit testing is a type of software testing where one individual piece of code or unit ,is being tested by itself so a unit test isolates one single piece of code and verifies that piece is working correctly usually for java that single piece of code is going to be a class or even a method inside a class ,so unit tests are just code that you write also in java that individual method that you want to test and verify that it’s doing exactly what it should do

二、Junit

1、导入依赖

<dependencies>
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter</artifactId>
        <version>5.9.2</version>
        <scope>test</scope>
    </dependency>
</dependencies>

2、使用一个简单的测试类

2.1 创建测试类

创建一个目标类如下:

package com.zhc.demo;

public class Calculator {
    
    

    public int add(int a, int b) {
    
    
        return a + b;
    }
}

点击空白处 ,使用IDEA的快捷键 CTRL+SHIFT+T创建测试类

image-20230410104755247

注意:
  • 测试类的报路径应该和目标类一致

  • 测试类命名规则: 类名 为 目标类+Test

  • 测试方法命名规则: 条件A ShouldReutrn 条件B

  • 对应的测试方法上应该加入注解 @Test

  • IDEA自动生成的测试在符合上述规则的条件下,还导入了junit提供的断言Assert包。

  • 完整初始测试类如下

    package com.zhc.demo;
    
    import static org.junit.jupiter.api.Assertions.*;
    
    class CalculatorTest {
          
          
    
    
    }
    

2.2、使用断言,编写测试内容

比如这次要使用的是比较断言,需要传入两个参数,前面的期望,后面是实际,比较结果不同就会抛出异常:

image-20230410105541028

也可以使用 布尔断言,判断结果是否为true 或false来判断。写法如下:

assertEquals( 4 , calculator.add(2 , 2));// 比较断言
assertTrue(calculator.add(2 ,2) == 4); 	// 布尔断言

2.3、运行测试

image-20230410105623064

运行成功则没有提示,如何运行失败,则会报出异常

image-20230410105922357

三、完善单元测试

1、多条件测试才能保证符合预期

以目标类是加法计算为例,如果把目标类的加法,改成乘法,我们会发现 输入的参数(2 ,2)得出的结果也是4 ,这就涉及到测试的完善程度的问题了。

所以,一个完善的单元测试,一个方法要用多个条件进行测试才对。

image-20230410110449423

2、单元测试的覆盖率

  • 一个完整的单元测试,应该保证所有独立的的场景都要进行单独的测试。

  • 千万不要将多个场景用一次测试处理

  • IDEA提供了展示测试覆盖率的工具,使用方法如下:

    image-20230410112150140

视频中使用了一个多分支方法,来模拟该场景,我也写一个demo,如下

package com.zhc.demo;

public class Multiple {
    
    

    public String multipleBranch(int num){
    
    
        if (num < 0) {
    
    
            throw new IllegalArgumentException("参数不合法:应该大于0");
        }
        else if (num < 60 ){
    
    
            return "D";
        }
        else if (num < 70 ){
    
    
            return "C";
        }
        else if (num < 80 ){
    
    
            return "B";
        }
        else {
    
    
            return "A";
        }
    }
}

测试方法如下

package com.zhc.demo;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

class MultipleTest {
    
    

    @Test
    void fiftyNineShouldReturnD() {
    
    
        Multiple multiple  = new Multiple();
        assertEquals("D" , multiple.multipleBranch(59));
    }
    @Test   
    void sixtyNineShouldReturnC() {
    
    
        Multiple multiple  = new Multiple();
        assertEquals("C" , multiple.multipleBranch(79));
    }
    @Test
    void seventyNineShouldReturnB() {
    
    
        Multiple multiple  = new Multiple();
        assertEquals("B" , multiple.multipleBranch(89));
    }
    @Test
    void ninetyNineShouldReturnA() {
    
    
        Multiple multiple  = new Multiple();
        assertEquals("A" , multiple.multipleBranch(99));
    }
}

运行测试后可以查看到覆盖率:

image-20230410112418116

双击可以跳转到对应的类下:

image-20230410112504993


3、根据边缘条件继续提高覆盖率

根据前面的简单场景的单元测试,我们提供了一个高覆盖率的测试场景。但是思考一下,如果我修改原方法——比如“将D的条件改为 (num < 65)”,我们会发现测试依然的通过的。

所以这个时候就需要考虑到边缘条件的问题了。

  • 其实不难发现前面的测试类已经根据边缘条件进行了分段测试来保障覆盖率了。
  • 但是要完整的测试边缘条件,应该继续添加新的测试场景来确保目标方法符合预期

在上述的案例中,就需要继续添加新的测试条件:对num 等于60/80/90的场景,再分别进行测试,以确保目标方法符合预期。

4、使用断言来处理异常

在正常的操作中,抛出异常就以为的测试失败。但是有些场景,比如上述的分级案例,就是要抛出异常才是符合预期的,应该如何测试呢?视频里给出的写法。

异常断言assertThrows:第一个参数的异常类.class,第二个参数的一个lambda表示来表示运行的方法内容。

@Test
void negativeOneShouldReturnIllegalArgumentException(){
    
    
    Multiple multiple  = new Multiple();
    assertThrows(IllegalArgumentException.class ,
                 () -> {
    
    
                     multiple.multipleBranch(-1);
                 });
}
  • 如果异常类型不符,报错如下:

    image-20230410114049789

  • 如果没有抛出异常,也会报错

    image-20230410114126301

总结

在实际的开发中,一个完整的单元测试不仅要保障代码正常运行,还要保障测试代码覆盖了所有的分支,并且要根据边缘条件,尽可能的确保目标方法符合预期。

为了确保你的代码是正确的,我们可以编写一堆测试场景,这样做有以下好处

  • 便于在后续对代码进行更新和拓展
  • 而且每次更新代码都可以再次更新或新增单元测试
  • 因为单元测试的存在,你有绝对的信心,去以任何想要的方式去重构代码 (因为你知道 :即使代码重构的一团糟,单元测试也会告诉你失败了)

最后作者做了一个小重构,将案例中的else都删除了,而且符合预期,说明重构成功了。

猜你喜欢

转载自blog.csdn.net/Xcong_Zhu/article/details/130056710
今日推荐