文章目录
JUnit
JUnit 是一个编写可重复测试的简单框架。它是单元测试框架的XUnit体系结构的一个实例。写好单元测试,也是程序员的一项重要开发技能和对项目开发的负责。
JUnit 4 : https://junit.org/junit4/
JUnit 5: https://junit.org/junit5/
集成
Maven
除了JUnit 的基本包,还可以引入一些第三方 jar 包,如匹配器 hamcrest-library 、模拟测试 mockito-core 等来有效编写单元测试。
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
使用方法
断言
JUnit 为所有的基本数据类型、对象和数组提供重载断言方法。第一个可选的参数为当断言失败时输出的提示信息。断言参数值顺序依次为期望值,实际值。
Assert 类中常用的方法:
public class AssertTests {
@Test
public void testAssertEquals() {
Assert.assertEquals("failure - string are not equals", "hello", "hello");
Assert.assertNotEquals("failure - string are equals", "hello", "world");
}
@Test
public void testAssertArrayEquals() {
byte[] expected = "hello".getBytes();
byte[] actual = "hello".getBytes();
Assert.assertArrayEquals("failure - byte arrays not same", expected, actual);
}
@Test
public void testAssertBoolean() {
Assert.assertTrue("failure - should be true", true);
Assert.assertFalse("failure - should be false", false);
}
@Test
public void testAssertNull() {
Assert.assertNull("should be null ", null);
Assert.assertNotNull("should not be null", new Object());
}
@Test
public void testAssertSame() {
Integer expected = Integer.valueOf("666");
int actual = 666;
// assertSame 会将所有参数转为Object 对象,比较的是对象的地址,而不是数值
Assert.assertSame("should be same", expected, expected);
Assert.assertNotSame("should not be same", expected, actual);
}
@Test
public void testAssertThatBothContainsString() {
Assert.assertThat("hello JUnit",
CoreMatchers.both(CoreMatchers.containsString("hello")).and(CoreMatchers.containsString("J")));
}
@Test
public void testAssertThatHasItems() {
Assert.assertThat(Arrays.asList("one", "two", "three"), CoreMatchers.hasItem("one"));
}
}
Suite
当你需要运行多个测试类时,可以使用 Junit 提供的 suite 来包含多个测试类,并指定多个测试类的执行顺序。执行顺序以 @Suite.SuiteClasses 中引入的测试类顺序为准。
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
}
指定测试方法执行顺序
JUnit设计之初,没有指定测试方法调用的执行顺序。到目前为止,这些方法只是按照反射API返回的顺序被调用。然而,使用JVM命令是不明智的,因为Java平台没有指定任何特定的顺序,并且事实上JDK 7返回或多或少的随机顺序。当然,编写良好的测试代码不会有任何顺序,但有些会有顺序,可预测的失败比某些平台上的随机失败要好。
从4.11版开始,JUnit 可以使用 @FixMethodOrder 指定确定性但不可预测的顺序:
- @FixMethodOrder(MethodSorters.DEFAULT):默认家执行顺序
- @FixMethodOrder(MethodSorters.JVM):按照JVM返回的顺序执行测试方法。此顺序可能因运行而异。
- @FixMethodOrder(MethodSorters.NAME_ASCENDING):按方法名的字典顺序对测试方法进行排序。
使用方法:
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class TestMethodOrder {
@Test
public void testA() {
System.out.println("first");
}
@Test
public void testB() {
System.out.println("second");
}
}
异常测试
预期异常的处理
当我们需要验证代码是否能否按照预期抛出特定的异常时,可以在@Test() 中指定预期的异常类型。当无此异常发生时,则测试不通过。
@Test(expected = IndexOutOfBoundsException.class)
public void empty() {
new ArrayList<Object>().get(0);
}
更深一步的异常测试
上述的方法只适合用于一些简单的异常测试,但他有一些限制,比如不能指定异常中测试消息的值,也不能在引发异常后测试域对象的状态。我们有两种方式来捕捉异常,并指定异常消息的值:
- 使用 try…catch… 捕捉异常
- 使用 ExpectedException 指定规则
// 1. 使用 try...catch... 捕捉异常
@Test
public void testExceptionMessage() {
try {
new ArrayList<Object>().get(0);
} catch (IndexOutOfBoundsException anIndexOutOfBoundsException) {
Assert.assertThat(anIndexOutOfBoundsException.getMessage(), CoreMatchers.is("Index: 0, Size: 0"));
}
}
// 2. 使用 ExpectedException 指定规则
public ExpectedException thrown = ExpectedException.none();
@Test
public void shouldTestExceptionMessage() throws IndexOutOfBoundsException {
List<Object> list = new ArrayList<Object>();
thrown.expect(IndexOutOfBoundsException.class);
thrown.expectMessage("Index: 0, Size: 0");
list.get(0); // execution will never get past this line
}
匹配器和断言
待研究:https://github.com/junit-team/junit4/wiki/Matchers-and-assertthat
忽略测试
某些情况下,由于某个测试方法失败而影响整个测试进程时,想暂时忽略跳过此方法时,可以使用@Ignore() 方法。
@Ignore("忽略时的提示消息")
@Test
public void testAssertEquals() {
Assert.assertEquals("failure - string are not equals", "hello", "hello2");
}
超时设置
在测试有些比较耗时或者需要在规定时间内执行完毕的方法时,可以指定方法执行的超时时间。当超过时间限制时,则会引发超时异常使得测试失败,有助于我们明确哪些需要更进一步优化运行速度的代码。有以下2中方式设置超时时间:
- @Test(timeout= …),作用范围仅为该测试方法
- 使用@Rule指定Timeout 的规则,作用范围为整个测试类,注意也会覆盖 @Test 中设置的 timeout
// 1、@Test(timeout= ...)
@Test(timeout = 5000)
public void testTimeOut() {
}
// 2、使用@Rule指定Timeout 的规则
@Rule
public Timeout timeout = Timeout.seconds(5);
private final CountDownLatch latch = new CountDownLatch(1);
@Test
public void testSleepForTooLong() throws Exception {
TimeUnit.SECONDS.sleep(100); // sleep for 100 seconds
}
@Test
public void testBlockForever() throws Exception {
latch.await(); // will block
}
参数化测试
待研究:https://github.com/junit-team/junit4/wiki/Parameterized-tests
假设
假设即当如果不满足某个条件时,则不要运行此测试,它的作用与 Assert 类似,只不过不会被当成一个失败的测试。
import static org.junit.Assume.*;
@Test
public void testAssumeTrue() {
assumeTrue("assumeTrue is false", true);
assumeFalse("assumeFalse is true", false);
}
@Test
public void testAssumeThat() {
assumeThat("assumeThat is false", "hello world", CoreMatchers.containsString("hello"));
}
@Test
public void testN() {
assumeNotNull("assumeNotNull is false", new Object());
}
规则
规则允许非常灵活地添加或重新定义测试类中每个测试方法的行为。测试人员可以重用或扩展 JUnit 提供的规则之一,或者编写自己的规则。
JUnit 提供的规则比较多,详细使用方法可以件官方文档例子。
理论
待研究:https://github.com/junit-team/junit4/wiki/Theories
Test fixtures
测试夹具的目的是确保有一个众所周知且固定的环境,在该环境中运行测试,以便结果可重复。例如:
- 准备输入数据和设置/创建假对象或模拟对象
- 使用特定的已知数据集加载数据库
- 复制一组特定的已知文件创建一个测试夹具将创建一组初始化为特定状态的对象。
JUnit 提供了一些注解以在测试类或测试方法执行之前执行某些特定的方法。
- @BeforeClass 在测试类中的所有测试方法执行之前执行(该方法必须是静态)
- @AfterClass 在测试类中的所有测试方法执行之后执行(该方法必须是静态)
- @Before 在每一个测试方法之前运行
- @After.每一个测试方法之后运行
例子:
@BeforeClass
public static void testBeforeClass() {
System.out.println("@BeforeClass");
}
@AfterClass
public static void testAfterClass() {
System.out.println("@AfterClass");
}
@Before
public void testBefore() {
System.out.println("@Before");
}
@After
public void testAftee() {
System.out.println("@After");
}
@Test
public void test1() {
System.out.println("JUnit test1");
}
@Test
public void test2() {
System.out.println("JUnit test2");
}
输出结果:
@BeforeClass
@Before
JUnit test1
@After
@Before
JUnit test2
@After
@AfterClass
类别
从一组给定的测试类中,Categories 运行程序仅运行用于 @Includecategory 注释给定的类或该类别的子类型进行注释的类和方法。@Includecategory(Superclass.class),将运行使用 @Category(Dubclass.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 {
// 只会运行 A.b、 B.c,但不会执行 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 {
// 只会运行 A.b,但不会执行 A.a 、B.c
}