0


JUnit 4 超详细教程(一)

JUnit 4 超详细教程(一)

下篇文档地址:JUnit 4 超详细教程(二)

1.介绍

1.1.什么是JUnit?

JUnit是Java编程语言的单元测试框架,用于编写和可重复运行的自动化测试。

1.2.单元测试的好处

  • 编码完成就可以立刻测试,尽早发现问题
  • 将测试保存成为了代码,可以随时快速执行
  • 可以嵌入持续集成流水线,自动为每次代码修改保驾护航

1.3.JUnit 4 官网

JUnit 4 官网地址

2.JUnit 4 的使用

2.1.注解

JUnit4通过注解的方式来识别测试方法。目前支持的主要注解有(下列注解标示了单元测试的不同运行阶段执行的代码):

  • @BeforeClass 全局只会执行一次,而且是第一个运行
  • @Before 在测试方法运行之前运行(每个测试方法之前都会执行一次)
  • @Test 测试方法
  • @After 在测试方法运行之后允许(每个测试方法之后都会执行一次)
  • @AfterClass 全局只会执行一次,而且是最后一个运行
  • @Ignore 忽略此方法
  • @Runwith 放在测试类名之前,用来确定这个类怎么运行的。也可以不标注,会使用默认运行器
  • @Parameters 用于使用参数化功能
  • @SuiteClasses 用于套件测试 @BeforeClass @Before @After @AfterClass这些注解标注的方法又称测试的Fixture。

2.2.断言

JUnit为所有原语类型、对象和数组(原语或对象)提供重载断言方法。参数顺序为预期值后接实际值。或者,第一个参数可以是失败时输出的字符串消息。
有一个稍有不同的断言,

assertThat

具有可选失败消息的参数、实际值和

Matcher

对象。请注意,与其他assert方法相比,它的预期值和实际值是相反的(实际值后接预期值)。
下面例子列举了所有可用的assert方法,可以在使用时作用法参考

importstaticorg.hamcrest.CoreMatchers.allOf;importstaticorg.hamcrest.CoreMatchers.anyOf;importstaticorg.hamcrest.CoreMatchers.both;importstaticorg.hamcrest.CoreMatchers.containsString;importstaticorg.hamcrest.CoreMatchers.equalTo;importstaticorg.hamcrest.CoreMatchers.everyItem;importstaticorg.hamcrest.CoreMatchers.hasItems;importstaticorg.hamcrest.CoreMatchers.not;importstaticorg.hamcrest.CoreMatchers.sameInstance;importstaticorg.hamcrest.CoreMatchers.startsWith;importstaticorg.junit.Assert.assertArrayEquals;importstaticorg.junit.Assert.assertEquals;importstaticorg.junit.Assert.assertFalse;importstaticorg.junit.Assert.assertNotNull;importstaticorg.junit.Assert.assertNotSame;importstaticorg.junit.Assert.assertNull;importstaticorg.junit.Assert.assertSame;importstaticorg.junit.Assert.assertThat;importstaticorg.junit.Assert.assertTrue;importjava.util.Arrays;importorg.hamcrest.core.CombinableMatcher;importorg.junit.Test;publicclassAssertTests{@TestpublicvoidtestAssertArrayEquals(){byte[] expected ="trial".getBytes();byte[] actual ="trial".getBytes();assertArrayEquals("failure - byte arrays not same", expected, actual);}@TestpublicvoidtestAssertEquals(){assertEquals("failure - strings are not equal","text","text");}@TestpublicvoidtestAssertFalse(){assertFalse("failure - should be false",false);}@TestpublicvoidtestAssertNotNull(){assertNotNull("should not be null",newObject());}@TestpublicvoidtestAssertNotSame(){assertNotSame("should not be same Object",newObject(),newObject());}@TestpublicvoidtestAssertNull(){assertNull("should be null",null);}@TestpublicvoidtestAssertSame(){Integer aNumber =Integer.valueOf(768);assertSame("should be same", aNumber, aNumber);}// JUnit Matchers assertThat@TestpublicvoidtestAssertThatBothContainsString(){assertThat("albumen",both(containsString("a")).and(containsString("b")));}@TestpublicvoidtestAssertThatHasItems(){assertThat(Arrays.asList("one","two","three"),hasItems("one","three"));}@TestpublicvoidtestAssertThatEveryItemContainsString(){assertThat(Arrays.asList(newString[]{"fun","ban","net"}),everyItem(containsString("n")));}// Core Hamcrest Matchers with assertThat@TestpublicvoidtestAssertThatHamcrestCoreMatchers(){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(newObject(),not(sameInstance(newObject())));}@TestpublicvoidtestAssertTrue(){assertTrue("failure - should be true",true);}}

2.3.测试执行的顺序

importorg.junit.*;/**
 * JunitTest
 *
 * @author Brian Tse
 * @since 2021/10/19 9:10
 */publicclassJunitTest{@BeforeClasspublicstaticvoidbeforeClass(){System.out.println("@BeforeClass --> 全局只会执行一次,而且是第一个运行");}@AfterClasspublicstaticvoidafterClass(){System.out.println("@AfterClass --> 全局只会执行一次,而且是最后一个运行");}@Beforepublicvoidbefore(){System.out.println("@Before --> 在测试方法运行之前运行(每个测试方法之前都会执行一次)");}@Afterpublicvoidafter(){System.out.println("@After --> 在测试方法运行之后允许(每个测试方法之后都会执行一次)");}@TestpublicvoidtestCase1(){System.out.println("@Test --> 测试方法1");}@TestpublicvoidtestCase2(){System.out.println("@Test --> 测试方法2");}}

执行结果如下:

@BeforeClass --> 全局只会执行一次,而且是第一个运行

@Before --> 在测试方法运行之前运行(每个测试方法之前都会执行一次)
@Test --> 测试方法1
@After --> 在测试方法运行之后允许(每个测试方法之后都会执行一次)

@Before --> 在测试方法运行之前运行(每个测试方法之前都会执行一次)
@Test --> 测试方法2
@After --> 在测试方法运行之后允许(每个测试方法之后都会执行一次)

@AfterClass --> 全局只会执行一次,而且是最后一个运行

Process finished with exit code 0

2.4.异常测试

如何验证代码是否按预期抛出异常?验证代码是否正常完成很重要,但确保代码在异常情况下按预期运行也很重要。
例如:

newArrayList<Object>().get(0);

此代码也许会引发IndexOutOfBoundsException。JUnit中有多种方法可以编写测试来验证此行为。

2.4.1.使用

assertThrows

方法

JUnit 4.13版本可以使用

assertThrows

方法。此方法可以断言给定的函数调用(例如,指定为lambda表达式或方法引用)会导致引发特定类型的异常。此外,它还返回抛出的异常,以便进一步断言(例如,验证抛出的消息和原因是否符合预期)。此外,可以在引发异常后对域对象的状态进行断言。

importorg.junit.Test;importjava.util.ArrayList;importjava.util.List;importstaticorg.junit.Assert.*;/**
 * ExceptionTest
 *
 * @author Brian Tse
 * @since 2021/10/19 9:25
 */publicclassExceptionTest{@TestpublicvoidtestExceptionAndState(){List<Object> list =newArrayList<>();// 可以断言给定的函数调用会导致引发特定类型的异常IndexOutOfBoundsException thrown =assertThrows(IndexOutOfBoundsException.class,()-> list.add(1,newObject()));// 根据返回抛出的异常,进一步断言抛出的消息和原因是否符合预期assertEquals("Index: 1, Size: 0", thrown.getMessage());// 在引发异常后对域对象的状态进行断言assertTrue(list.isEmpty());}}

2.4.2.Try/Catch 语句

如果项目中尚未使用JUnit 4.13或代码库不支持lambdas,则可以使用JUnit 3.x中流行的try/catch习惯用法:

importorg.junit.Test;importjava.util.ArrayList;importjava.util.List;importstaticorg.hamcrest.core.Is.is;importstaticorg.junit.Assert.assertThat;importstaticorg.junit.Assert.fail;/**
 * ExceptionTest
 *
 * @author Brian Tse
 * @since 2021/10/19 9:25
 */publicclassExceptionTest{@TestpublicvoidtestExceptionAndState(){List<Object> list =newArrayList<>();try{Object o = list.get(0);fail("Expected an IndexOutOfBoundsException to be thrown");}catch(IndexOutOfBoundsException anIndexOutOfBoundsException){assertThat(anIndexOutOfBoundsException.getMessage(),is("Index: 0, Size: 0"));}}}

2.4.3.expected 参数和 @Test 注释一起使用

Junit 用代码处理提供了一个追踪异常的选项。你可以测试代码是否它抛出了想要得到的异常。expected 参数和 @Test 注释一起使用。现在让我们看看 @Test(expected)。

importorg.junit.Test;importjava.util.ArrayList;/**
 * ExceptionTest
 *
 * @author Brian Tse
 * @since 2021/10/19 9:25
 */publicclassExceptionTest{@Test(expected =IndexOutOfBoundsException.class)publicvoidtestExceptionAndState(){newArrayList<Object>().get(0);}}

应谨慎使用’expected’参数。如果方法中的any代码抛出’IndexOutOfBoundsException’,则上述测试将通过。使用该方法,无法测试异常中消息的值,或引发异常后域对象的状态。

2.4.4.预期异常规则

另一种测试异常的方法是’预期异常’规则,但这种方法在JUnit4.13中已被弃用。通过此规则,您不仅可以指示所需的异常,还可以指示所需的异常消息。

importorg.junit.Rule;importorg.junit.Test;importorg.junit.rules.ExpectedException;importjava.util.ArrayList;importjava.util.List;/**
 * ExceptionTest
 *
 * @author Brian Tse
 * @since 2021/10/19 9:25
 */publicclassExceptionTest{@RulepublicExpectedException thrown =ExpectedException.none();@TestpublicvoidshouldTestExceptionMessage()throwsIndexOutOfBoundsException{List<Object> list =newArrayList<Object>();

        thrown.expect(IndexOutOfBoundsException.class);
        thrown.expectMessage("Index: 0, Size: 0");// execution will never get past this line
        list.get(0);}}

expectMessage还允许使用Matchers,这为您的测试提供了更大的灵活性。例如:

thrown.expectMessage(CoreMatchers.containsString("Size: 0"));

此外,可以使用匹配器来检查异常,如果它具有您希望验证的嵌入状态,这将非常有用。例如

importstaticorg.hamcrest.Matchers.hasProperty;importstaticorg.hamcrest.Matchers.is;importstaticorg.hamcrest.Matchers.startsWith;importjavax.ws.rs.NotFoundException;importjavax.ws.rs.core.Response;importjavax.ws.rs.core.Response.Status;importorg.junit.Rule;importorg.junit.Test;importorg.junit.rules.ExpectedException;publicclassTestExy{@RulepublicExpectedException thrown =ExpectedException.none();@TestpublicvoidshouldThrow(){TestThing testThing =newTestThing();
    thrown.expect(NotFoundException.class);
    thrown.expectMessage(startsWith("some Message"));
    thrown.expect(hasProperty("response",hasProperty("status",is(404))));
    testThing.chuck();}privateclassTestThing{publicvoidchuck(){Response response =Response.status(Status.NOT_FOUND).entity("Resource not found").build();thrownewNotFoundException("some Message", response);}}}

有关“ExpectedException”规则的详细讨论,请参阅此博文.

请注意,当测试调用抛出异常的被测方法时,该方法之后的测试中不会执行任何代码(因为被测方法正在抛出异常)。这可能会导致混淆,也是JUnit 4.13废弃’ExpectedException.none()'的原因之一。

2.5.忽略测试

带有@Ignore注解的测试方法不会被执行

@Ignore@TestpublicvoidtestCase2(){System.out.println("@Test --> 测试方法2");}

2.6.超时测试

JUnit提供了一个超时选项,如果一个测试用例比起指定的毫秒数花费了更多的时间,那么JUnit将自动将它标记为失败,timeout参数和@Test注解一起使用,例如@Test(timeout=1000)。 继续使用刚才的例子,现在将testCase1的执行时间延长到2000毫秒,并加上时间参数,设置超时为1000毫秒,然后执行测试类

@Test(timeout =1000)publicvoidtestCase1()throwsInterruptedException{TimeUnit.SECONDS.sleep(2);System.out.println("@Test --> 测试方法1");}

结果如下:

org.junit.runners.model.TestTimedOutException: test timed out after 1000 milliseconds

    at java.lang.Thread.sleep(Native Method)
    at java.lang.Thread.sleep(Thread.java:340)
    at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
    at com.brian.junit4.demo.JunitTest.testCase1(JunitTest.java:38)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.FailOnTimeout$CallableStatement.call(FailOnTimeout.java:298)
    at org.junit.internal.runners.statements.FailOnTimeout$CallableStatement.call(FailOnTimeout.java:292)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.lang.Thread.run(Thread.java:748)

2.7.参数化测试

JUnit 4引入了一项名为参数化测试的新功能。参数化测试允许开发人员使用不同的值反复运行相同的测试。创建参数化测试需要遵循五个步骤:

  1. 使用@RunWith(Parameterized.class)注释测试类。
  2. 创建一个使用@Parameters注释的公共静态方法,该方法返回一个对象集合作为测试数据集。
  3. 创建一个公共构造函数,它接受相当于一行“测试数据”的内容。
  4. 为测试数据的每个“列”创建一个实例变量。
  5. 使用实例变量作为测试数据的来源创建测试用例。

对于每行数据,将调用一次测试用例。让我们看看参数化测试的实际效果。

importorg.junit.Before;importorg.junit.Test;importorg.junit.runner.RunWith;importorg.junit.runners.Parameterized;importjava.util.Arrays;importjava.util.Collection;importstaticorg.junit.Assert.assertEquals;/**
 * PrimeNumberCheckerTest
 *
 * @author Brian Tse
 * @since 2021/10/19 10:44
 *///使用 @RunWith(Parameterized.class)注释测试类@RunWith(Parameterized.class)publicclassPrimeNumberCheckerTest{// 为测试数据的每个“列”创建一个实例变量privateInteger inputNumber;privateBoolean expectedResult;privatePrimeNumberChecker primeNumberChecker;@Beforepublicvoidinitialize(){
        primeNumberChecker =newPrimeNumberChecker();}// 创建一个公共构造函数,它接受相当于一行“测试数据”的内容publicPrimeNumberCheckerTest(Integer inputNumber,Boolean expectedResult){this.inputNumber = inputNumber;this.expectedResult = expectedResult;}//创建一个使用 @Parameters注释的公共静态方法,该方法返回一个对象集合作为测试数据集@Parameterized.ParameterspublicstaticCollectionprimeNumbers(){returnArrays.asList(newObject[][]{{2,true},{6,false},{19,true},{22,false},{23,true}});}@TestpublicvoidtestPrimeNumberChecker(){// 使用实例变量作为测试数据的来源创建测试用例System.out.println("Parameterized Number is : "+ inputNumber);assertEquals(expectedResult, primeNumberChecker.validate(inputNumber));}}

结果如下:

Parameterized Number is : 2

Parameterized Number is : 6

Parameterized Number is : 19

Parameterized Number is : 22

Parameterized Number is : 23

2.8.assertThat和Matchers

2.8.1.assertThat

此断言语法的优点包括:更具可读性和可键入性。

JUnit中的部分断言的可读性并不是很好,有时我们不得不自己编写表达式并断言其结果,并且因为我们没有提供失败的信息,当这个断言失败时只会抛出java.lang.AssertionError,无法知道到底是哪一部分出错。

assertTrue(responseString.contains("color")|| responseString.contains("colour"));// ==> failure message: // java.lang.AssertionError:assertThat(responseString,anyOf(containsString("color"),containsString("colour")));// ==> failure message:// java.lang.AssertionError: // Expected: (a string containing "color" or a string containing "colour")//      but: was "responseString字符串"

JUnit4.4引入了Hamcrest框架,Hamcest提供了一套匹配符Matcher,这些匹配符更接近自然语言,可读性高,更加灵活。并且使用全新的断言语法:assertThat,结合Hamcest提供的匹配符,只用这一个方法,就可以实现所有的测试。

assertThat语法如下:

  • assertThat(T actual, Matcher matcher);
  • assertThat(String reason, T actual, Matcher matcher);

其中reason为断言失败时的输出信息,actual为断言的值或对象,matcher为断言的匹配器,里面的逻辑决定了给定的actual对象满不满足断言。

JUnit4的匹配器定义在org.hamcrest.CoreMatchers 和 org.junit.matchers.JUnitMatchers 中。通过静态导入的方式引入相应的匹配器。

2.8.2.JUnitMatchers

org.junit.matchers.JUnitMatchers比较器中大部分都被标记为Deprecated,并使用org.hamcrest.CoreMatchers对应方法进行取代,但有两个方法得到了保留:

static<TextendsException>Matcher<T>isException(Matcher<T> exceptionMatcher)static<TextendsThrowable>Matcher<T> isThrowable Matcher<T> throwableMatcher)

2.8.3.CoreMatchers

Hamcrest CoreMatchers在JUnit4.9版本被包含在JUnit的分发包中。

@Testpublicvoidtest(){// 一般匹配符// allOf:所有条件必须都成立,测试才通过assertThat(actual,allOf(greaterThan(1),lessThan(3)));// anyOf:只要有一个条件成立,测试就通过assertThat(actual,anyOf(greaterThan(1),lessThan(1)));// anything:无论什么条件,测试都通过assertThat(actual,anything());// is:变量的值等于指定值时,测试通过assertThat(actual,is(2));// not:和is相反,变量的值不等于指定值时,测试通过assertThat(actual,not(1));// 数值匹配符// closeTo:浮点型变量的值在3.0±0.5范围内,测试通过assertThat(actual,closeTo(3.0,0.5));// greaterThan:变量的值大于指定值时,测试通过assertThat(actual,greaterThan(3.0));// lessThan:变量的值小于指定值时,测试通过assertThat(actual,lessThan(3.5));// greaterThanOrEuqalTo:变量的值大于等于指定值时,测试通过assertThat(actual,greaterThanOrEqualTo(3.3));// lessThanOrEqualTo:变量的值小于等于指定值时,测试通过assertThat(actual,lessThanOrEqualTo(3.4));// 字符串匹配符// containsString:字符串变量中包含指定字符串时,测试通过assertThat(actual,containsString("ci"));// startsWith:字符串变量以指定字符串开头时,测试通过assertThat(actual,startsWith("Ma"));// endsWith:字符串变量以指定字符串结尾时,测试通过assertThat(actual,endsWith("i"));// euqalTo:字符串变量等于指定字符串时,测试通过assertThat(actual,equalTo("Magci"));// equalToIgnoringCase:字符串变量在忽略大小写的情况下等于指定字符串时,测试通过assertThat(actual,equalToIgnoringCase("magci"));// equalToIgnoringWhiteSpace:字符串变量在忽略头尾任意空格的情况下等于指定字符串时,测试通过assertThat(actual,equalToIgnoringWhiteSpace(" Magci   "));// 集合匹配符// hasItem:Iterable变量中含有指定元素时,测试通过assertThat(actual,hasItem("Magci"));// hasEntry:Map变量中含有指定键值对时,测试通过assertThat(actual,hasEntry("mgc","Magci"));// hasKey:Map变量中含有指定键时,测试通过assertThat(actual,hasKey("mgc"));// hasValue:Map变量中含有指定值时,测试通过assertThat(actual,hasValue("Magci"));}
标签: 单元测试 java junit

本文转载自: https://blog.csdn.net/weixin_43498556/article/details/120839089
版权归原作者 修猿 所有, 如有侵权,请联系我们删除。

“JUnit 4 超详细教程(一)”的评论:

还没有评论