JUnit4.x详解

JUnit4.x

1、概述

1.1、junit框架

JUnit是一个Java测试框架,使编写可靠和高效的测试变得容易。它可以用于大多数语言制作的应用程序,但特别适合于测试Java应用程序。JUnit也可以用来创建自动测试。

JUnit框架是最流行的Java测试框架之一。它提供了一些功能,使编写测试变得容易,包括支持多个测试用例、断言和报告。JUnit也是多功能的,允许用各种语言编写测试。

它允许你有效地创建和运行测试,并已成为最流行的Java测试框架之一。xUnit框架激发了Smalltalk和C++的JUnit。由于JUnit是xUnit测试框架家族的成员,它被设计为支持不同的测试,包括单元、功能和集成测试。

JUnit主要用于单元测试,但它也可用于其他测试,如功能和集成测试。功能测试是测试系统的功能。它们与单元测试不同,因为它们测试的是整个系统而不是单个单元。集成测试测试两个或多个系统的集成。它们与单元测试不同,因为它们测试的是系统的各个组成部分如何协同工作,而不是单独工作。

1.2、junit核心

  • Assert:断言,断言是用来验证系统的预期行为的。JUnit提供了一套断言方法,可用于检查测试结果。
  • 测试:一个@Test注解的方法定义了一个测试,为了运行这个方法,JUnit会创建一个包含类的新实例,然后再调用这个被注释的方法。
  • 测试类:一个测试类是@Test方法的容器。
  • Suite:测试套件,测试套件是用来分组相关的测试。JUnit提供了一种方法来创建可以一起运行的测试套件。
  • Runner:测试运行器,通过@RunWith注解指定。

1.3、依赖

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

2、JUnit注解

2.1、常用注解

  • @Test:测试注解,标记一个方法可以是测试用例。
    • expected:指定一个期望的异常
    • timeout:指定一个超时时间,单位毫秒
  • @Before:Before注解表示,该方法必须在类中的每个测试之前执行,以便执行某些必要的先决条件。每个测试方法都会运行一次。
  • @BeforeClass:BeforeClass注解指出这是附着在静态方法必须执行一次并在类的所有测试之前,这种情况一般用于测试计算、共享配制方法(如数据库连接)。每个测试类只运行一次。
  • @After:After注释表示,该方法在每项测试后执行(如执行每一个测试后重置某些变量,删除临时变量等)。每个测试方法都会运行一次。
  • @AfterClass:当需要执行所有测试在JUnit测试用例类后执行,AlterClass注解可以使用以清理一些资源(如数据库连接),注意:方法必须为静态方法。每个测试类只运行一次。
  • @Ignore:当想暂时禁用特定的测试执行可以使用这个注解,每个被注解为@Ignore的方法将不再执行。
  • @Runwith:放在测试类名之前,用来确定这个类怎么运行的。也可以不标注,会使用默认运行器。
  • @SuiteClasses:用于套件测试。
  • @Parameters:用于使用参数化功能。
  • @FixMethodOrder:指定测试类中测试方法的执行顺序。
  • @Rule:指定方法级测试规则。
  • @ClassRule:指定类级别测试规则

2.2、执行顺序

public class JunitTest {

    /**
     * 只执行一次
     */
    @BeforeClass
    public static void beforeClass() {
        System.out.println("before class");
    }

    /**
     * 只执行一次
     */
    @AfterClass
    public static void afterClass() {
        System.out.println("after class");
    }

    /**
     * 有多少个@Test就执行几次
     */
    @Before
    public void before() {
        System.out.println("before");
    }

    /**
     * 有多少个@Test就执行几次
     */
    @After
    public void after() {
        System.out.println("after");
    }

    @Test
    public void test1() {
        System.out.println("test1");
    }

    @Test
    public void test2() {
        System.out.println("test2");
    }
}

输出:

before class
before
test1
after
before
test2
after
after class

3、断言

3.1、断言方法

JUnit的Assert类提供了一系列断言方法。

  • 断言一个条件为真
    • static public void assertTrue(String message, boolean condition)
    • static public void assertTrue(boolean condition)
  • 断言一个条件为假
    • static public void assertFalse(String message, boolean condition)
    • static public void assertFalse(boolean condition)
  • 断言测试失败,抛出AssertionError
    • static public void fail(String message)
    • static public void fail()
  • 断言两个值相等:值类型可能是int,short,long,byte,char,Object,第一个参数是一个可选字符串消息
    • static public void assertEquals(String message, Object expected, Object actual)
    • static public void assertEquals(Object expected, Object actual)
    • static public void assertEquals(String message, double expected, double actual, double delta)
    • static public void assertEquals(String message, float expected, float actual, float delta)
    • static public void assertNotEquals(String message, float unexpected, float actual, float delta)
    • static public void assertEquals(long expected, long actual)
    • static public void assertEquals(String message, long expected, long actual)
    • static public void assertEquals(double expected, double actual, double delta)
    • static public void assertEquals(float expected, float actual, float delta)
  • 断言两个值不相等
    • static public void assertNotEquals(String message, Object unexpected, Object actual)
    • static public void assertNotEquals(Object unexpected, Object actual)
    • static public void assertNotEquals(String message, long unexpected, long actual)
    • static public void assertNotEquals(long unexpected, long actual)
    • static public void assertNotEquals(String message, double unexpected, double actual, double delta)
    • static public void assertNotEquals(double unexpected, double actual, double delta)
    • static public void assertNotEquals(float unexpected, float actual, float delta)
  • 断言预期数组和结果数组相等:数组类型可能是int,short,long,byte,char,Object
    • public static void assertArrayEquals(String message, Object[] expecteds, Object[] actuals) throws ArrayComparisonFailure
    • public static void assertArrayEquals(Object[] expecteds, Object[] actuals)
    • public static void assertArrayEquals(String message, boolean[] expecteds, boolean[] actuals) throws ArrayComparisonFailure
    • public static void assertArrayEquals(boolean[] expecteds, boolean[] actuals)
    • public static void assertArrayEquals(String message, byte[] expecteds, byte[] actuals) throws ArrayComparisonFailure
    • public static void assertArrayEquals(byte[] expecteds, byte[] actuals)
    • public static void assertArrayEquals(String message, char[] expecteds, char[] actuals) throws ArrayComparisonFailure
    • public static void assertArrayEquals(char[] expecteds, char[] actuals)
    • public static void assertArrayEquals(String message, short[] expecteds, short[] actuals) throws ArrayComparisonFailure
    • public static void assertArrayEquals(short[] expecteds, short[] actuals)
    • public static void assertArrayEquals(String message, int[] expecteds, int[] actuals) throws ArrayComparisonFailure
    • public static void assertArrayEquals(int[] expecteds, int[] actuals)
    • public static void assertArrayEquals(String message, long[] expecteds, long[] actuals) throws ArrayComparisonFailure
    • public static void assertArrayEquals(long[] expecteds, long[] actuals)
    • public static void assertArrayEquals(String message, double[] expecteds, double[] actuals, double delta) throws ArrayComparisonFailure
    • public static void assertArrayEquals(double[] expecteds, double[] actuals, double delta)
    • public static void assertArrayEquals(String message, float[] expecteds, float[] actuals, float delta) throws ArrayComparisonFailure
    • public static void assertArrayEquals(float[] expecteds, float[] actuals, float delta)
  • 断言一个对象不为空(null)
    • static public void assertNotNull(String message, Object object)
    • static public void assertNotNull(Object object)
  • 断言一个对象为空(null)
    • static public void assertNull(String message, Object object)
    • static public void assertNull(Object object)
  • 断言两个对象引用相同的对象
    • static public void assertSame(String message, Object expected, Object actual)
    • static public void assertSame(Object expected, Object actual)
  • 断言两个对象不是引用同一个对象
    • static public void assertNotSame(String message, Object unexpected, Object actual)
    • static public void assertNotSame(Object unexpected, Object actual)
  • 配合Hamcrest匹配器断言
    • public static <T> void assertThat(T actual, Matcher<? super T> matcher)
    • public static <T> void assertThat(String reason, T actual, Matcher<? super T> matcher)

3.2、断言示例

import org.hamcrest.CoreMatchers;
import org.junit.Test;
import static org.junit.Assert.*;

/**
 * @Author: acton_zhang
 * @Date: 2024/1/3 9:37 下午
 * @Version 1.0
 */
public class AssertionsTest {

    @Test
    public void testAssert() {
        //断言一个条件为真
        assertTrue(2 > 1);

        //断言一个条件为假
        assertFalse(1 > 2);

        //断言两个值相等,equals
        assertEquals(1, 1);
        assertEquals(2.2, 2.2, 0);
        assertEquals("hello", "hello");

        //断言两个值不相等
        assertNotEquals(1, 2);
        assertNotEquals(2.2, 1.1);
        assertNotEquals("hello", "world");

        //断言两个数组相等
        assertArrayEquals(new int[]{1, 2, 3}, new int[]{1, 2, 3});
        assertArrayEquals(new String[]{"a", "b", "c"}, new String[]{"a", "b", "c"});

        //断言一个对象不为空
        assertNotNull(new Object());

        //断言一个对象为空
        assertNull(null);

        //断言两个对象引用一致
        String a = "hello";
        String b = "hello";
        assertSame(a, b);

        //断言两个对象引用不一致
        Object aa = new Object();
        Object bb = new Object();
        assertNotSame(aa, bb);

        //Hamcrest匹配器
        assertThat(5.5, CoreMatchers.equalTo(5.5));
    }
}

4、Hamcrest

Hamcrest是一个库,包含了大量有用的匹配器对象(也称为约束或者谓语),它可以被植入到开发语言中(Java、C++等)。

4.1、匹配符

目前,JUnit提供的匹配符,定义在两个类里面:

  • org.junit.matchers.JUnitMatchers
  • org.hamcrest.CoreMatchers

JUnitMatchers提供的匹配符有两个:

  • isThrowable(Matcher<T> throwableMatcher):将委托给throwableMatcher
  • isException(Matcher<T> exceptionMatcher):将委托给exceptionMatcher

CoreMathers提供的匹配符如下:

  • 一般匹配符
    • anything:总是匹配,如果你不关心测试下的对象是什么是有用的
    • equalTo:测试对象相等使用Object.equals方法
    • instanceOf:测试类型
    • nullValue:测试是否为null
    • notNullValue:测试是否不为null
    • sameInstance:测试是否为相同引用
    • theInstance:测试是否为相同引用
  • 逻辑匹配符:用于组合多个匹配符
    • allOf:所有匹配器都匹配才匹配,相当于&&
    • anyOf:任何匹配器匹配就匹配,相当于||
    • both:当两个指定的匹配器都匹配时,该匹配器就会匹配
    • either:当指定的匹配器中的任何一个匹配时,该匹配器就会匹配
    • not:该匹配器包装现有的匹配器,但反转匹配的逻辑。
  • 字符串匹配符
    • containsString:测试是否包含指定字符串
    • startsWith:测试是否以指定字符串开头
    • endsWith:测试是否以指定字符串结尾
  • 集合相关匹配符
    • everyItem:当集合中的每一项都与指定的匹配器匹配时,该匹配器就会匹配
    • hasItem:当集合中包含匹配器指定的项时,该匹配器匹配
    • hasItems:当集合中包含匹配器指定的多个项时,该匹配器匹配
  • 其他匹配符
    • describedAs:包装现有的匹配器,用指定的描述覆盖其描述
    • is:改进可读性的装饰匹配符
    • isA:改进可读性的装饰匹配符

4.2、示例

import org.junit.Test;

import java.io.Serializable;
import java.util.Arrays;

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

/**
 * @Author: acton_zhang
 * @Date: 2024/1/3 11:58 下午
 * @Version 1.0
 */
public class HamcrestTest {

    @Test
    public void hamcrestTest() {
        /**
         * 一般匹配符
         */
        //anything,总是匹配
        assertThat(1, anything());

        //equalTo,Object.equals
        assertThat("hello", equalTo("hello"));

        //instanceOf,测试类型
        assertThat("hello", instanceOf(String.class));
        //简写
        assertThat("hello", isA(String.class));

        //nullValue,是否为null
        assertThat(null, nullValue());

        //notNullValue,不为null
        assertThat(new Object(), notNullValue());

        //sameInstance,是否为相同引用
        assertThat("123", sameInstance("123"));

        //theInstance,是否为相同引用
        assertThat("123", theInstance("123"));

        /**
         * 逻辑匹配符
         */
        //allOf,相当于&&
        assertThat("hello", allOf(notNullValue(), isA(String.class), startsWith("h")));

        //anyOf,相当于||
        assertThat("hello", anyOf(nullValue(), isA(String.class)));

        //both,两个条件都匹配
        assertThat("hello", both(containsString("h")).and(containsString("o")));

        //either,两个条件匹配一个即可
        assertThat("fan", either(containsString("a")).or(containsString("b")));

        //not,取反
        assertThat(null, not(notNullValue()));
        assertThat(null, not(1));

        /**
         * 字符串匹配符
         */
        //containsString,包含
        assertThat("hello", containsString("ll"));

        //startsWith,以...开头
        assertThat("hello", startsWith("he"));

        //endsWith,以...结尾
        assertThat("hello", endsWith("lo"));

        /**
         * 集合相关匹配器
         */
        //everyItem,集合每一项都匹配
        assertThat(Arrays.asList("ab", "ac", "ad", "ae"), everyItem(containsString("a")));

        //hasItem,集合包含某一项,或集合中的某一项能匹配时,该匹配器匹配
        assertThat(Arrays.asList("ab", "ac", "ad", "ae"), hasItem("ab"));
        assertThat(Arrays.asList("ab", "ac", "ad", "ae"), hasItem(equalTo("ab")));

        //hasItems,集合包含某几个项,或集合中的某几个项能匹配时,该匹配器匹配
        assertThat(Arrays.asList("ab", "ac", "ad", "ae"), hasItems("ab", "ac"));
        assertThat(Arrays.asList("ab", "ac", "ad", "ae"), hasItems(equalTo("ab"), startsWith("ad")));

        /**
         * 其他匹配符
         */
        //describedAs,包装现有的匹配器,用指定的描述覆盖其描述
        assertThat(null, describedAs("is null", nullValue()));

        //is,改进可读性的装饰匹配符
        assertThat(1, is(1));
        assertThat(1, is(equalTo(1)));
        assertThat(1, is(instanceOf(Number.class)));

        //isA,改进可读性的装饰匹配符
        assertThat(1, isA(Number.class));
        assertThat(1, isA(Serializable.class));
    }
}

4.3、自定义匹配符

public class IsNotANumber extends TypeSafeMatcher<Double> {
    protected boolean matchesSafely(Double number) {
        return number.isNaN();
    }

    public void describeTo(Description description) {
        description.appendText("not a number");
    }

    @Factory
    public static <T> Matcher<Double> notANumber() {
        return new IsNotANumber();
    }
}
@Test
public void numberTest() {
    assertThat(Math.sqrt(-1), is(IsNotANumber.notANumber()));
}

5、Runner(运行器)

JUnit可以向后兼容3.8.x版本。所以JUnit4不仅要运行JUnit4的测试还要运行JUnit3.x版本的测试。所以JUnit提供了不同的运行器,分别用来运行JUnit3.x和JUnit4的测试以及其他不同的测试集。

  • org.junit.internal.runners.JUnit38ClassRunner:它将测试用例作为JUnit3.8的测试用例来启动。
  • org.junit.runners.BlockJUnit4ClassRunner:它作为默认的JUnit4的测试用例来启动。
  • org.junit.runners.Parameterized:这个运行器可以使用不同参数来运行相同的测试集。
  • org.junit.runners.Suite:Suite是一个包含不同测试的容器,同时Suite也是一个运行器,可以运行一个测试类中所以以@Test注释的方法。

可以通过@RunWith注解指定测试的运行器,例如SpringBoot提供的运行器集成了spring的一些功能。

@RunWith(SpringJUnit4ClassRunner.class)

另外为了统一运行器,JUnit提供了一个facade门面,即org.junit.runner.JUnitCore,它可以运行任何运行器,并收集测试结果与统计信息。

6、参数化测试

Parameterized(参数化)的测试运行器允许使用不同的参数多次运行同一个测试。

要使用参数化的测试运行器来运行一个测试类,那就需要满足一下要求:

  1. 测试类必须使用@RunWith注解,并且要将Parameterized.class作为它的参数
  2. 必须声明测试中所使用的所有实例变量,用于存放期望值和测试用数据
  3. 为测试类声明一个使用注解 org.junit.runners.Parameterized.Parameters 修饰的,返回值为 java.util.Collection公共静态方法,并在此方法中初始化所有需要测试的参数对。
  4. 为测试类声明一个带有参数的公共构造函数,并在其中为第二个环节中声明的几个变量赋值
  5. 编写测试方法,使用定义的变量作为参数进行测试

被测试的方法:

public class Caculator {

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

测试类:

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import static org.junit.Assert.*;

import java.util.Arrays;
import java.util.Collection;



//指定参数化运行器
@RunWith(value = Parameterized.class)
public class ParameterizedTest {

    //声明所需的实例变量
    private double expected;//期望的值
    private double valueA;//测试方法参数A
    private double valueB;//测试方法参数B

    //带参数的构造,为实例变量赋值
    public ParameterizedTest(double expected, double valueA, double valueB) {
        this.expected = expected;
        this.valueA = valueA;
        this.valueB = valueB;
    }

    //Parameters修饰的静态方法,返回Collection,内部是所需的参数数组
    @Parameterized.Parameters
    public static Collection<Double[]> getTestParameters() {
        return Arrays.asList(new Double[][]{
                {2.2, 1.1, 1.1},
                {5.5, 2.2, 3.3},
                {9.9, 3.3, 6.6}
        });
    }

    //测试方法
    @Test
    public void testAdd() {
        Caculator caculator = new Caculator();
        //浮点数计算,精度0.0000000001即可
        assertEquals(expected, caculator.add(valueA, valueB), 0.0000000001);
    }
}

7、套件测试

Suite(套件)测试可以组合多个测试类一次运行。Suite是一个容器,用来把几个测试归在一起,并把它们作为一个集合一起运行。

如果没有使用@SuiteClasses指定一个Suite,那么运行器会自动创建一个Suite。默认的Suite会扫描测试类,找出所有以@Test注释的方法。默认的Suite会在内部为每个@Test方法创建一个测试类的实例。然后JUnit就会独立地执行每个@Test方法,以避免潜在的负面影响。

Suite对象其实及时一个Runner,可以执行测试类中所有@Test注释的方法。

7.1、组合一组测试类

将多个测试类组成一个套件:

public class TestA {

    @Test
    public void test1() {
        System.out.println("testA.test1 run...");
    }

    @Test
    public void test2() {
        System.out.println("testA.test2 run...");
    }
}
public class TestB {

    @Test
    public void test1() {
        System.out.println("TestB.test1 run...");
    }

    @Test
    public void test2() {
        System.out.println("TestB.test2 run...");
    }
}
//指定套件测试运行器
@RunWith(Suite.class)
@Suite.SuiteClasses({
        //这里组合测试类,影响程序运行的顺序
        TestA.class,
        TestB.class
})
public class SuiteTest {
}

输出:

testA.test1 run...
testA.test2 run...
TestB.test1 run...
TestB.test2 run...

7.2、组合一组测试集

将多个测试套件组合成测试集:

public class TestC {
    @Test
    public void test1() {
        System.out.println("testC.test1 run...");
    }

    @Test
    public void test2() {
        System.out.println("testC.test2 run...");
    }
}
public class TestD {

    @Test
    public void test1() {
        System.out.println("testD.test1 run...");
    }

    @Test
    public void test2() {
        System.out.println("testD.test2 run...");
    }
}
//指定套件测试运行器
@RunWith(Suite.class)
@Suite.SuiteClasses({
        //这里组合测试类,影响程序运行的顺序
        TestC.class,
        TestD.class
})
public class SuiteTest2 {
}
//指定套件测试运行器
@RunWith(Suite.class)
@Suite.SuiteClasses({
        //这里组合测试类,影响程序运行的顺序
        SuiteTest.class,
        SuiteTest2.class
})
public class SuiteCollectionTest {
}

输出:

testA.test1 run...
testA.test2 run...
TestB.test1 run...
TestB.test2 run...
testC.test1 run...
testC.test2 run...
testD.test1 run...
testD.test2 run...

在这里插入图片描述

8、忽略测试

  • 一个带有@Ignore注解的测试方法不会被执行
  • 如果一个测试类带有@Ignore注解,则它的测试方法将不会被执行
public class TestB {

	//忽略该方法
    @Ignore
    @Test
    public void test1() {
        System.out.println("TestB.test1 run...");
    }

    @Test
    public void test2() {
        System.out.println("TestB.test2 run...");
    }
}
//忽略该类所有方法
@Ignore
public class TestC {

    @Test
    public void test1() {
        System.out.println("testC.test1 run...");
    }

    @Test
    public void test2() {
        System.out.println("testC.test2 run...");
    }
}

再次运行测试集,输出:

testA.test1 run...
testA.test2 run...

Test ignored.
TestB.test2 run...
testD.test1 run...
testD.test2 run...

Test ignored.

在这里插入图片描述

9、超时测试

Junit 4 提供了 @Test注解的timeout参数来测试任意特定方法的执行时间。如果测试方法的执行时间大于指定的超时参数,测试方法将抛出异常,测试结果为失败。指定的超时参数是以毫秒记。

public class TimeoutTest {

    //指定超时时间,单位毫秒
    @Test(timeout = 2000)
    public void test() throws InterruptedException {
        System.out.println("start...");
        Thread.sleep(3000);
        System.out.println("end...");
    }
}

输出:

start...

org.junit.runners.model.TestTimedOutException: test timed out after 2000 milliseconds

10、期望异常测试

Junit4 提供了@Test注解的expected参数来指定期望方法抛出的异常。如果测试方法没有抛出异常或异常不匹配,测试结果为失败。

public class ExceptionTest {

    @Test(expected = NullPointerException.class)
    public void test() {
        Object o = new Object();
        o = null;
        o.hashCode();
    }
}

11、优先级测试

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

  • MethodSorters.JVM:按照JVM返回的顺序保留测试方法。注意,JVM的顺序可能因不同的实现而不同
  • MethodSorters.DEFAULT:(默认的顺序)按确定但不可预测的顺序对测试方法进行排序
  • MethodSorters.NAME_ASCENDING:按方法名称字典顺序对测试方法进行排序
//方法名字典顺序执行
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class MethodSortTest {

    @Test
    public void bbb() {
        System.out.println("bbb");
    }

    @Test
    public void ccc() {
        System.out.println("ccc");
    }

    @Test
    public void aaa() {
        System.out.println("aaa");
    }

}

输出:

aaa
bbb
ccc

12、Rule

一个 Junit Rule(规则) 就是一个实现了org.junit.rules.TestRule 接口的类,这些类的作用类似于@Before @After注解,通过在类的执行前后运行指定的代码逻辑来增强测试,此外,Junit Rule还能做一些@Before @After注解实现不了的功能,如动态获取测试类、测试方法的信息

假设我们的测试用例需要在运行的时候连接外部资源(如数据库),在测试结束的时候释放连接,如果我们想在多个测试类中使用该数据库,那么最终将在每个测试类中重复该代码。通过使用 Rule,我们可以将重复的逻辑隔离在一个地方,并方便的在多个测试类中重用该代码。

Rule使用遵循以下步骤:

  • 在测试类中添加一个public的字段,该字段类型需是org.junit.rules.TestRule接口的实现类
  • 使用@Rule注解标记该字段

12.1、内置规则

1)、TemporaryFolder(临时文件/文件夹)

测试的时候,我们经常需要创建一个临时文件/文件夹,但是在测试类中管理这些文件/文件夹有时候会很麻烦,这时使用TemporaryFolderRule就能很好的帮我们管理测试过程中的文件的创建与删除:

public class TemporaryFolderRuleTest {

    //定义TemporaryFolder规则
    @Rule
    public TemporaryFolder temporaryFolder = new TemporaryFolder();

    @Test
    public void test() throws IOException {
        //创建一个临时文件a.txt
        File file = temporaryFolder.newFile("a.txt");
        //断言
        assertTrue(file.isFile());
        assertEquals(temporaryFolder.getRoot(), file.getParentFile());
        //测试完成之后,TemporaryFolder会自动删除临时文件夹和文件(但不校验删除是否成功)
    }
}

其它常用方法:

  • newFile():创建一个随机名称的文件
  • newFolder(String… folderNames):根据多级目录文件夹,如newFolder(“a”,“b”)创建文件夹a/b
  • newFolder():创建一个随机名称的文件夹
2)、ExpectedException(异常)

ExpectedException可以用来校验代码即将发生的异常:

public class ExpectedExceptionRuleTest {

    //定义ExpectedException规则
    @Rule
    public ExpectedException thrown = ExpectedException.none();

    @Test
    public void test() {
        //接下来代码会抛出IllegalArgumentException
        thrown.expect(IllegalArgumentException.class);
        //且case是NullPointerException
        thrown.expectCause(isA(NullPointerException.class));
        //且message是this is a exception
        thrown.expectMessage("this is a exception");

        //抛出异常
        throw new IllegalArgumentException("this is a exception", new NullPointerException());
    }
}
3)、TestName(测试名称)

TestName可以很方便的让我们获取当前执行的测试方法的名称:

public class TestNameRuleTest {

    //定义TestName规则
    @Rule
    public TestName name = new TestName();

    @Test
    public void test() {
        String methodName = name.getMethodName();
        assertEquals("test", methodName);
    }
}
4)、Timeout(超时)

对于添加了TimeoutRule 的测试类,当测试类中的测试方法执行超过TimeoutRule 配置的时间时,测试方法执行就会被标记为失败:

public class TimeoutRuleTest {

    //定义Timeout规则
    @Rule
    public Timeout timeout = Timeout.seconds(5);

    @Test
    public void test() throws InterruptedException {
        TimeUnit.SECONDS.sleep(10);
    }
}
5)、ErrorCollector(收集Error)

顾名思义,ErrorCollector Rule就是用来收集Error的,正常情况下,当测试方法抛异常的时候,方法执行会中断。使用ErrorCollector可以把异常收集起来,让测试用例继续执行,待执行用例跑完之后再将异常信息报告出来。

public class ErrorCollectorRuleTest {

    //定义ErrorCollector规则
    @Rule
    public final ErrorCollector errorCollector = new ErrorCollector();

    @Test
    public void test() {
        errorCollector.addError(new Throwable("First thing went wrong!"));
        errorCollector.addError(new Throwable("Second thing went wrong!"));
        errorCollector.checkThat("Hello World", not(containsString("ERROR!")));
        System.out.println("run finished");
    }
}

输出:

run finished

java.lang.Throwable: First thing went wrong!
...
java.lang.Throwable: Second thing went wrong!
...
Process finished with exit code 255

从执行结果可以看出,程序先打印run finished再输出错误信息。说明Errorcollector暂时先讲错误信息给收集起来,待测试用例执行完再将报错信息抛出,整个测试用例的执行结果为 fail。

6)、Verifier(自定义)

Verifier是一个抽象类,当我们需要在测试用例中验证一些额外的行为的时候,可以使用这个类并重写该类的verify方法。事实上,有些Rule就是通过继承Verifier类实现的。

public class VerifierRuleTest {

    private List messageLog = new ArrayList();

    @Rule
    public Verifier verifier = new Verifier() {
        @Override
        protected void verify() throws Throwable {
            //在每个测试用例执行完的时候额外验证一下messageLog是否为空
            assertFalse("Message Log is not Empty!", messageLog.isEmpty());
        }
    };

    @Test
    public void successTest() {
        //该用例成功
        messageLog.add("I love java");
    }

    @Test
    public void failTest() {
        //该用例失败
        System.out.println("I don't like java");
    }
}
7)、DisableOnDebug(屏蔽其它Rule)

有时候我们希望在 debug 的时候 disable 掉某些 Rule,比如我们的测试代码中定义了一个Timeout Rule,但 debug 的时候往往会超出 Timeout Rule 设定的超时时间,这时候就可以使用 DisableOnDebug Rule 将 Timeout Rule 给 disable 掉,这样当我们通过 debug 模式执行测试用例时,Timeout Rule 就不会生效。

public class DisableOnDebugRuleTest {

    //定义DisableOnDebug规则,屏蔽掉超时规则
    @Rule
    public DisableOnDebug disableTimeout = new DisableOnDebug(Timeout.seconds(5));

    //debug模式可以通过测试
    //直接run会失败
    @Test
    public void test() throws InterruptedException {
        TimeUnit.SECONDS.sleep(10);
    }
}
8)、ExternalResource(外部资源)

当做集成测试的时候,有可能需要在执行前准备一些数据(可以是一个文件/数据库连接),并在执行完将准备的数据进行删除。这时候就可以用 ExternalResource Rule 实现,前面所讲的 TemporaryFolder Rule 正式通过集成 ExternalResource 实现的,详情可以参考 TemporaryFolder 源码。

12.2、@ClassRule

如果想让 Rule 作用于 class 级别,可以使用@ClassRule注解(类似于@Before、@After跟@BeforeClass、@AfterClass的区别)。

public class UsesExternalResource {
    
    //ClassRule必须标注在public static 字段上
    @ClassRule
    public static ExternalResource resource = new ExternalResource() {
        //所有测试运行之前运行
        @Override
        protected void before() {
            System.out.println("before");
        }

        //所有测试运行之后运行
        @Override
        protected void after() {
            System.out.println("after");
        }
    };


    @Test
    public void test1() {
        System.out.println("test1");
    }

    @Test
    public void test2() {
        System.out.println("test2");
    }
}

输出:

before
test1
test2
after

12.3、自定义Rule

除了使用junit提供的默认Rule外,还可以自定义我们自己的Rule, 自定义Rule需要继承TestRule接口,并实现其apply方法。

public class CustomerRuleTest {

    @Rule
    public TestMethodNameLogger testMethodNameLogger = new TestMethodNameLogger();

    @Test
    public void test() {
        System.out.println("run case");
    }


    static class TestMethodNameLogger implements TestRule {

        /**
         *
         * @param statement 表示测试方法执行过程中的 runtime
         * @param description 可以获取到当前正在执行的测试用例的一些信息(类名,方法名等
         * @return
         */
        @Override
        public Statement apply(Statement statement, Description description) {
            return new Statement() {
                @Override
                public void evaluate() throws Throwable {
                    System.out.println("before test,class name is " + description.getClassName());
                    //执行测试方法
                    statement.evaluate();
                    System.out.println("after test,class name is " + description.getClassName());
                }
            };
        }
    }
}

输出:

before test,class name is pers.zhang.rules.CustomerRuleTest
run case
after test,class name is pers.zhang.rules.CustomerRuleTest

12.4、Rule链

有多个Rule的时候,可以通过RuleChain来控制多个Rule的执行顺序,类似于过滤器链(FilterChain)。

public class RuleChainsTest {

    @Rule
    public RuleChain ruleChain = RuleChain
            .outerRule(new SimpleMsgLogger("First Rule"))
            .around(new SimpleMsgLogger("Second Rule"))
            .around(new SimpleMsgLogger("Third Rule"));

    @Test
    public void test() {
        System.out.println("run case");
    }

    static class SimpleMsgLogger implements TestRule {

        private String log;

        public SimpleMsgLogger(String log) {
            this.log = log;
        }

        @Override
        public Statement apply(Statement base, Description description) {
            return new Statement() {
                @Override
                public void evaluate() throws Throwable {
                    System.out.println("Starting:" + log);
                    base.evaluate();
                    System.out.println("Finishing:" + log);
                }
            };
        }
    }
}

输出:

Starting:First Rule
Starting:Second Rule
Starting:Third Rule
run case
Finishing:Third Rule
Finishing:Second Rule
Finishing:First Rule

12.5、Rule 使用场景示例

写过 spring 项目的都知道,当我们想要在测试类中使用 spring 容器,需要在测试类上添加 @RunWith(SpringRunner.class) 注解,但此时如果你又想用 junit 的参数化测试的话,就会有冲突产生,因为 junit 参数化测试需要在测试类上添加 @RunWith(Parameterized.class) 注解

我们知道,一个测试类不能同时添加两个 @RunWith 注解,对于这种情况,就可以使用 junit Rule 解决。

可以使用 @SpringClassRule @SpringMethodRule 这两个注解来替代 @RunWith(SpringRunner.class) 启动 spring 容器。

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = WiremockApplication.class)
public class RuleTest {
    @ClassRule
    public static SpringClassRule springClassRule = new SpringClassRule();
    @Rule
    public SpringMethodRule springMethodRule = new SpringMethodRule();
    @Autowired
    public SampleController sampleController;

    @Test
    public void test() {
        sampleController.hello();
    }

}

13、IDEA中的JUnit

13.1、IDEA 的 JUnit 配置

IDEA 一般默认安装了插件 JUnit,如下图所示:可在 settings 中的 Plugins 选项卡中的 Installed 一栏中搜索 JUnit 查看。

在这里插入图片描述

13.2、IDEA生成测试类

首先,在项目根目录下新建 Test 文件夹(或其他任意你喜欢的名字),并在 Project Structure 中标记为 Test(默认为 Source),用以保存生成的测试类。标记后,自动生成的测试类都会自动保存在此文件夹下。

在这里插入图片描述

在需要测试的类上右键 -> Go To -> Test,选择 “create new test…”,打开生成界面。

在这里插入图片描述

在生成界面中选择 Testing Library 为 JUnit 4(默认为 Arquillian JUnit 4),同时勾选需要测试的函数,然后点击 OK 即可生成。(可以自行指定生成的包,@Before,@After方法等)
在这里插入图片描述

在这里插入图片描述

之后我们可以看到 Test 文件夹下已经有了生成好的测试类。

package pers.zhang.junit;

import org.junit.Test;

import static org.junit.Assert.*;


public class CaculatorTest {

    @Test
    public void add() {
    }
}

13.3、测试覆盖率查看

待测试方法:

public class Caculator {

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

测试类:

public class CaculatorTest {

    @Test
    public void add() {
        Caculator caculator = new Caculator();
        assertEquals(2.2, caculator.add(1.1, 1.1), 0);
    }
}

选择 Edit Configuration 以设置 Code Coverage 检查。

在弹出的窗口中左侧选择 JUnit 对应的运行模块,然后选择 Code Coverage 选项卡,之后选择下方的 Tracing(默认为 Sampling),接着点击右侧的添加期望检查的类。

在这里插入图片描述

之后选择 Run “测试类名” with Coverage 即可查看测试覆盖率了。左侧的绿色条代表方法中的每条语句都测试到了,红色代表完全没有测试,黄色代表部分语句测试到。

在这里插入图片描述

  • 29
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
java.lang.IllegalStateException: Failed to load ApplicationContext at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:132) at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:123) at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:118) at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83) at org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener.prepareTestInstance(SpringBootDependencyInjectionTestExecutionListener.java:43) at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:244) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:227) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:289) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:291) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:246) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97) at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329) at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293) at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBefor
05-31

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值