1.原理图
2. 基本架构
被测代码
package com.jerry; public class Calculator{ private static int result; public void add(int n) { result = result + n; } public void substract(int n) { result = result - n; } public void multiply(int n) { result = result * n; } public void divide(int n){ try { result = result / n; }catch(ArithmeticException ex){ System.out.println(ex); throw new ArithmeticException("The n not allowed to 0!!") } } } |
最基本的测试代码
public class CalculatorTest { private static Calculator calculator = new Calculator(); @BeforeEach public void setUp() throws Exception { calculator.clear(); } @Test @DisplayName("测试加法") public void testAdd() { calculator.add(2); calculator.add(3); assertEquals(5, calculator.getResult()); } @Test @DisplayName("测试减法") public void testSubstract() { calculator.add(5); calculator.substract(3); Assertions.assertEquals(2, calculator.getResult()); } @Test @DisplayName("测试乘法") public void testMultiply() { calculator.add(3); calculator.multiply(2); Assertions.assertEquals(6, calculator.getResult()); } @Test @DisplayName("测试除法") public void testDivide() { calculator.add(9); calculator.divide(3); Assertions.assertEquals(3, calculator.getResult()); } |
标签@DisplayName可以在测试结果的时候显示其内容。
3.JUnit5的修饰符
描述装饰符的程序
package com.jerry; import static org.junit.jupiter.api.Assertions.assertAll; import org.junit.jupiter.api.*; public class MyFirstJunit5Test { @BeforeAll @DisplayName("每条用例开始时执行") public static void startALL(){ System.out.println("BeforeAll"); } @AfterAll @DisplayName("每条用例开始时执行") public static void endAll(){ System.out.println("AfterAll"); } @BeforeEach @DisplayName("每条用例开始时执行") void start(){ System.out.println("BeforeEach"); } @AfterEach @DisplayName("每条用例结束时执行") void end(){ System.out.println("AfterEach"); } @Test void myFirstTest() { System.out.println("myFirstTest"); Assertions.assertEquals(2, 1 + 1); } @Test @DisplayName("描述测试用例") void testWithDisplayName() { System.out.println("testWithDisplayName"); } @Test @Disabled("这条用例暂时跑不过,忽略!") void myFailTest(){ System.out.println("Disabled Testcase"); Assertions.assertEquals(1,2); } @Test @DisplayName("运行一组断言") public void assertAllCase() { System.out.println("运行一组断言"); assertAll("groupAssert", () -> Assertions.assertEquals(2, 1 + 1), () -> Assertions.assertTrue(1 > 0) ); } @Test @DisplayName("依赖注入1") public void testInfo(final TestInfo testInfo) { System.out.println(testInfo.getDisplayName()); } @Test @DisplayName("依赖注入2") public void testReporter(final TestReporter testReporter) { testReporter.publishEntry("name", "Alex"); } } |
运行结果(缩进为了看起来方便,我自己设置的)
BeforeAll BeforeEach 依赖注入1 AfterEach BeforeEach TestIdentifier [依赖注入2] ReportEntry [timestamp = 2020-08-21T11:38:49.361976500, name = 'Alex’] AfterEach BeforeEach myFirstTest AfterEach BeforeEach testWithDisplayName AfterEach BeforeEach 运行一组断言 AfterEach AfterAll |
注意:@BeforeAll、 @AfterALL注解方法必须是静态方法,否则会抛出运行时错误。
4. JUnit5 新加断言
5. 异常断言
被测的程序
public void divide(int n){ try { result = result / n; }catch(ArithmeticException ex){ System.out.println(ex); throw new ArithmeticException("The n not allowed to 0!!"); } } |
测试程序
@Test @DisplayName("测试除0异常") public void testDivideByZero() { calculator.add(9); Throwable exception = Assertions.assertThrows(ArithmeticException.class, () -> calculator.divide(0)); Assertions.assertEquals("The n not allowed to 0!!",exception.getMessage()); } |
6. 超时断言
@Test public void squareRoot() { Assertions.assertTimeoutPreemptively(Duration.of(2, ChronoUnit.SECONDS), () -> calculator.squareRoot(4)) } |
判断程序是否应该在两秒钟执行完毕。类似于JUnit4中的(timeout=XXX)。
7. 参数化测试
7.1单参数
@ParameterizedTest @ValueSource(ints = {1,2,3,4}) @DisplayName("参数化测试_单参数") public void parameterizedTest(int param) { calculator.add(param); calculator.add(-1 * param); Assertions.assertEquals(0, calculator.getResult()); } |
标签:
·@ValueSource: 为参数化测试指定入参来源,支持八大基础类以及String类型,Class类型
·@NullSource: 表示为参数化测试提供一个null的入参
·@EnumSource: 表示为参数化测试提供一个枚举入参
ValueSource类型:
·String values: @ValueSource(strings = {"foo", "bar", "baz"})
·Double values: @ValueSource(doubles = {1.5D, 2.2D, 3.0D})
·Long values: @ValueSource(longs = {2L, 4L, 8L})
·Integer values: @ValueSource(ints = {2, 4, 8})
7.2 Enum参数
import java.util.concurrent.TimeUnit; … @ParameterizedTest @EnumSource(value = TimeUnit.class, names = {"SECONDS", "MINUTES"}) @DisplayName("参数化测试_Enum参数") void testTimeUnitJustSecondsAndMinutes(TimeUnit unit) { Assertions.assertTrue(EnumSet.of(TimeUnit.SECONDS, TimeUnit.MINUTES).contains(unit)); Assertions.assertFalse(EnumSet .of(TimeUnit.DAYS, TimeUnit.HOURS, TimeUnit.MILLISECONDS, TimeUnit.NANOSECONDS, TimeUnit.MICROSECONDS).contains(unit)); } |
7.3参数化测试——方法参数(多参数)
注意:这个方法返回必须是个流
@ParameterizedTest @MethodSource("paramGenerator") @DisplayName("参数化测试_ 方法参数(多参数)") void MethodParameForSquareRoot(int param, int result){ calculator.squareRoot(param); Assertions.assertEquals(result, calculator.getResult()); } static Stream<Arguments> paramGenerator(){ return Stream.of(Arguments.of(4,2), Arguments.of(9,3), Arguments.of(16,4)); } |
7.4.参数化测试——CVS文件参数化
@ParameterizedTest @CsvFileSource(resources = "data.csv") //指定csv文件位置 @DisplayName("参数化测试-csv文件") public void parameterizedCVSFile(int param, int result) { calculator.squareRoot(param); Assertions.assertEquals(result, calculator.getResult()); } |
其中,data.csv为:
42 93 164 |
8.内嵌测试类
一般一个产品类对应一个测试类,但是使用JUnit,可以实现类的嵌套。
package com.jerry; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; public class NestedTestDemo { @Test @DisplayName("Nested") void isInstantiatedWithNew() { System.out.println("最一层--内嵌单元测试"); } @Nested @DisplayName("Nested2") class Nested2 { @BeforeEach void Nested2_init() { System.out.println("Nested2_init"); } @Test void Nested2_test() { System.out.println("第二层-内嵌单元测试"); } @Nested @DisplayName("Nested3") class Nested3 { @BeforeEach void Nested3_init() { System.out.println("Nested3_init"); } @Test void Nested3_test() { System.out.println("第三层-内嵌单元测试"); } } } } |
结果输出:
第一层--内嵌单元测试
Nested2_init
第二层-内嵌单元测试
Nested2_init
Nested3_init
第三层-内嵌单元测试
9. 重复测试
@RepeatedTest(5) //表示重复执行5次 @DisplayName("重复减法") public void testSubstractManyTimes() { calculator.add(5); calculator.substract(3); Assertions.assertEquals(2, calculator.getResult()); } |
这个测试用例将被执行5次,为什么设计这个方法,我个人没有理解。
10. 动态测试
@TestFactory @DisplayName("动态测试") Iterator<DynamicTest> dynamicTests() { return Arrays.asList( dynamicTest("第1个动态测试", () ->{calculator.squareRoot(4);Assertions.assertEquals(2, calculator.getResult());}), dynamicTest("第2个动态测试", () ->{calculator.squareRoot(9);Assertions.assertEquals(3, calculator.getResult());}), dynamicTest("第3个动态测试", () ->{calculator.squareRoot(16);Assertions.assertEquals(4, calculator.getResult());}), dynamicTest("第4个动态测试", () ->{calculator.squareRoot(25);Assertions.assertEquals(5, calculator.getResult());}) ).iterator(); } |
动态测试用得比较少,这个功能的原因我个人也不太理解。
11. 分组断言assertAll
@Test @DisplayName("开根号分组断言") public void testGroup() { int[] parem = {4, 9, 16, 25, 36}; int[] result ={2, 3, 4, 5, 6}; Assertions.assertAll("parem,result", () -> {calculator.squareRoot(parem[0]);Assertions.assertEquals(result[0], calculator.getResult());}, () -> {calculator.squareRoot(parem[1]);Assertions.assertEquals(result[1], calculator.getResult());}, () -> {calculator.squareRoot(parem[2]);Assertions.assertEquals(result[2], calculator.getResult());}, () -> {calculator.squareRoot(parem[3]);Assertions.assertEquals(result[3], calculator.getResult());}, () -> {calculator.squareRoot(parem[4]);Assertions.assertEquals(result[4], calculator.getResult());} ); |
分组断言中任一个断言的失败,都会将以 MultipleFailuresError 错误进行抛出提示。另外可以看出,使用分组断言也可以实现参数化
本文出自51Testing会员投稿,51Testing软件测试网及相关内容提供者拥有内容的全部版权,未经明确的书面许可,任何人或单位不得对本网站内容复制、转载或进行镜像,否则将追究法律责任。