什么是JUnit
JUnit是Java单元测试框架,方便好用。
单元测试就是最小粒度的测试,Java中就是方法测试,最小粒度是方法。
我们说的JUnit默认指JUnit4,即Java7一下版本使用的单元测试,Java8以后请使用JUnit5,提供了更好用的测试API
本项目所有代码均在github开源:https://github.com/initit-com/JUnitDemo
下载安装
推荐使用Maven引入,本质就是引入了junt.jar和hamcrest-core.jar两个jar包
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
快速上手
//@Test 标注一个方法为单元测试代码
//单元测试是最小粒度的测试,即方法测试
//方法必须是public的 ,返回值必须是 void
@Test
public void test() {
//todo 这里写你的业务代码
int num = 1;
//断言:判断结果是否否和你的预期
//符合预期 true时 绿条通过
//不符合预期 false时 红条并输出报错信息
assertEquals(1, num);
}
断言 assert
//断言两个数组是否相等 assertArrayEquals
@Test
public void testAssertArrayEquals() {
byte[] expected = "trial".getBytes();
byte[] actual = "trial".getBytes();
assertArrayEquals("failure - byte arrays not same", expected, actual);
}
//断言两个对象是否相等
@Test
public void testAssertEquals() {
assertEquals("failure - strings are not equal", "text", "text");
}
//断言是否为false
@Test
public void testAssertFalse() {
assertFalse("failure - should be false", false);
}
//断言是否为true
@Test
public void testAssertTrue() {
assertTrue("failure - should be true", true);
}
//断言是否为空
@Test
public void testAssertNotNull() {
assertNotNull("should not be null", new Object());
}
//断言两个对象不一样
@Test
public void testAssertNotSame() {
assertNotSame("should not be same Object", new Object(), new Object());
}
//断言为空
@Test
public void testAssertNull() {
assertNull("should be null", null);
}
//断言两对象一样
@Test
public void testAssertSame() {
Integer aNumber = Integer.valueOf(768);
assertSame("should be same", aNumber, aNumber);
}
//匹配器断言
// JUnit Matchers assertThat
@Test
public void testAssertThatBothContainsString() {
assertThat("albumen", both(containsString("a")).and(containsString("b")));
}
@Test
public void testAssertThatHasItems() {
assertThat(Arrays.asList("one", "two", "three"), hasItems("one", "three"));
}
@Test
public void testAssertThatEveryItemContainsString() {
assertThat(Arrays.asList(new String[]{"fun", "ban", "net"}), everyItem(containsString("n")));
}
// Core Hamcrest Matchers with assertThat
@Test
public void testAssertThatHamcrestCoreMatchers() {
assertThat("good", allOf(equalTo("good"), startsWith("good")));
assertThat("good", not(allOf(equalTo("bad"), equalTo("good"))));
assertThat("good", anyOf(equalTo("bad"), equalTo("good")));
assertThat(7, not(CombinableMatcher.<Integer>either(equalTo(3)).or(equalTo(4))));
assertThat(new Object(), not(sameInstance(new Object())));
}
假设 assume
表达一种假设成立的情况下才进行测试,假设不成立则不测试
//假设判断当前操作系统
@Test
public void filenameIncludesUsername() {
//assumeThat(File.separatorChar, is('/'));
assumeThat(File.separatorChar, is('\\'));
System.out.println("走了");
assertThat("configfiles/optimus.cfg", is("configfiles/optimus.cfg"));
}
//windows 下运行 会通过,其他系统会报错
测试执行的顺序
多个测试方法一起执行时,jvm会随机顺序执行,如果你想执行方法的执行顺序,可以使用(MethodSorters.DEFAULT)@FixMethodOrder
@FixMethodOrder(MethodSorters.JVM)
:将测试方法按 JVM 返回的顺序保留。此顺序可能因运行而异。@FixMethodOrder(MethodSorters.NAME_ASCENDING)
:按方法名称、字典顺序对测试方法进行排序。
示例:以下代码将按其名称的顺序执行测试方法,按升序排序
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class Demo3_测试方法执行顺序 {
@Test
public void testA() {
System.out.println("first");
}
@Test
public void testB() {
System.out.println("second");
}
@Test
public void testC() {
System.out.println("third");
}
}
测试结果如下
异常测试
JUnit可以测试程序运行时抛出的异常,是否跟预期一致
//预期 抛出异常
//expected = IndexOutOfBoundsException.class
@Test(expected = IndexOutOfBoundsException.class)
public void empty() {
new ArrayList<Object>().get(0);
}
//预期异常消息的值
@Test
public void testExceptionMessage() {
try {
new ArrayList<Object>().get(0);
fail("Expected an IndexOutOfBoundsException to be thrown");
} catch (IndexOutOfBoundsException anIndexOutOfBoundsException) {
assertThat(anIndexOutOfBoundsException.getMessage(), is("Index: 0, Size: 0"));
}
}
//预期异常规则
@Rule
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");
// thrown.expectMessage(CoreMatchers.containsString("Size: 0")); //或者这样写
list.get(0); // execution will never get past this line
}
匹配器和断言
一种可读性更好的断言方式:assertThat([value], [matcher statement]);
assertThat(x, is(3));
assertThat(x, is(not(4)));
assertThat(responseString, either(containsString("color")).or(containsString("colour")));
assertThat(myList, hasItem("3"));
匹配器断言excel
参考:https://github.com/tobyweston/simple-excel
//安装
//可通过Maven 存储库手动下载或将存储库添加到您的"pom.xml"。
<repositories>
<repository>
<id>bad.robot</id>
<name>bad.robot repository on robotooling</name>
<url>http://www.robotooling.com/maven/</url>
<snapshots>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
</snapshots>
</repository>
</repositories>
//然后添加依赖项。
<dependency>
<groupId>bad.robot</groupId>
<artifactId>simple-excel</artifactId>
<version>1.1</version>
<scope>compile</scope>
</dependency>
//使用
@Test
public void shouldReplaceCellsInComplicatedAlternateSyntaxExample() throws IOException {
HSSFWorkbook workbook = getWorkbook("shouldReplaceCellsInComplicatedExampleTemplate.xls");
new PoiWorkbookMutator(workbook)
.replaceCell(coordinate(C, 5), "Adding")
.replaceCell(coordinate(D, 11), "a")
.replaceCell(coordinate(G, 3), "cell")
.replaceCell(coordinate(J, 10), "total")
.replaceCell(coordinate(M, 15), 99.99d);
assertThat(workbook, sameWorkbook(getWorkbook("shouldReplaceCellsInComplicatedExampleTemplateExpected.xls")));
}
//输出结构
java.lang.AssertionError:
Expected: equality of cell "G1"
but: cell at "G1" contained <"Text"> expected <99.99D>
at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20)
匹配器断言Json
参考:https://github.com/hertzsprung/hamcrest-json
//安装
<dependency>
<groupId>uk.co.datumedge</groupId>
<artifactId>hamcrest-json</artifactId>
<version>0.1</version>
</dependency>
//使用
assertThat(
"{\"age\":43, \"friend_ids\":[16, 52, 23]}",
sameJSONAs("{\"friend_ids\":[52, 23, 16]}")
.allowingExtraUnexpectedFields()
.allowingAnyArrayOrdering());
匹配器断言Xml
参考:https://github.com/davidehringer/xml-matchers
Source xml = ...
assertThat(xml, hasXPath("/mountains/mountain"));
Node xml = ...
assertThat(
the(xml),
hasXPath("count(/mountains/mountain)",
returningANumber(),
greaterThanOrEqualTo(2d)));
String xml = "<mountains><mountain>K2</mountain></mountains>";
String xmlWithSpaceWrappingText = "<mountains><mountain>\n\tK2\n\t </mountain></mountains>";
assertThat(the(xml), isEquivalentTo(the(xmlWithSpaceWrappingText)));
Schema schema = w3cXmlSchemaFromClasspath("org/xmlmatchers/validation/example.xsd");
String xml = "<person private=\"true\"><name>Dave</name></person>";
assertThat(the(xml), conformsTo(schema));
忽略测试
如果由于某种原因,您不希望测试失败,您只想忽略它,可以暂时忽略测试
@Ignore("Test is ignored as a demonstration")
@Test
public void testSame() {
assertThat(1, is(1));
}
测试超时
测试超过一定时间自动失败报错
//毫秒数
@Test(timeout = 1000)
public void testWithTimeout() throws Exception {
Thread.sleep(999);
}
超时规则(适用于测试类中的所有测试用例)
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.Timeout;
public class HasGlobalTimeout {
public static String log;
private final CountDownLatch latch = new CountDownLatch(1);
@Rule
// 10 seconds max per method tested
public Timeout globalTimeout = Timeout.seconds(10);
@Test
public void testSleepForTooLong() throws Exception {
log += "ran1";
TimeUnit.SECONDS.sleep(100); // sleep for 100 seconds
}
@Test
public void testBlockForever() throws Exception {
log += "ran2";
latch.await(); // will block
}
}
测试生命周期处理
有时候,你需要在测试方法运行前做一些准备处理,测试方法完成后做一下后续处理。提供四个测试注解
- @BeforeClass – 表示在类中的任意public static void方法执行之前执行
- @AfterClass – 表示在类中的任意public static void方法执行之后执行
- @Before – 表示在任意使用@Test注解标注的public void方法执行之前执行
- @After – 表示在任意使用@Test注解标注的public void方法执行之后执行
- @Test – 使用该注解标注的public void方法会表示为一个测试方法
package org.byron4j.spring_mvc_log4j;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
public class BasicAnnotationTest {
// Run once, e.g. Database connection, connection pool
@BeforeClass
public static void runOnceBeforeClass() {
System.out.println("@BeforeClass - runOnceBeforeClass");
}
// Run once, e.g close connection, cleanup
@AfterClass
public static void runOnceAfterClass() {
System.out.println("@AfterClass - runOnceAfterClass");
}
// Should rename to @BeforeTestMethod
// e.g. Creating an similar object and share for all @Test
@Before
public void runBeforeTestMethod() {
System.out.println("@Before - runBeforeTestMethod");
}
// Should rename to @AfterTestMethod
@After
public void runAfterTestMethod() {
System.out.println("@After - runAfterTestMethod");
}
@Test
public void test_method_1() {
System.out.println("@Test - test_method_1");
}
@Test
public void test_method_2() {
System.out.println("@Test - test_method_2");
}
}
//输出
@BeforeClass - runOnceBeforeClass
@Before - runBeforeTestMethod
@Test - test_method_1
@After - runAfterTestMethod
@Before - runBeforeTestMethod
@Test - test_method_2
@After - runAfterTestMethod
@AfterClass - runOnceAfterClass
参考资源如下
- JUnit官网: https://junit.org/junit4/
- JUnit源码:https://github.com/junit-team/junit4
- JUnit文档:https://github.com/junit-team/junit4/wiki
- JUnit API文档:https://junit.org/junit4/javadoc/latest/