什么是JUnit

JUnit是Java单元测试框架,方便好用。

单元测试就是最小粒度的测试,Java中就是方法测试,最小粒度是方法。

我们说的JUnit默认指JUnit4,即Java7一下版本使用的单元测试,Java8以后请使用JUnit5,提供了更好用的测试API

本项目所有代码均在github开源:https://github.com/initit-com/JUnitDemo

下载安装

推荐使用Maven引入,本质就是引入了junt.jar和hamcrest-core.jar两个jar包

  1. <dependency>
  2. <groupId>junit</groupId>
  3. <artifactId>junit</artifactId>
  4. <version>4.12</version>
  5. <scope>test</scope>
  6. </dependency>

快速上手

  1. //@Test 标注一个方法为单元测试代码
  2. //单元测试是最小粒度的测试,即方法测试
  3. //方法必须是public的 ,返回值必须是 void
  4. @Test
  5. public void test() {
  6. //todo 这里写你的业务代码
  7. int num = 1;
  8. //断言:判断结果是否否和你的预期
  9. //符合预期 true时 绿条通过
  10. //不符合预期 false时 红条并输出报错信息
  11. assertEquals(1, num);
  12. }

断言 assert

  1. //断言两个数组是否相等 assertArrayEquals
  2. @Test
  3. public void testAssertArrayEquals() {
  4. byte[] expected = "trial".getBytes();
  5. byte[] actual = "trial".getBytes();
  6. assertArrayEquals("failure - byte arrays not same", expected, actual);
  7. }
  8. //断言两个对象是否相等
  9. @Test
  10. public void testAssertEquals() {
  11. assertEquals("failure - strings are not equal", "text", "text");
  12. }
  13. //断言是否为false
  14. @Test
  15. public void testAssertFalse() {
  16. assertFalse("failure - should be false", false);
  17. }
  18. //断言是否为true
  19. @Test
  20. public void testAssertTrue() {
  21. assertTrue("failure - should be true", true);
  22. }
  23. //断言是否为空
  24. @Test
  25. public void testAssertNotNull() {
  26. assertNotNull("should not be null", new Object());
  27. }
  28. //断言两个对象不一样
  29. @Test
  30. public void testAssertNotSame() {
  31. assertNotSame("should not be same Object", new Object(), new Object());
  32. }
  33. //断言为空
  34. @Test
  35. public void testAssertNull() {
  36. assertNull("should be null", null);
  37. }
  38. //断言两对象一样
  39. @Test
  40. public void testAssertSame() {
  41. Integer aNumber = Integer.valueOf(768);
  42. assertSame("should be same", aNumber, aNumber);
  43. }
  44. //匹配器断言
  45. // JUnit Matchers assertThat
  46. @Test
  47. public void testAssertThatBothContainsString() {
  48. assertThat("albumen", both(containsString("a")).and(containsString("b")));
  49. }
  50. @Test
  51. public void testAssertThatHasItems() {
  52. assertThat(Arrays.asList("one", "two", "three"), hasItems("one", "three"));
  53. }
  54. @Test
  55. public void testAssertThatEveryItemContainsString() {
  56. assertThat(Arrays.asList(new String[]{"fun", "ban", "net"}), everyItem(containsString("n")));
  57. }
  58. // Core Hamcrest Matchers with assertThat
  59. @Test
  60. public void testAssertThatHamcrestCoreMatchers() {
  61. assertThat("good", allOf(equalTo("good"), startsWith("good")));
  62. assertThat("good", not(allOf(equalTo("bad"), equalTo("good"))));
  63. assertThat("good", anyOf(equalTo("bad"), equalTo("good")));
  64. assertThat(7, not(CombinableMatcher.<Integer>either(equalTo(3)).or(equalTo(4))));
  65. assertThat(new Object(), not(sameInstance(new Object())));
  66. }

假设 assume

表达一种假设成立的情况下才进行测试,假设不成立则不测试

  1. //假设判断当前操作系统
  2. @Test
  3. public void filenameIncludesUsername() {
  4. //assumeThat(File.separatorChar, is('/'));
  5. assumeThat(File.separatorChar, is('\\'));
  6. System.out.println("走了");
  7. assertThat("configfiles/optimus.cfg", is("configfiles/optimus.cfg"));
  8. }
  9. //windows 下运行 会通过,其他系统会报错

测试执行的顺序

多个测试方法一起执行时,jvm会随机顺序执行,如果你想执行方法的执行顺序,可以使用(MethodSorters.DEFAULT)@FixMethodOrder

  • @FixMethodOrder(MethodSorters.JVM):将测试方法按 JVM 返回的顺序保留。此顺序可能因运行而异。
  • @FixMethodOrder(MethodSorters.NAME_ASCENDING):按方法名称、字典顺序对测试方法进行排序。

示例:以下代码将按其名称的顺序执行测试方法,按升序排序

  1. import org.junit.FixMethodOrder;
  2. import org.junit.Test;
  3. import org.junit.runners.MethodSorters;
  4. @FixMethodOrder(MethodSorters.NAME_ASCENDING)
  5. public class Demo3_测试方法执行顺序 {
  6. @Test
  7. public void testA() {
  8. System.out.println("first");
  9. }
  10. @Test
  11. public void testB() {
  12. System.out.println("second");
  13. }
  14. @Test
  15. public void testC() {
  16. System.out.println("third");
  17. }
  18. }

测试结果如下

image.png

异常测试

JUnit可以测试程序运行时抛出的异常,是否跟预期一致

  1. //预期 抛出异常
  2. //expected = IndexOutOfBoundsException.class
  3. @Test(expected = IndexOutOfBoundsException.class)
  4. public void empty() {
  5. new ArrayList<Object>().get(0);
  6. }
  7. //预期异常消息的值
  8. @Test
  9. public void testExceptionMessage() {
  10. try {
  11. new ArrayList<Object>().get(0);
  12. fail("Expected an IndexOutOfBoundsException to be thrown");
  13. } catch (IndexOutOfBoundsException anIndexOutOfBoundsException) {
  14. assertThat(anIndexOutOfBoundsException.getMessage(), is("Index: 0, Size: 0"));
  15. }
  16. }
  17. //预期异常规则
  18. @Rule
  19. public ExpectedException thrown = ExpectedException.none();
  20. @Test
  21. public void shouldTestExceptionMessage() throws IndexOutOfBoundsException {
  22. List<Object> list = new ArrayList<Object>();
  23. thrown.expect(IndexOutOfBoundsException.class);
  24. thrown.expectMessage("Index: 0, Size: 0");
  25. // thrown.expectMessage(CoreMatchers.containsString("Size: 0")); //或者这样写
  26. list.get(0); // execution will never get past this line
  27. }

匹配器和断言

一种可读性更好的断言方式:assertThat([value], [matcher statement]);

  1. assertThat(x, is(3));
  2. assertThat(x, is(not(4)));
  3. assertThat(responseString, either(containsString("color")).or(containsString("colour")));
  4. assertThat(myList, hasItem("3"));

匹配器断言excel

参考:https://github.com/tobyweston/simple-excel

  1. //安装
  2. //可通过Maven 存储库手动下载或将存储库添加到您的"pom.xml"。
  3. <repositories>
  4. <repository>
  5. <id>bad.robot</id>
  6. <name>bad.robot repository on robotooling</name>
  7. <url>http://www.robotooling.com/maven/</url>
  8. <snapshots>
  9. <enabled>true</enabled>
  10. <updatePolicy>always</updatePolicy>
  11. </snapshots>
  12. </repository>
  13. </repositories>
  14. //然后添加依赖项。
  15. <dependency>
  16. <groupId>bad.robot</groupId>
  17. <artifactId>simple-excel</artifactId>
  18. <version>1.1</version>
  19. <scope>compile</scope>
  20. </dependency>
  21. //使用
  22. @Test
  23. public void shouldReplaceCellsInComplicatedAlternateSyntaxExample() throws IOException {
  24. HSSFWorkbook workbook = getWorkbook("shouldReplaceCellsInComplicatedExampleTemplate.xls");
  25. new PoiWorkbookMutator(workbook)
  26. .replaceCell(coordinate(C, 5), "Adding")
  27. .replaceCell(coordinate(D, 11), "a")
  28. .replaceCell(coordinate(G, 3), "cell")
  29. .replaceCell(coordinate(J, 10), "total")
  30. .replaceCell(coordinate(M, 15), 99.99d);
  31. assertThat(workbook, sameWorkbook(getWorkbook("shouldReplaceCellsInComplicatedExampleTemplateExpected.xls")));
  32. }
  33. //输出结构
  34. java.lang.AssertionError:
  35. Expected: equality of cell "G1"
  36. but: cell at "G1" contained <"Text"> expected <99.99D>
  37. at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20)

匹配器断言Json

参考:https://github.com/hertzsprung/hamcrest-json

  1. //安装
  2. <dependency>
  3. <groupId>uk.co.datumedge</groupId>
  4. <artifactId>hamcrest-json</artifactId>
  5. <version>0.1</version>
  6. </dependency>
  7. //使用
  8. assertThat(
  9. "{\"age\":43, \"friend_ids\":[16, 52, 23]}",
  10. sameJSONAs("{\"friend_ids\":[52, 23, 16]}")
  11. .allowingExtraUnexpectedFields()
  12. .allowingAnyArrayOrdering());

匹配器断言Xml

参考:https://github.com/davidehringer/xml-matchers

  1. Source xml = ...
  2. assertThat(xml, hasXPath("/mountains/mountain"));
  3. Node xml = ...
  4. assertThat(
  5. the(xml),
  6. hasXPath("count(/mountains/mountain)",
  7. returningANumber(),
  8. greaterThanOrEqualTo(2d)));
  9. String xml = "<mountains><mountain>K2</mountain></mountains>";
  10. String xmlWithSpaceWrappingText = "<mountains><mountain>\n\tK2\n\t </mountain></mountains>";
  11. assertThat(the(xml), isEquivalentTo(the(xmlWithSpaceWrappingText)));
  12. Schema schema = w3cXmlSchemaFromClasspath("org/xmlmatchers/validation/example.xsd");
  13. String xml = "<person private=\"true\"><name>Dave</name></person>";
  14. assertThat(the(xml), conformsTo(schema));

忽略测试

如果由于某种原因,您不希望测试失败,您只想忽略它,可以暂时忽略测试

  1. @Ignore("Test is ignored as a demonstration")
  2. @Test
  3. public void testSame() {
  4. assertThat(1, is(1));
  5. }

测试超时

测试超过一定时间自动失败报错

  1. //毫秒数
  2. @Test(timeout = 1000)
  3. public void testWithTimeout() throws Exception {
  4. Thread.sleep(999);
  5. }

超时规则(适用于测试类中的所有测试用例)

  1. import org.junit.Rule;
  2. import org.junit.Test;
  3. import org.junit.rules.Timeout;
  4. public class HasGlobalTimeout {
  5. public static String log;
  6. private final CountDownLatch latch = new CountDownLatch(1);
  7. @Rule
  8. // 10 seconds max per method tested
  9. public Timeout globalTimeout = Timeout.seconds(10);
  10. @Test
  11. public void testSleepForTooLong() throws Exception {
  12. log += "ran1";
  13. TimeUnit.SECONDS.sleep(100); // sleep for 100 seconds
  14. }
  15. @Test
  16. public void testBlockForever() throws Exception {
  17. log += "ran2";
  18. latch.await(); // will block
  19. }
  20. }

测试生命周期处理

有时候,你需要在测试方法运行前做一些准备处理,测试方法完成后做一下后续处理。提供四个测试注解

  • @BeforeClass – 表示在类中的任意public static void方法执行之前执行
  • @AfterClass – 表示在类中的任意public static void方法执行之后执行
  • @Before – 表示在任意使用@Test注解标注的public void方法执行之前执行
  • @After – 表示在任意使用@Test注解标注的public void方法执行之后执行
  • @Test – 使用该注解标注的public void方法会表示为一个测试方法
  1. package org.byron4j.spring_mvc_log4j;
  2. import org.junit.After;
  3. import org.junit.AfterClass;
  4. import org.junit.Before;
  5. import org.junit.BeforeClass;
  6. import org.junit.Test;
  7. public class BasicAnnotationTest {
  8. // Run once, e.g. Database connection, connection pool
  9. @BeforeClass
  10. public static void runOnceBeforeClass() {
  11. System.out.println("@BeforeClass - runOnceBeforeClass");
  12. }
  13. // Run once, e.g close connection, cleanup
  14. @AfterClass
  15. public static void runOnceAfterClass() {
  16. System.out.println("@AfterClass - runOnceAfterClass");
  17. }
  18. // Should rename to @BeforeTestMethod
  19. // e.g. Creating an similar object and share for all @Test
  20. @Before
  21. public void runBeforeTestMethod() {
  22. System.out.println("@Before - runBeforeTestMethod");
  23. }
  24. // Should rename to @AfterTestMethod
  25. @After
  26. public void runAfterTestMethod() {
  27. System.out.println("@After - runAfterTestMethod");
  28. }
  29. @Test
  30. public void test_method_1() {
  31. System.out.println("@Test - test_method_1");
  32. }
  33. @Test
  34. public void test_method_2() {
  35. System.out.println("@Test - test_method_2");
  36. }
  37. }
  38. //输出
  39. @BeforeClass - runOnceBeforeClass
  40. @Before - runBeforeTestMethod
  41. @Test - test_method_1
  42. @After - runAfterTestMethod
  43. @Before - runBeforeTestMethod
  44. @Test - test_method_2
  45. @After - runAfterTestMethod
  46. @AfterClass - runOnceAfterClass

参考资源如下