package org.junit.jupiter.api; import static org.apiguardian.api.API.Status.STABLE; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.apiguardian.api.API; import org.junit.platform.commons.annotation.Testable; /** * {@code @Test} is used to signal that the annotated method is a * <em>test</em> method. * * <p>{@code @Test} methods must not be {@code private} or {@code static} * and must not return a value. * * <p>{@code @Test} methods may optionally declare parameters to be * resolved by {@link org.junit.jupiter.api.extension.ParameterResolver * ParameterResolvers}. * * <p>{@code @Test} may also be used as a meta-annotation in order to create * a custom <em>composed annotation</em> that inherits the semantics of * {@code @Test}. * * <h2>Test Execution Order</h2> * * <p>By default, test methods will be ordered using an algorithm that is * deterministic but intentionally nonobvious. This ensures that subsequent runs * of a test suite execute test methods in the same order, thereby allowing for * repeatable builds. In this context, a <em>test method</em> is any instance * method that is directly annotated or meta-annotated with {@code @Test}, * {@code @RepeatedTest}, {@code @ParameterizedTest}, {@code @TestFactory}, or * {@code @TestTemplate}. * * <p>Although true <em>unit tests</em> typically should not rely on the order * in which they are executed, there are times when it is necessary to enforce * a specific test method execution order — for example, when writing * <em>integration tests</em> or <em>functional tests</em> where the sequence of * the tests is important, especially in conjunction with * {@link TestInstance @TestInstance(Lifecycle.PER_CLASS)}. * * <p>To control the order in which test methods are executed, annotate your * test class or test interface with {@link TestMethodOrder @TestMethodOrder} * and specify the desired {@link MethodOrderer} implementation. * * @since 5.0 * @see RepeatedTest * @see org.junit.jupiter.params.ParameterizedTest * @see TestTemplate * @see TestFactory * @see TestInfo * @see DisplayName * @see Tag * @see BeforeAll * @see AfterAll * @see BeforeEach * @see AfterEach */ @Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @API(status = STABLE, since = "5.0") @Testable public @interface Test { }
JUnit 4 vs JUnit 5
以下是JUnit 4和JUnit 5注解之间的一些主要区别
这些是JUnit 4和JUnit 5之间的一些重要区别,JUnit 5引入了许多新的功能和改进,以提供更灵活、强大的测试框架。你可以根据项目的需要选择适合的JUnit版本。
Junit5 常用注解
@SpringBootTest: 用于指定测试类启用Spring Boot Test,默认会提供Mock环境。
@ExtendWith: 如果只想启用Spring环境进行简单测试,不想启用Spring Boot环境,可以配置扩展为:SpringExtension。
@Test: 指定方法为测试方法。
@TestMethodOrder: 用于配置测试类中方法的执行顺序策略,配置为OrderAnnotation时,按@Order顺序执行。
@Order: 用于配置方法的执行顺序,数字越低执行顺序越高。
@DisplayName: 用于指定测试类和测试方法的别名。
@BeforeAll: 在测试类的所有测试方法前执行一次,可用于全局初始化。
@AfterAll: 在测试类的所有测试方法后执行一次,可用于全局销毁资源。
@BeforeEach: 在测试类的每个测试方法前都执行一次。
@AfterEach: 在测试类的每个测试方法后都执行一次。
@Disabled: 禁用测试方法。
@RepeatedTest: 指定测试方法重复执行。
@ParameterizedTest: 指定参数化测试方法,类似重复执行,从@ValueSource中获取参数。
@ValueSource: 用于参数化测试指定参数。
@AutoConfigureMockMvc: 启用MockMvc的自动配置,可用于测试接口。
栗子
以下是上述注解的使用方法示例以及相应的Java代码:
@SpringBootTest
:用于指定Spring Boot测试。示例:
@SpringBootTest public class MySpringBootTest { // 测试方法 }
@ExtendWith
:用于配置测试类的执行环境。示例:
@ExtendWith(SpringExtension.class) public class MySpringTest { // 测试方法 }
@Test
:指定方法为测试方法。示例:
@Test public void testSomeMethod() { // 测试逻辑 }
4.@TestMethodOrder
和 @Order
:配置测试方法的执行顺序。示例:
@TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class OrderedTestExample { @Order(1) @Test public void testMethod1() { // 测试逻辑 } @Order(2) @Test public void testMethod2() { // 测试逻辑 } }
5.@DisplayName
:用于指定测试类和测试方法的别名。示例:
@DisplayName("My Test Suite") public class MyTestSuite { @Test @DisplayName("My Test Case") public void myTestCase() { // 测试逻辑 } }
6.@BeforeAll
和 @AfterAll
:在测试类的所有测试方法前和后执行一次,可用于全局初始化和销毁资源。示例:
@BeforeAll public static void setup() { // 初始化操作 } @AfterAll public static void teardown() { // 资源销毁操作 }
7.@BeforeEach
和 @AfterEach
:在测试类的每个测试方法前和后都执行一次。示例:
@BeforeEach public void beforeEachTest() { // 执行前的准备工作 } @AfterEach public void afterEachTest() { // 执行后的清理工作 }
@Disabled
:禁用测试方法。示例:
@Test @Disabled("This test is not ready yet.") public void disabledTest() { // 未完成的测试逻辑 }
@RepeatedTest
:指定测试方法重复执行。示例:
@RepeatedTest(5) public void repeatedTest() { // 该测试方法会重复执行5次 }
10.@ParameterizedTest
和 @ValueSource
:用于参数化测试。示例:
@ParameterizedTest @ValueSource(strings = { "apple", "banana", "cherry" }) public void testFruit(String fruit) { // 使用参数化的水果名称进行测试 }
11.@AutoConfigureMockMvc
:启用MockMvc的自动配置,可用于测试接口。示例:
@SpringBootTest @AutoConfigureMockMvc public class MyControllerIntegrationTest { @Autowired private MockMvc mockMvc; @Test public void testController() throws Exception { mockMvc.perform(get("/api/someendpoint")) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)); } }
这些示例演示了如何使用这些注解来编写JUnit 5和Spring Boot测试。您可以根据您的具体需求和测试场景进行相应的配置和使用。
package com.artisan.boottest.example; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest public class BasicTest { @Test public void test() { String artisan = "artisan good"; Assertions.assertEquals("artisan good", artisan); } }
package com.artisan.boottest.example; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.*; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.test.context.junit.jupiter.SpringExtension; /** * @author 小工匠 * @version 1.0 * @description: JUnit指定方法测试顺序 * @mark: show me the code , change the world */ @Slf4j @ExtendWith(SpringExtension.class) @TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class MethodOrderTest { @Test @Order(1) @DisplayName("order为1的方法") void lowOrder(){ log.info("lowOrder method"); } @Test @Order(2) @DisplayName("order为2的方法") void highOrder(){ log.info("highOrder method"); } }
package com.artisan.boottest.example; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.*; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.test.context.junit.jupiter.SpringExtension; /** * @author 小工匠 * @version 1.0 * @description: JUnit生命周期测试 * @mark: show me the code , change the world */ @Slf4j @ExtendWith(SpringExtension.class) public class LifecycleTest { @BeforeAll static void allInit() { log.info("allInit():在所有方法前执行,只执行一次"); } @BeforeEach void eachInit() { log.info("eachInit():在测试方法前执行,每个测试方法前都执行"); } @Test void successTest() { log.info("successTest():方法执行成功"); } @AfterEach void eachDown() { log.info("eachDown():在测试方法后执行,每个测试方法后都执行"); } @AfterAll static void allDown() { log.info("allDown():在测试方法后执行,每个测试方法后都执行"); } }
package com.artisan.boottest.example; import cn.hutool.core.thread.ThreadUtil; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.test.context.junit.jupiter.SpringExtension; import java.time.Duration; /** * @author 小工匠 * @version 1.0 * @description: JUnit断言测试 * @mark: show me the code , change the world */ @Slf4j @ExtendWith(SpringExtension.class) public class AssertTest { // 可以使用fail方法直接断言方法执行失败并输出提示信息。 @Test void failTest() { Assertions.fail("failTest():方法执行失败"); } // 还可以通过assertTrue、assertNull、assertEquals这类方法来断言结果是否符合预期。 @Test void trueTest(){ Assertions.assertTrue(666==666); } @Test void trueFalse(){ Assertions.assertFalse(8888<=9999); } @Test void nullTest(){ String str = null; Assertions.assertNull(str); } @Test void notNullTest(){ String str = "test"; Assertions.assertNotNull(str); } @Test void equalsTest(){ String str1 = "test"; String str2 = "test"; Assertions.assertEquals(str1,str2); } @Test void notEqualsTest(){ String str1 = "test"; String str2 = "test"; Assertions.assertNotEquals(str1,str2); } // 也可以使用assertThrows方法来断言方法中抛出的异常。 @Test void throwsTest(){ Assertions.assertThrows(NullPointerException.class,()->{ String str = null; log.info(str.toLowerCase()); }); } // 还可通过assertTimeout方法断言方法的执行时间。 @Test void timeoutTest(){ Assertions.assertTimeout(Duration.ofMillis(1000),()->{ long sleepTime = 2000; ThreadUtil.sleep(sleepTime); log.info("timeoutTest():休眠{}毫秒",sleepTime); }); } // 或者通过assertAll方法将几个断言结合起来使用,Assertions类中提供的工具方法很多,具体可以参考它的代码。 @Test void assertAllTest(){ Assertions.assertAll(()->{ trueTest(); },()->{ nullTest(); },()->{ equalsTest(); }); } }
package com.artisan.boottest.example; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import org.springframework.test.context.junit.jupiter.SpringExtension; /** * @author 小工匠 * @version 1.0 * @description: 其他常用的注释 * @mark: show me the code , change the world */ @Slf4j @ExtendWith(SpringExtension.class) public class OthreTest { // Spring Boot Test除了上述测试功能,还可以使用@Disabled来禁用某个测试方法 @Test @Disabled("用于测试@Disabled注解") void disabledTest() { log.info("disabledTest():方法被执行"); } // 也可以使用@RepeatedTest来实现循环测试 private static int count = 0; @RepeatedTest(5) void repeatedTest() { count++; log.info("repeatedTest():重复执行第{}次",count); } // 还可以通过@ParameterizedTest来进行参数化测试 @ParameterizedTest @ValueSource(ints = {1,2,3}) public void parameterizedTest(int a){ log.info("parameterizedTest():a={}",a); } }
【三层测试】
package com.artisan.boottest.project; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.jdbc.Sql; /** * @author 小工匠 * @version 1.0 * @description: Dao层方法测试 * @mark: show me the code , change the world */ @Slf4j @SpringBootTest public class MapperTest { @Autowired private PmsBrandMapper brandMapper; @Test void testGetById(){ long id = 6; PmsBrand pmsBrand = brandMapper.selectByPrimaryKey(id); LOGGER.info("brand name:{}",pmsBrand.getName()); Assertions.assertEquals("小米",pmsBrand.getName()); } }
package com.artisan.boottest.project; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; /** * @author 小工匠 * @version 1.0 * @description: Service层方法测试 * @mark: show me the code , change the world */ @Slf4j @SpringBootTest public class ServiceTest { @Autowired private PmsBrandService brandService; @Test void testGetById(){ long id = 6; PmsBrand pmsBrand = brandService.getBrand(id); log.info("brand name:{}",pmsBrand.getName()); Assertions.assertEquals("小米",pmsBrand.getName()); } }
package com.artisan.boottest.project; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultHandlers; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; /** * @author 小工匠 * @version 1.0 * @description: 对于Controller层方法进行测试,有时我们需要模拟请求,使用MockMvc即可 * @mark: show me the code , change the world */ @SpringBootTest @AutoConfigureMockMvc public class ControllerTest { @Autowired private MockMvc mockMvc; @Test void mvcTest() throws Exception { //模拟发送一个请求访问分页查询品牌列表的接口 mockMvc.perform(MockMvcRequestBuilders.get("/brand/list") //设置请求地址 .param("pageNum", "1") //设置请求参数 .param("pageSize", "5")) .andExpect(MockMvcResultMatchers.status().isOk()) //断言返回状态码为200 .andDo(MockMvcResultHandlers.print()) //在控制台打印日志 .andReturn(); //返回请求结果 } }