您的位置:

Spring Boot单元测试详解

一、JUnit5与Spring Boot Test

Spring Boot Test 是 Spring Boot 提供的测试框架,用于简化单元测试的编写过程。Spring Boot Test整合了许多JUnit5的特性,比如@ExtendWith,@Test,@Nested等。使用Spring Boot Test,我们可以做到“无环境”单元测试,即在不需要外部环境支持的情况下进行测试。

在使用Spring Boot Test时,需要引入spring-boot-starter-test依赖,如下:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

对于单元测试,可以使用@SpringBootTest注解,表示加载整个Spring应用上下文。@SpringBootTest使用随机端口启动应用程序,可以使用@LocalServerPort注解注入随机端口值。

    @SpringBootTest
    public class MyTest {

        @LocalServerPort
        private int port;

        private static final String URL_PREFIX = "http://localhost:";

        @Test
        public void test(){
            RestTemplate restTemplate = new RestTemplate();
            String result = restTemplate.getForObject(URL_PREFIX + port + "/hello", String.class);
            Assertions.assertEquals("hello", result);
        }
    }

二、Mockito进行依赖的Mock与Stub

Mockito是一个用于Java应用程序的单元测试框架,主要用于对依赖进行Mock或Stub。Mock指的是一个对象,并且根据需要返回预定义的输出。Stub指的是一个对象,如果根据需要返回预定义的输出,则执行代码的默认实现。

Mockito可以通过Mockito.mock()创建Mock对象。Mock对象只是一个空壳,它不会执行代码的默认实现,只会返回预先设置的值。Mockito使用when().thenReturn()设置预期返回值。

    public class MyTest {
        @Test
        public void test(){
            //创建Mock对象
            MyDAO myDaoMock = Mockito.mock(MyDAO.class);

            //设置预期返回值
            when(myDaoMock.getDataById(1)).thenReturn("data1");

            //调用被测方法,并验证返回值是否符合预期
            MyService myService = new MyService(myDaoMock);
            String result = myService.getDataById(1);
            Assertions.assertEquals("data1", result);

            //验证是否已经调用MyDAO的getDataById方法
            verify(myDaoMock, times(1)).getDataById(1);
        }
    }

三、Testcontainers进行外部环境的集成测试

Testcontainers是一个Java库,可在本地或CI环境中运行Docker容器进行测试,用于进行外部环境的集成测试。

Testcontainers可与JUnit5和Spring Boot Test一起使用,只需在测试类中添加@Container和@DynamicPropertySource注解即可。@Container指示Testcontainers在测试之前运行Docker容器,@DynamicPropertySource则指定将动态分配的端口自动配置为Spring Boot 的配置文件中的属性。

    @SpringBootTest
    @ExtendWith(SpringExtension.class)
    @Testcontainers
    public class MyTest {

        @Container
        private static final MySQLContainer mysqlContainer = new MySQLContainer();

        @DynamicPropertySource
        static void mysqlProperties(DynamicPropertyRegistry registry) {
            registry.add("spring.datasource.url", mysqlContainer::getJdbcUrl);
            registry.add("spring.datasource.username", mysqlContainer::getUsername);
            registry.add("spring.datasource.password", mysqlContainer::getPassword);
        }

        @Autowired
        private MyRepository myRepository;
        
        @Test
        public void test(){
            //测试代码
        }
    }

四、WebFlux测试

Spring Framework 5中引入的WebFlux是一种非阻塞、反应式编程风格的Web框架。对于WebFlux测试,可以使用spring-test库中的webTestClient实现。

webTestClient可以很容易地创建并发送请求,接收响应,并进行断言。webTestClient从中获得响应的Mono对象,可以通过subscribe方法阻塞响应。

    @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
    @AutoConfigureWebTestClient
    public class MyTest {

        @Autowired
        private WebTestClient webTestClient;

        @Test
        public void test(){
            String result = webTestClient.get().uri("/hello")
                    .exchange().expectStatus().isOk()
                    .returnResult(String.class).getResponseBody().blockFirst();
            Assertions.assertEquals("hello", result);
        }
    }

五、小结

Spring Boot Test是便于编写和执行测试用例的框架,支持单元测试、集成测试和WebFlux测试。Mockito用于测试中对依赖进行Mock或Stub,通过对依赖的预设行为,达到对实际业务场景的模拟测试。Testcontainers可以方便地测试运行在Docker容器中的外部环境,使测试用例更接近于实际生产环境。WebFlux基于异步非阻塞的编程模型,WebTestClient提供了对WebFlux应用的便捷测试支持。