目录
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:该匹配器包装现有的匹配器,但反转匹配的逻辑。
- allOf:所有匹配器都匹配才匹配,相当于
字符串匹配符
:- 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(参数化)的测试运行器允许使用不同的参数多次运行同一个测试。
要使用参数化的测试运行器来运行一个测试类,那就需要满足一下要求:
- 测试类必须使用
@RunWith
注解,并且要将Parameterized.class
作为它的参数 - 必须声明测试中所使用的所有实例变量,用于存放期望值和测试用数据
- 为测试类声明一个使用注解
org.junit.runners.Parameterized.Parameters
修饰的,返回值为java.util.Collection
的公共静态方法
,并在此方法中初始化所有需要测试的参数对。 - 为测试类声明一个
带有参数的公共构造函数
,并在其中为第二个环节中声明的几个变量赋值 - 编写测试方法,使用定义的变量作为参数进行测试
被测试的方法:
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 即可查看测试覆盖率了。左侧的绿色条代表方法中的每条语句都测试到了,红色代表完全没有测试,黄色代表部分语句测试到。