什么是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@Testpublic void test() {//todo 这里写你的业务代码int num = 1;//断言:判断结果是否否和你的预期//符合预期 true时 绿条通过//不符合预期 false时 红条并输出报错信息assertEquals(1, num);}
断言 assert
//断言两个数组是否相等 assertArrayEquals@Testpublic void testAssertArrayEquals() {byte[] expected = "trial".getBytes();byte[] actual = "trial".getBytes();assertArrayEquals("failure - byte arrays not same", expected, actual);}//断言两个对象是否相等@Testpublic void testAssertEquals() {assertEquals("failure - strings are not equal", "text", "text");}//断言是否为false@Testpublic void testAssertFalse() {assertFalse("failure - should be false", false);}//断言是否为true@Testpublic void testAssertTrue() {assertTrue("failure - should be true", true);}//断言是否为空@Testpublic void testAssertNotNull() {assertNotNull("should not be null", new Object());}//断言两个对象不一样@Testpublic void testAssertNotSame() {assertNotSame("should not be same Object", new Object(), new Object());}//断言为空@Testpublic void testAssertNull() {assertNull("should be null", null);}//断言两对象一样@Testpublic void testAssertSame() {Integer aNumber = Integer.valueOf(768);assertSame("should be same", aNumber, aNumber);}//匹配器断言// JUnit Matchers assertThat@Testpublic void testAssertThatBothContainsString() {assertThat("albumen", both(containsString("a")).and(containsString("b")));}@Testpublic void testAssertThatHasItems() {assertThat(Arrays.asList("one", "two", "three"), hasItems("one", "three"));}@Testpublic void testAssertThatEveryItemContainsString() {assertThat(Arrays.asList(new String[]{"fun", "ban", "net"}), everyItem(containsString("n")));}// Core Hamcrest Matchers with assertThat@Testpublic 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
表达一种假设成立的情况下才进行测试,假设不成立则不测试
//假设判断当前操作系统@Testpublic 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_测试方法执行顺序 {@Testpublic void testA() {System.out.println("first");}@Testpublic void testB() {System.out.println("second");}@Testpublic void testC() {System.out.println("third");}}
测试结果如下

异常测试
JUnit可以测试程序运行时抛出的异常,是否跟预期一致
//预期 抛出异常//expected = IndexOutOfBoundsException.class@Test(expected = IndexOutOfBoundsException.class)public void empty() {new ArrayList<Object>().get(0);}//预期异常消息的值@Testpublic 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"));}}//预期异常规则@Rulepublic ExpectedException thrown = ExpectedException.none();@Testpublic 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>//使用@Testpublic 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")@Testpublic 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 testedpublic Timeout globalTimeout = Timeout.seconds(10);@Testpublic void testSleepForTooLong() throws Exception {log += "ran1";TimeUnit.SECONDS.sleep(100); // sleep for 100 seconds}@Testpublic 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@BeforeClasspublic static void runOnceBeforeClass() {System.out.println("@BeforeClass - runOnceBeforeClass");}// Run once, e.g close connection, cleanup@AfterClasspublic static void runOnceAfterClass() {System.out.println("@AfterClass - runOnceAfterClass");}// Should rename to @BeforeTestMethod// e.g. Creating an similar object and share for all @Test@Beforepublic void runBeforeTestMethod() {System.out.println("@Before - runBeforeTestMethod");}// Should rename to @AfterTestMethod@Afterpublic void runAfterTestMethod() {System.out.println("@After - runAfterTestMethod");}@Testpublic void test_method_1() {System.out.println("@Test - test_method_1");}@Testpublic 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/
 
