SpringBoot使用Junit5
Junit5 基本语法
Junit5官网 https://junit.org/junit5/
注释
JUnit Jupiter 支持以下用于配置测试和扩展框架的注解。
除非另有说明,所有核心注释都位于模块中的org.junit.jupiter.api包中junit-jupiter-api。
注解 | 描述 |
---|---|
@Test | 表示方法是测试方法。与 JUnit 4 的@Test注解不同,该注解不声明任何属性,因为 JUnit Jupiter 中的测试扩展基于它们自己的专用注解进行操作。这些方法是继承的,除非它们被覆盖。 |
@ParameterizedTest | 表示方法是参数化测试。这些方法是继承的,除非它们被覆盖。 |
@RepeatedTest | 表示方法是重复测试的测试模板。这些方法是继承的,除非它们被覆盖。 |
@TestFactory | 表示方法是动态测试的测试工厂。这些方法是继承的,除非它们被覆盖。 |
@TestTemplate | 表示方法是测试用例的模板,旨在根据注册提供程序返回的调用上下文的数量多次调用。这些方法是继承的,除非它们被覆盖。 |
@TestMethodOrder | 用于为注解的测试类配置测试方法执行顺序;类似于 JUnit 4 的@FixMethodOrder. 此类注释是继承的。 |
@TestInstance | 用于为带注释的测试类配置测试实例生命周期。此类注释是继承的。 |
@DisplayName | 声明测试类或测试方法的自定义显示名称。此类注释不会被继承。 |
@DisplayNameGeneration | 为测试类声明一个自定义显示名称生成器。此类注释是继承的。 |
@BeforeEach | 表示被注解的方法应该在当前类中的每个、、、 或方法之前 执行;类似于 JUnit 4 的. 这些方法是继承的,除非它们被覆盖。 @Test@RepeatedTest@ParameterizedTest@TestFactory@Before |
@AfterEach | 表示该注释的方法应该被执行之后 每个 @Test,@RepeatedTest,@ParameterizedTest,或@TestFactory方法在当前类; 类似于 JUnit 4 的@After. 这些方法是继承的,除非它们被覆盖。 |
@BeforeAll | 表示该注释的方法应该被执行之前 所有 @Test,@RepeatedTest,@ParameterizedTest,和@TestFactory方法在当前类; 类似于 JUnit 4 的@BeforeClass. 此类方法是继承的(除非它们被隐藏或覆盖)并且必须是static(除非使用“每类”测试实例生命周期)。 |
@AfterAll | 表示该注释的方法应该被执行之后 的所有 @Test,@RepeatedTest,@ParameterizedTest,和@TestFactory方法在当前类; 类似于 JUnit 4 的@AfterClass. 此类方法是继承的(除非它们被隐藏或覆盖)并且必须是static(除非使用“每类”测试实例生命周期)。 |
@Nested | 表示被注解的类是一个非静态嵌套测试类。@BeforeAll和@AfterAll方法不能直接在@Nested测试类中使用,除非使用“每类”测试实例生命周期。此类注释不会被继承。 |
@Tag | 用于在类或方法级别声明过滤测试的标签;类似于 TestNG 中的测试组或 JUnit 4 中的类别。此类注释在类级别继承,但不在方法级别继承。 |
@Disabled | 用于禁用测试类或测试方法;类似于 JUnit 4 的@Ignore. 此类注释不会被继承。 |
@Timeout | 如果测试、测试工厂、测试模板或生命周期方法的执行超过给定的持续时间,则用于使测试失败。此类注释是继承的。 |
@ExtendWith | 用于以声明方式注册扩展。此类注释是继承的。 |
@RegisterExtension | 用于通过字段以编程方式注册扩展。这些字段是继承的,除非它们被隐藏。 |
@TempDir | 用于在生命周期方法或测试方法中通过字段注入或参数注入提供临时目录;位于org.junit.jupiter.api.io包中。 |
@ParameterizedTest 参数化测试
结合@ValueSource @CsvSource 构建要测试的参数
@NullSource: 提供Null参数 @EmptySource:提供空参数String list map 数组
@NullAndEmptySource 组合注释,结合了@NullSource和的功能 @EmptySource
如测试回文的demo
@ParameterizedTest
@ValueSource(strings = { "racecar", "radar", "able was I ere I saw elba" })
void palindromes(String candidate) {
assertTrue(StringUtils.isPalindrome(candidate));
}
回文方法
public static boolean isPalindrome(String str)
{
if(str==null||str.length()==0)
{
throw new RuntimeException("字符串为空");
}
int mid=(str.length()-1)/2;
for(int i=0;i<=mid;i++)
{
if(str.charAt(i)!=str.charAt(str.length()-1-i))
{
return false;
}
}
return true;
}
测试输出
palindromes(String) ✔
├─ [1] candidate=racecar ✔
├─ [2] candidate=radar ✔
└─ [3] candidate=able was I ere I saw elba ✔
@CsvSource示例
@ParameterizedTest(name = "{0} + {1} = {2}")
@CsvSource({
"0, 1, 1",
"1, 2, 3",
"49, 51, 100",
"1, 100, 101"
})
void add(int first, int second, int expectedResult) {
Calculator calculator = new Calculator();
String express = StrUtil.format("{}+{}",first,second);
// hutool calculator
assertEquals(expectedResult, calculator.calculate(express),
() -> first + " + " + second + " should equal " + expectedResult);
}
测试输出
✔add(int, int, int)
✔0 + 1 = 1
✔1 + 2 = 3
✔49 + 51 = 100
✔1 + 100 = 101
@CsvSource中NIL表示null
示例输入 结果参数列表
@CsvSource({ "apple, banana" }) "apple", "banana"
@CsvSource({ "apple, 'lemon, lime'" }) "apple", "lemon, lime"
@CsvSource({ "apple, ''" }) "apple", ""
@CsvSource({ "apple, " }) "apple", null
@CsvSource(value = { "apple, banana, NIL" }, nullValues = "NIL") "apple", "banana",null
@CsvFileSource从csv文件中加载数据
@CsvFileSource(resources = "/two-column.csv", numLinesToSkip = 1)
@Timeout 超时测试
程序运行超过时间则失败
@Test
@Timeout(value = 200,unit = TimeUnit.MILLISECONDS)
void timeout(){
ThreadUtil.sleep(1000);
}
@DisplayName 显示名称
可以输入表情符,是不是很好玩
@Test
@DisplayName("╯°□°)╯")
void testWithDisplayNameContainingSpecialCharacters() {
}
@Test
@DisplayName("😱")
void testWithDisplayNameContainingEmoji() {
}
断言Assertions
Assertions类提供了一些断言方法
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTimeout;
import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
import static org.junit.jupiter.api.Assertions.assertTrue;
重复测试@RepeatedTest
对同一测试执行多次,如@Timeout 测试服务耗时,测试内容有随机变量,运行多次测试。
@RepeatedTest(value = 10, name = "{displayName} {currentRepetition}/{totalRepetitions}")
@DisplayName("Repeat!")
void customDisplayName(TestInfo testInfo) {
// assertEquals("Repeat! 1/10", testInfo.getDisplayName());
}
@TestFactory 测试工厂
生成测试方法与DynamicTest结合
如下生成两个测试方法
@TestFactory
Collection dynamicTestsFromCollection() {
return Arrays.asList(
dynamicTest("1st dynamic test", () -> assertTrue(isPalindrome("madam"))),
dynamicTest("2nd dynamic test", () -> assertEquals(4, calculator.multiply(2, 2)))
);
}
SpringBoot测试
引入maven依赖
org.springframework.boot
spring-boot-starter-test
test
@SpringBootTest 会加载springBoot的上下文环境,之后可以使用spring容器中的bean直接@Autowired注入
@AutoConfigureMockMvc 紫宗配置MockMvc,使用直接@Autowired注入
@SpringBootTest
@AutoConfigureMockMvc
class SkyControllerTest {
@Autowired
MockMvc mockMvc;
@Test
void sky () throws Exception {
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get("/sky"))
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().isOk())
.andReturn();
String content = mvcResult.getResponse().getContentAsString();
System.out.println(content);
}
@Test
void deliver () throws Exception {
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/deliver").param("box","iphone"))
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().isOk())
.andReturn();
String content = mvcResult.getResponse().getContentAsString();
System.out.println(content);
}
}
若不加@AutoConfigureMockMvc 可以手动生成MockMvc对象。
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();