一、概述
当一个类被@RunWith
注释或拓展了一个@RunWith
注释的类,JUnit将会使用引用的类来执行测试,而不是使用JUnit内置的运行器。
- JUnit4的默认运行器(runner)是
BlockJUnit4ClassRunner
,其取代了原来的JUnit4ClassRunner
运行器。 - Annotating a class with
@RunWith(JUnit4.class)
will always invoke the default JUnit 4 runner in the current version of JUnit, this class aliases the current default JUnit 4 class runner. - 用
@RunWith(JUnit4.class)
注释类总是会在当前版本的JUnit中调用默认的JUnit 4运行器,这个类是当前默认的JUnit 4类运行器的别名。
Specialized[专业的] Runners:
Suite
:Suite【测试集】是一个标准的运行器允许你手动构件包含来自许多类的测试集。Parameterized
:Parameterized【参数化】是一个标准的运行器来实现参数化测试。运行参数化测试类时,测试方法和测试数据进行合并来创建测试实例。Categories
:你可以使用Categories运行器来制定一组测试被包含或排除。
Third Party Runners:
SpringJUnit4ClassRunner
- MockitoJUnitRunner
- HierarchicalContextRunner
- Avh4’s Nested
- NitorCreation’s NestedRunner
二、使用Suite进行打包测试
使用Suite
运行器允许你手动创建一个包含多个类的测试集。使用@RunWith(Suite.class)
和 @SuiteClasses(TestClass1.class, ...)
来注释类。然后你可以运行这个类,其会运行 @SuiteClasses
中指定的所有类。
@RunWith
注解指定使用 org.junit.runners.Suite
运行器进行运行测试类。@Suite.SuiteClasses
告诉测试运行器 Suite
该测试包含哪些测试类。
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
@RunWith(Suite.class)
@Suite.SuiteClasses({
TestFeatureLogin.class,
TestFeatureLogout.class,
TestFeatureNavigate.class,
TestFeatureUpdate.class
})
public class FeatureTestSuite {
// the class remains empty,
// used only as a holder for the above annotations
}
注意:我们可以直接使用 @SuiteClasses
注解来取代 @Suite.SuiteClasses
注解。
三、使用Parameterized进行参数化测试
定制的Parameterized
运行器实现了参数化测试。在运行参数化测试类时,将测试方法和测试数据进行合并进而创建实例。
例如,我们来测试一个Fibonacci【斐波纳契】函数,如下:
public class Fibonacci {
public static int compute(int n) {
int result = 0;
if (n <= 1) {
result = n;
} else {
result = compute(n - 1) + compute(n - 2);
}
return result;
}
}
import static org.junit.Assert.assertEquals;
import java.util.Arrays;
import java.util.Collection;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class FibonacciTest {
@Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] {
{ 0, 0 }, { 1, 1 }, { 2, 1 }, { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 }
});
}
private int fInput;
private int fExpected;
public FibonacciTest(int input, int expected) {
fInput= input;
fExpected= expected;
}
@Test
public void test() {
assertEquals(fExpected, Fibonacci.compute(fInput));
}
}
每一个FibonacciTest
实例使用两个参数进行构造,而数据值来自@Parameters
指定的方法。
使用@Parameter
进行字段注入来取代构造器
也可以使用@Parameter
注解将数据值直接注入到字段中,而不需要构造函数,示例如下:
import static org.junit.Assert.assertEquals;
import java.util.Arrays;
import java.util.Collection;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class FibonacciTest {
@Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] {
{ 0, 0 }, { 1, 1 }, { 2, 1 }, { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 }
});
}
@Parameter // first data value (0) is default
public /* NOT private */ int fInput;
@Parameter(1)
public /* NOT private */ int fExpected;
@Test
public void test() {
assertEquals(fExpected, Fibonacci.compute(fInput));
}
}
public class Fibonacci {
...
}
单参数测试(Since 4.12-beta-3)
如果你的测试只需要一个参数,则不需要用数组包装它。相反,您可以提供一个Iterable或数组对象。
@Parameters
public static Iterable<? extends Object> data() {
return Arrays.asList("first test", "second test");
}
或
@Parameters
public static Object[] data() {
return new Object[] { "first test", "second test" };
}
识别单个测试用例
为了方便地识别参数化测试中的单个测试用例,您可以使用@Parameters
注解并提供一个名称。此名称允许包含在运行时替换的占位符:
{index}
: 当前参数索引{0}, {1}, …
: the first, second, and so on, parameter value. NOTE: 单引号'
应该转义为两个单引号''
。
示例:
import static org.junit.Assert.assertEquals;
import java.util.Arrays;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class FibonacciTest {
@Parameters(name = "{index}: fib({0})={1}")
public static Iterable<Object[]> data() {
return Arrays.asList(new Object[][] {
{ 0, 0 }, { 1, 1 }, { 2, 1 }, { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 }
});
}
private int input;
private int expected;
public FibonacciTest(int input, int expected) {
this.input = input;
this.expected = expected;
}
@Test
public void test() {
assertEquals(expected, Fibonacci.compute(input));
}
}
public class Fibonacci {
...
}
在上面给出的示例中,参数化运行程序创建的名称类似于[1: fib(3)=2]。如果不指定名称,默认情况下将使用当前参数索引。
四、使用Categories进行分类测试
从给定的一组测试类中,Categories
运行器只运行@IncludeClass
注解中指定的类别的类和方法。类或接口都可以用作类别【categories】,因此也具有继承关系。如果@IncludeClass
指定了@Category({SubClass.class})
,那么其所包含的@Category({SubClass.class})
子类别也将会运行。
还可以通过使用@ExcludeCategory
注解来排除类别,示例:
public interface FastTests { /* category marker */ }
public interface SlowTests { /* category marker */ }
public class A {
@Test
public void a() {
fail();
}
@Category(SlowTests.class)
@Test
public void b() {
}
}
@Category({SlowTests.class, FastTests.class})
public class B {
@Test
public void c() {
}
}
@RunWith(Categories.class)
@IncludeCategory(SlowTests.class)
@SuiteClasses( { A.class, B.class }) // Note that Categories is a kind of Suite
public class SlowTestSuite {
// Will run A.b and B.c, but not A.a
}
@RunWith(Categories.class)
@IncludeCategory(SlowTests.class)
@ExcludeCategory(FastTests.class)
@SuiteClasses( { A.class, B.class }) // Note that Categories is a kind of Suite
public class SlowTestSuite {
// Will run A.b, but not A.a or B.c
}
Maven中使用categories
你可以通过[maven-surefire-plugin][surefire] (用于单元测试) 或 [maven-failsafe-plugin][failsafe] (用于集成测试)插件来使用类别【Categories】。使用任何一个插件,你可以配置一个包含或排除的类别【Categories】列表。在不使用任何一种选项的情况下,所有测试都将在默认情况下执行。
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<groups>com.example.FastTests,com.example.RegressionTests</groups>
</configuration>
</plugin>
</plugins>
</build>
类似地,要排除某个类别,您可以使用<excludedGroups/>
配置元素。
Gradle中使用categories
Gradle的测试任务允许指定要包含和排除的JUnit类别。
test {
useJUnit {
includeCategories 'org.gradle.junit.CategoryA'
excludeCategories 'org.gradle.junit.CategoryB'
}
}
类别的典型用法
Categories are used to add metadata on the tests.
The frequently encountered categories usages are about:
- The type of automated tests: UnitTests, IntegrationTests, SmokeTests, RegressionTests, PerformanceTests …?
- How quick the tests execute: SlowTests, QuickTests * In which part of the ci build the tests should be executed: NightlyBuildTests
- The state of the test: UnstableTests, InProgressTests
This is also used to add project specific metadata like which feature of a project is covered by the test.
See usages of Junit Categories on github hosted projects
参考资料: