Junit与Spring Test简单使用
- Junit5简介
- Junit5 注解
- Junit5与Spring结合
- 差异概览
- Mocking
- @MockBean
- @SpyBean
- Demo
- 注意事项
又要写测试代码了,总结记录一下。
Junit5简介
与单一模块设计的Junit4不同,Junit5引入了模块化架构,由三个主要子项目组成:
- JUnit Platform:测试运行的基础平台,支持不同的测试引擎(不仅仅是 JUnit,还可以扩展其他测试框架,如 TestNG)。
- JUnit Jupiter:JUnit5 的新编程模型和扩展模型,包含新的注解和测试方法(如 @Test, @BeforeEach,@AfterEach 等)。
- JUnit Vintage:提供对 JUnit4 及更早版本的向后兼容支持,因此 JUnit5 可以运行旧的 JUnit4 测试代码。
Junit5 注解
- @BeforeEach 和 @AfterEach:取代了 JUnit4 的 @Before 和 @After,作用于每个测试方法。
- @BeforeAll 和 @AfterAll:取代了 JUnit4 的 @BeforeClass 和 @AfterClass,可以作用于整个类生命周期,且在 JUnit5 中可以是非静态方法(通过注入 TestInstance)。
- @DisplayName:允许为测试方法和类指定自定义名称,方便生成更具可读性的测试报告。
- @Nested:支持嵌套的测试类,便于组织复杂的测试场景。
- @ParameterizedTest:增强了参数化测试的支持,允许为测试方法传递多个参数集。
- DynamicTest:动态创建测试用例,支持灵活的测试流程。
@BeforeAll``@AfterAll
类级别,只执行一次
@BeforeEach
@AfterEach
方法级别,每个方法都会执行All 和 each的区别,在执行类级别测试时才能看出来: all仅执行一次,each执行次数取决于有多少个Test方法
Junit5与Spring结合
- 在与Spring集成时,不再使用@RunWith
- @ExtendWith, 指定拓展为Spring, 测试中可以使用Spring注解进行依赖注入,@ContextConfiguration指定配置类
- @Transactional, 测试中提供事务支持
- Spring框架提供的 @MockBean @SpyBean注解, 提供Mocking支持,模拟Bean行为
在springboot项目中,
@SpringBootTest
注解内部就是使用了
@ExtendWith({SpringExtension.class})`提前帮我们配置好了
差异概览
功能/特性 | JUnit4 | JUnit5 | Spring Test |
---|---|---|---|
架构 | 单一模块 | 模块化架构(Platform, Jupiter, Vintage) | 基于 TestContext 框架,与 JUnit 集成 |
注解 | @Test , @Before , @After | @Test , @BeforeEach , @AfterEach | @ContextConfiguration , @Transactional |
扩展机制 | @RunWith , TestRule | @ExtendWith , TestInstance | @ExtendWith(SpringExtension.class) |
参数化测试 | 较弱的参数化测试支持 | 强大的参数化测试支持 | 与 Spring 环境集成,支持 Mock 和依赖注入 |
事务管理 | N/A | N/A | 支持 @Transactional ,测试完成后自动回滚 |
Spring 集成 | @RunWith(SpringJUnit4ClassRunner.class) | @ExtendWith(SpringExtension.class) | 内置的对 Spring 上下文的管理和 Bean 注入支持 |
Mocking
@MockBean
如果不指定规则,则mockBean执行完返回默认值,即对象为null,数字为0
如果指定了规则,就按照规则返回,下面例子按照规则返回ok
@SpyBean
有规则按照规则走,没有规则按照真实服务进行。比如:在多服务调用过程中,如果部分服务不可用,可以定义规则,如果服务可用,则调用真实的服务。
Demo
import org.junit.jupiter.api.*;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.mock.mockito.SpyBean;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.doReturn;@SpringBootTest
class DemoTestApplicationTests {@SpyBeanMyService myService;// 所有方法使用myService方法,可自定义覆盖@MockBeanMyService2 myService2; // 所有方法需要自定义返回,否则使用默认初始值或null// 调用Spy模拟服务,@Testvoid invokeSpy(){// 仅定义hello的mock规则doReturn("hello handsome").when(myService).hello("wyy");assertEquals("hello handsome", myService.hello("wyy"));// 未定义规则,直接调用say()逻辑assertEquals("hello", myService.say("hello"));}// 调用Mock模拟服务,@Testvoid invokeMock(){// 仅定义hello的mock规则doReturn("hello handsome").when(myService2).hello("wyy");assertEquals("hello handsome", myService2.hello("wyy"));// 未定义规则,返回初始值或nullassertEquals("hello", myService2.say("hello"));}// All 和 each的区别,执行单独的测试方法区分不了,// 在执行类级别测试时才能看出来: all仅执行一次,each执行次数取决于有多少个Test方法@BeforeAllstatic void beforeAll(){System.out.println("before all");}@BeforeEachvoid beforeEach(){System.out.println("before each");}@AfterEachvoid afterEach(){System.out.println("after each");}@AfterAllstatic void afterAll(){System.out.println("after all");}
}
注意事项
- 使用断言进行判断,严禁System.out进行人工判断
- 丰富测试场景的多样性,通过不同参数测试增加多样性,提升测试覆盖率