51Testing软件测试论坛
标题:
基于JUnit5框架的Java单元测试流程
[打印本页]
作者:
lsekfe
时间:
2020-12-7 11:20
标题:
基于JUnit5框架的Java单元测试流程
1.原理图
[attach]131190[/attach]
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可以在测试结果的时候显示其内容。
[attach]131191[/attach]
3.JUnit5的修饰符
[attach]131192[/attach]
描述装饰符的程序
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 新加断言
[attach]131193[/attach]
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
第三层-内嵌单元测试
[attach]131194[/attach]
9. 重复测试
@RepeatedTest(5) //表示重复执行5次
@DisplayName("重复减法")
public void testSubstractManyTimes() {
calculator.add(5);
calculator.substract(3);
Assertions.assertEquals(2, calculator.getResult());
}
复制代码
这个测试用例将被执行5次,为什么设计这个方法,我个人没有理解。
10. 动态测试
<strong> </strong>@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软件测试论坛 (http://bbs.51testing.com/)
Powered by Discuz! X3.2