详解介绍JUnit单元测试框架(完整版)

(一)JUnit介绍

目录

(一)JUnit介绍

1.什么是单元测试?

2.什么是单元测试框架?

3.什么是JUnit?

(二)JUnit 安装

1.IntelliJ IDEA 安装 Junit

2.Maven 安装 Junit

(三)JUnit 编写单元测试

1.编写单元测试

2.测试功能模块

(四)JUnit 注解

1.JUnit 注解

2.例子

(五)JUnit 注解之Fixture

什么是Fixture

JUnit 中的 Fixture

(六)JUnit 用例执行顺序

@FixMethodOrder

例子

 (七)JUnit 断言方法

JUnit 断言方法

例子

(八)JUnit 测试批量运行

IntelliJ IDEA 中设置运行

通过测试套件运行

(九)JUnit5 介绍与安装

JUnit5 介绍

Maven 安装

(十)JUnit5 创建测试

创建测试用例

(十一)JUnit5 新的用法

(十二)补充:JUnit 注解之Rule

使用框架自带的Rule

自定义的Rule


1.什么是单元测试?

单元测试负责对最小的软件设计单元(模块)进行验证,根据软件设计文档中对模块功能的描述,对重要的程序分支进行测试并发现错误。

2.什么是单元测试框架?

对于单元测试框架来讲,它主要完成以下几件事。

提供用例组织与执行:测试用例只有几条时,可以不考虑用例组织,但是用例达到成百上千时,大量的测试用例堆砌在一起,就产生了扩展性与维护性等问题

提供丰富的断言方法:不论是功能测试,还是单元测试,在用例执行完之后都需要将实际结果与预期结果相比较(断言),从而断定用例是否执行通过。单元测试框架一般提供丰富的断言方法。例如:判断相等/不等、包含/不包含、True/False的断言方法等

提供丰富的日志: 当测试用例执行失败时能抛出清晰的失败原因,当所有用例执行完成后能提供丰富的执行结果。例如,总执行时间、失败用例数、成功用例数等。

从这些特性来看单元测试框架的作用是:帮助我们更自动化完成测试,所以,它是自动化测试的基础。

3.什么是JUnit?

Junit 官网:http://junit.org/

JUnit 是一个编写可重复测试的简单框架。它是单元测试框架的 xUnit 架构的一个实例。

(二)JUnit 安装

Junit目前分两个版本,Junit4 和 Junit5 , 本系列教程打算从 Junit4 开始介绍,最后,再介绍 Junit5 有哪些新特性

1.IntelliJ IDEA 安装 Junit

Java 开发的同学,推荐使用 IntelliJ IDEA,推荐阅读《IntelliJ IDEA 教程》。

1、下载 junit-4.12.jar 文件:https://github.com/junit-team/junit4/releases

2、 打开 IntelliJ IDEA ,菜单栏:File菜单 –> Porject Structure 选项 –> Dependencies 标签 –> 点击 “+” 号 –> Library… –> Java 。 选择下载的 junit-4.12.jar 进行添加。

3、以同样的方式下载和导入 hamcrest: https://github.com/hamcrest/JavaHamcrest/releases ,否则,你将无法运行 Junit 单元测试。

2.Maven 安装 Junit

相比较而言,Maven 的安装要简单很多,打开你 Maven 项目中的 pom.xml 文件,添加如下配置:

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>

更多的 Maven 项目,可以登录:https://www.mvnrepository.com 网站查找。

(三)JUnit 编写单元测试

1.编写单元测试

创建 JunitDemo 类,编写第一个单元测试用例。

import static org.junit.Assert.assertEquals;
import org.junit.Test;

public class JunitDemo {

    @Test
    public void myFirstTest() {
        assertEquals(2+2, 4);
    }

}

@Test 用来注释一个普通的方法为一条测试用例。

assertEquals() 方法用于断言两个值是否相关。

2.测试功能模块

创建一个被测试类:Count ,代码如下:

public class Count {

    /**
     * 计算并返回两个参数的和
     */
    public int add(int x ,int y){
        return x + y;
    }
}

Count 类的实现非常简单,看注释就可以了。

接下来,创建 CountTest 类,用于测试 Count 类。

import static org.junit.Assert.assertEquals;
import org.junit.Test;

public class CountTest {

    @Test
    public void testAdd() {
        Count count = new Count();
        int result = count.add(2,2);
        assertEquals(result, 4);
    }

}

new 出 Count 类,调用 add() 方法并传参,通过 assertEquals() 断言 返回结果。

恭喜! 你已经会编写单元测试了。

(四)JUnit 注解

1.JUnit 注解

JUnit 注解说明:

注解说明
@Test:标识一条测试用例。 (A) (expected=XXEception.class)   (B) (timeout=xxx)
@Ignore: 忽略的测试用例。
@Before: 每一个测试方法之前运行。
@After : 每一个测试方法之后运行。
@BefreClass 所有测试开始之前运行。
@AfterClass 所有测试结果之后运行。

2.例子

创建被测试类 Count .

public class Count {

    /**
     * 计算并返回两个参数的和
     */
    public int add(int x ,int y){
        return x + y;
    }

    /**
     * 计算并返回两个数相除的结果
     */
    public int division(int a, int b){
        return a / b;
    }
}

创建测试类 CountTest .

import static org.junit.Assert.assertEquals;

import org.junit.Ignore;
import org.junit.Test;


public class CountTest {

    //验证超时
    @Test(timeout=100)
    public void testAdd() throws InterruptedException {
        Thread.sleep(101);
        new Count().add(1, 1);
    }

    //验证抛出异常
    @Test(expected=ArithmeticException.class)
    public void testDivision() {
        new Count().division(8, 0);
    }

    // 跳过该条用例
    @Ignore
    @Test
    public void testAdd2() {
        Count count = new Count();
        int result = count.add(2,2);
        assertEquals(result, 5);
    }

}

1、在 testAdd() 用例中设置 timeout=100 , 说明的用例的运行时间不能超过 100 毫秒, 但故意在用例添加 sleep() 方法休眠 101 毫秒,所以会导致用例失败。

2、在 Java 中被除数不能为0,所以 8⁄0 会报 ArithmeticException 异常, 在 @Test 中设置 expected=ArithmeticException.class ,说明抛该异常符合预期。

3、@Ignore 表来标识该用例跳过,不管用例运行成功还是失败。

执行结果如下:

(五)JUnit 注解之Fixture

继续介绍 JUnit 的注解

什么是Fixture

Test Fixture 是指一个测试运行所需的固定环境,准确的定义:

The test fixture is everything we need to have in place to exercise the SUT

在进行测试时,我们通常需要把环境设置成已知状态(如创建对象、获取资源等)来创建测试,每次测试开始时都处于一个固定的初始状态;测试结果后需要将测试状态还原,所以,测试执行所需要的固定环境称为 Test Fixture。

JUnit 中的 Fixture

被测试类同样使用上一小节的 Count , 创建 TestFixture 测试类。

import static org.junit.Assert.*;
import org.junit.*;

public class TestFixture {

    //在当前测试类开始时运行。
    @BeforeClass
    public static void beforeClass(){
        System.out.println("-------------------beforeClass");
    }

    //在当前测试类结束时运行。
    @AfterClass
    public static void afterClass(){
        System.out.println("-------------------afterClass");
    }

    //每个测试方法运行之前运行
    @Before
    public void before(){
        System.out.println("=====before");
    }

    //每个测试方法运行之后运行
    @After
    public void after(){
        System.out.println("=====after");
    }

    @Test
    public void testAdd1() {
        int result=new Count().add(5,3);
        assertEquals(8,result);
        System.out.println("test Run testadd1");
    }

    @Test
    public void testAdd2() {
        int result=new Count().add(15,13);
        assertEquals(28,result);
        System.out.println("test Run testadd2");
    }

}

代码中的注释已经对 @BeforeClass、 @AfterClass 、 @Before 、 @After 做了说明。

至于什么时候会用到这些方法跟你具体的业务用例有关,如果是 Web UI 自动化测试,可以把 浏览器驱动的定义放到 @Before中,浏览器的关闭放到 @After 中。

运行结果如下:

(六)JUnit 用例执行顺序

在运行测试的过程中,有时候需要控制用例的执行顺序。

@FixMethodOrder

JUnit 通过 @FixMethodOrder 注解来控制测试方法的执行顺序的。@FixMethodOrder 注解的参数是 org.junit.runners.MethodSorters 对象,在枚举类 org.junit.runners.MethodSorters 中定义了如下三种顺序类型:

  • MethodSorters.JVM

 Leaves the test methods in the order returned by the JVM. Note that the order from the JVM may vary from run to run (按照JVM得到的方法顺序,也就是代码中定义的方法顺序)

  • MethodSorters.DEFAULT(默认的顺序)

Sorts the test methods in a deterministic, but not predictable, order() (以确定但不可预期的顺序执行)

  • MethodSorters.NAME_ASCENDING

Sorts the test methods by the method name, in lexicographic order, with Method.toString() used as a tiebreaker (按方法名字母顺序执行)

例子

具体如何使用,看例子,创建 TestRunSequence 测试类。

import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
import static org.junit.Assert.assertEquals;

// 按字母顺序执行
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class TestRunSequence {

    @Test
    public void TestCase1() {
        assertEquals(2+2, 4);
    }

    @Test
    public void TestCase2() {
        assertEquals(2+2, 4);
    }

    @Test
    public void TestAa() {
        assertEquals("hello", "hi");
    }
}

MethodSorters.NAME_ASCENDING 设置按字母的顺序执行,所以,TestAa() 先被执行,虽然它在代码中是最后一条用例。

运行结果如下:

 (七)JUnit 断言方法

JUnit 断言方法

JUnit 所提供的断言方法:

方法说明
assertArrayEquals(expecteds, actuals)查看两个数组是否相等。
assertEquals(expected, actual)查看两个对象是否相等。类似于字符串比较使用的equals()方法。
assertNotEquals(first, second)查看两个对象是否不相等。
assertNull(object)查看对象是否为空。
assertNotNull(object)查看对象是否不为空。
assertSame(expected, actual)查看两个对象的引用是否相等。类似于使用“==”比较两个对象。
assertNotSame(unexpected, actual)查看两个对象的引用是否不相等。类似于使用“!=”比较两个对象。
assertTrue(condition)查看运行结果是否为true。
assertFalse(condition)查看运行结果是否为false。
assertThat(actual, matcher)查看实际值是否满足指定的条件。
fail()让测试失败。

例子

关于断言方法,我们前面用得最多的是 assertEquals ,用于断言两个对象是否相等。这里再介绍一个 assertTrue 的使用。

创建 AssertTest 测试类(包了含被测试方法):

import org.junit.*;
import static org.junit.Assert.*;


public class AssertTest {

    /**
     * 判断一个数是否为素数
     */
    public static Boolean Prime(int n) {
        for (int i = 2; i < Math.sqrt(n); i++) {
            if (n % i == 0) {
                return false;
            }
        }
        return true;
    }

    @Test
    public void testPrime(){
        int n = 7;
        assertTrue(AssertTest.Prime(n));
    }

}

Prime() 方法用于判断一个数是否为素数(只能被1和它本身整除的数),并返回 True 或 False ,在测试用例中通过 assertTrue来断言结果。

(八)JUnit 测试批量运行

前面测试用例的运行 主要针对单个测试类进行的,当然,在 IntelliJ IDEA 中也可以选择单个的方法执行。那如果我们想运行所有的用例的文件呢?

IntelliJ IDEA 中设置运行

设置

在 IntelliJ IDEA 中,菜单栏:Run菜单 –> Edit Configurations…选项

在 Junit 目录下,选择任意一个用例文件。

  • Test Kind : 选择用例的运行类型/级别。
  • packages : 选择用例运行的目录,即你的测试用例目录。

设置完成后,点击 “OK” 按钮。

运行

点击 IntelliJ IDEA 工具栏上的运行按钮,来运行 test 目录下的所有用例。

运行结果:

通过测试套件运行

这种方法引入一种 “测试套件” 的概念,JUnit 提供了一种批量运行测试类的方法,叫测试套件。

测试套件的写法需要遵循以下原则:

  1. 创建一个空类作为测试套件的入口;

  2. 使用注解 org.junit.runner.RunWith 和 org.junit.runners.Suite.SuitClasses 修饰这个空类。

  3. 将 org.junit.runners.Suite 作为参数传入给注解 RunWith,以提示 JUnit 为此类测试使用套件运行器执行。

  4. 将需要放入此测试套件的测试类组成数组作为注解 SuiteClasses 的参数。

  5. 保证这个空类使用public修饰,而且存在公开的不带任何参数的构造函数。

单独创建一个测试类 runAllTest .

package test;

import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;

@RunWith(Suite.class)
@SuiteClasses({
        CountTest.class,
        TestFixture.class,
        AssertTest.class,
        TestRunSequence.class,
})
public class runAllTest {

}

把需要运行的测试类放到 SuiteClasses 中,运行 runAllTest 测试类,即可批量执行测试用例。

(九)JUnit5 介绍与安装

官方网址:http://junit.org/junit5/

Junit5 已经不算是新的版本了,2016 年推出非正式版,相比较 JUnit4 安装和使用都有一定的差异。

JUnit5 介绍

The new major version of the programmer-friendly testing framework for Java 8

一个新的重要版本,程序员更友好的测试框架,基于 Java8。

关于

JUnit5 是 JUnit 的下一代。我们的目标是为 JVM 上的开发人员端测试创建一个最新的基础。这包括针对 Java 8 及以上,以及使许多不同风格的测试。

Junit5 组成

先看来个公式:

JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage

这看上去比 Junit4 复杂,实际上在导入包时也会复杂一些。

  • JUnit Platform 是在JVM上启动测试框架的基础。

  • JUnit Jupiter 是JUnit5扩展的新的编程模型和扩展模型,用来编写测试用例。Jupiter子项目为在平台上运行Jupiter的测试提供了一个TestEngine (测试引擎)。

  • JUnit Vintage 提供了一个在平台上运行 JUnit3 和 JUnit4 的 TestEngine 。

Maven 安装

首先,你需要通过 IntelliJ IDEA 创建一个 Maven 项目,IntelliJ IDEA 集成的有 Maven,所以,你很容易做到这一点。通过 Maven 的 pom.xml 文件,添加 Junit5 。

pom.xml 文件配置如下:

<dependencies>

    <dependency>
        <groupId>org.junit.platform</groupId>
        <artifactId>junit-platform-launcher</artifactId>
        <version>1.0.1</version>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-engine</artifactId>
        <version>5.0.1</version>
        <scope>test</scope>
    </dependency>
    
    <dependency>
        <groupId>org.junit.vintage</groupId>
        <artifactId>junit-vintage-engine</artifactId>
        <version>4.12.1</version>
        <scope>test</scope>
    </dependency>

</dependencies>

(十)JUnit5 创建测试

创建测试用例

我在 IntelliJ IDEA 中创建的 Maven 项目,目录结构如下:

在 test.java 目录下创建一个 FistJUnit5Tests 类。代码如下:

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

import org.junit.jupiter.api.Test;

class FirstJUnit5Tests {

    @Test
    void myFirstTest() {
        assertEquals(2, 1 + 1);
    }

}

明显看出和 Junit4 还是有些不同的。

首先,导入测试测试注解(@Test)和断言方法(assertEquals)的路径不同。

其次,不需要手动把测试和测试方法声明为 public 了。

(十一)JUnit5 新的用法

创建 JUnit5NewTests 测试类。

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertAll;

import org.junit.jupiter.api.*;

class JUnit5NewTests {

    @BeforeEach
    @DisplayName("每条用例开始时执行")
    void start(){

    }

    @AfterEach
    @DisplayName("每条用例结束时执行")
    void end(){

    }

    @Test
    void myFirstTest() {
        assertEquals(2, 1 + 1);
    }

    @Test
    @DisplayName("描述测试用例╯°□°)╯")
    void testWithDisplayName() {

    }

    @Test
    @Disabled("这条用例暂时跑不过,忽略!")
    void myFailTest(){
        assertEquals(1,2);
    }

    @Test
    @DisplayName("运行一组断言")
    public void assertAllCase() {
        assertAll("groupAssert",
                () -> assertEquals(2, 1 + 1),
                () -> assertTrue(1 > 0)
        );
    }

    @Test
    @DisplayName("依赖注入1")
    public void testInfo(final TestInfo testInfo) {
        System.out.println(testInfo.getDisplayName());
    }

    @Test
    @DisplayName("依赖注入2")
    public void testReporter(final TestReporter testReporter) {
        testReporter.publishEntry("name", "Alex");
    }

}

用法都已经通过测试用例的 @DisplayName 进行了说明,这里不再解释。

运行结果如下:

(十二)补充:JUnit 注解之Rule

一个JUnit Rule就是一个实现了TestRule的类,这些类的作用类似于 @Before@After,是用来在每个测试方法的执行前后执行一些代码的一个方法。 那为什么不直接用这些 @Before@After呢?这是因为它们都只能作用于一个类,如果同一个setup需要在两个类里面同时使用,那么你就要在两个测试类里面定义相同的@Before方法,然后里面写相同的代码,这就造成了代码重复。

此外,JUnit Rule还能做一些 @Before@After这些注解做不到的事情,那就是他们可以动态的获取将要运行的测试类、测试方法的信息。

使用框架自带的Rule

除了增加Rule特性,新版JUnit还添加了很多核心Rule

  • TemporaryFolder:测试可以创建文件与目录并且会在测试运行结束后将其删除。这对于那些与文件系统打交道且独立运行的测试来说很有用。
  • ExternalResource:这是一种资源使用模式,它会提前建立好资源并且会在测试结束后将其销毁。这对于那些使用socket、嵌入式服务器等资源的测试来说很有用。
  • ErrorCollector:可以让测试在失败后继续运行并在测试结束时报告所有错误。这对于那些需要验证大量独立条件的测试来说很有用(尽管这本身可能是个“test smell”)。
  • ExpectedException:可以在测试中指定期望的异常类型与消息。
  • Timeout:为类中的所有测试应用相同的超时时间。

例如,TimeOut这个Rule的使用。

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.Timeout;


public class RuleTestDemo {

    //使用Timeout这个Rule
    @Rule
    public Timeout timeout = new Timeout(1000);  

    @Test
    public void testMethod1() throws Exception {
        Thread.sleep(1001);
    }

    @Test
    public void testMethod2() throws Exception {
        Thread.sleep(999);
    }
}

使用JUnit所提供的Timeout类,该类用于控制测试用例的执行超时时间。这里设置为1秒,当用例执行超过1秒则失败。接下来分别在 testMethod1和testMethod2两个用例中使用sleep()方法来控制用例的执行时间,显然testMethod1超过1秒,则运行失败。

自定义的Rule

除了可以使用JUnit框架自带的Rule,还可以根据自己的需求自定义Rule。简单来说,自定义一个Rule就是implement一个TestRule 接口,并实现apply()方法。该方法需要返回一个Statement对象。例子如下:

import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;


public class MethodNameRule implements TestRule {

    public Statement apply(final Statement base, final Description description) {
        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                //在测试方法运行之前做一些事情,在base.evaluate()之前
                String className = description.getClassName();
                String methodName = description.getMethodName();

                base.evaluate();  //运行测试方法

                //在测试方法运行之后做一些事情,在base.evaluate()之后
                System.out.println("Class name:"+className+", method name: "+methodName);
            }
        };
    }
}

这里实现的功能是在每次测试用例运行之后,打印当前测试用例的类名和方法名。 在上面的例子中添加这里定义的MethodNameRule 。

……
public class RuleTestDemo {

    //使用Timeout这个Rule
    @Rule
    public Timeout timeout = new Timeout(1000);  

    //使用自定义Rule,
    @Rule
    public MethodNameRule methodNameRule = new MethodNameRule();

……

再次运行测试用例,执行结果如下:

 

  • 53
    点赞
  • 242
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值