单元测试 Spring Boot 应用服务层

Posted

技术标签:

【中文标题】单元测试 Spring Boot 应用服务层【英文标题】:Unit testing Spring Boot application Service layer 【发布时间】:2020-02-10 21:26:07 【问题描述】:

我对 Spring Boot 提供的测试功能和实用程序有点困惑。

我在我的项目中使用spring-boot-starter-test,我想在没有数据库连接的情况下对我的服务进行单元测试

目前我将@WebMvcTest 用于控制器测试套件,将@SpringBootTest 用于所有其他测试类。

但我在某处读到 @SpringBootTest 仅用于集成测试...

阅读documentation 我不明白建议的服务方法是什么。我应该只在与 repos 的集成中测试它们吗?

更新

这是我的一项服务的测试类的摘录:

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@SpringBootTest
internal class SignupServiceTest(
        @Autowired val signupService: SignupService
) 
    @MockkBean
    lateinit var userRepository: UserRepository

    @Test
    fun `should return exception if username already used`() 
        every  userRepository.findByUsername("registered-user")  returns fakeUser(username = "registered-user")

        assertThatThrownBy 
            signupService.createNewAccount(fakeSignupForm(username = "registered-user"))
        .isExactlyInstanceOf(UsernameNotAvailableException::class.java)
    

    // ... other tests


【问题讨论】:

不,您应该通过模拟 repo 层来测试各个服务类。是的,您不应该使用@SpringbootTest,它将加载整个应用程序上下文,单元测试不需要这样做 这就是我想要做的(我正在使用 MockK),但仍然需要数据库连接... 没有必要。放上相关的代码,可能会更容易建议 @pvpkiran 我添加了一些代码 使用@RunWith(MockitoJUnitRunner.class)代替springboottest,使用@Autowired@MockBean代替@Mock@InjectMocks 【参考方案1】:

尝试使用 mockito 模拟对数据库的调用,或使用 h2 数据库进行测试

【讨论】:

【参考方案2】:

与集成测试(启动 Spring 容器的测试,@SpringBootTest)相比,您应该更倾向于实施单元测试来测试您的业务逻辑(测试在没有 Spring 的情况下运行,纯 JUnit 测试),因为它们更轻量级并且可以更快地为您提供反馈.

引用Spring Boot doc

依赖注入的主要优点之一是它应该使您的代码更容易进行单元测试。您可以使用 new 运算符来实例化对象,甚至无需涉及 Spring。您还可以使用模拟对象而不是真正的依赖项。

通常,您需要超越单元测试并开始集成测试(使用 Spring ApplicationContext)。无需部署应用程序或连接到其他基础架构即可执行集成测试非常有用。

【讨论】:

这个。我对所有服务单元测试使用@RunWith(MockitoJUnitRunner.StrictStubs.class),并模拟所有依赖项。效果很好,而且运行速度非常快。【参考方案3】:

使用@SpringBootTest 进行单元测试有点矫枉过正。因为这会启动整个应用程序上下文。

要测试单个(服务)类,我会使用 @RunWith(MockitoJUnitRunner.class) 而不是 @Autowired@MockBean 使用 @Mock@InjectMocks(如果您使用构造函数注入,则不必使用它. 这将是更好的选择)

你仍然可以使用 @Autowired@ContextConfiguration 并加载特定的类(如果没有太多的传递依赖)

如果不想使用mock,那么可以使用嵌入式数据库,使用@DataMongoTest@DataJpaTest,使用Springboot测试能力。

保持简单......

【讨论】:

【参考方案4】:

我之前也在使用@SpringBootTest,然后意识到它也在尝试连接到我的数据库,这不应该是必需的,因为它是一个简单的服务测试并且所有依赖项都是模拟的。经过一番研究,我发现这个解决方案效果很好。还要注意现在我的 spring 属性没有被注入,所以我使用 @Spy 创建了一个带有一些测试值的 pojo 属性对象。

@ExtendWith(MockitoExtension.class)
public class MyServiceTest 

/**
 * This is the bean under test as it is not a mock
 */
@InjectMocks
private MyService myService;

@Mock
private OtherService otherService;

@Spy
private MyServiceProperties properties = mockProperties();

@BeforeEach
public void configureMocks() 
   given(this.otherService.getData(any())).willReturn(mockDataResponse());


@Test
void testMyServiceReport_Success() 
    myService.runReport();

//assert response as needed
    verify(otherService, times(1)).getData(any());

这里还有一些关于导入的帮助,有时可能会令人困惑:

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;

import static org.mockito.BDDMockito.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.times;
import static org.mockito.BDDMockito.verify;

【讨论】:

以上是关于单元测试 Spring Boot 应用服务层的主要内容,如果未能解决你的问题,请参考以下文章

在 Spring Boot 中使用 Hibernate 为 DAO 层配置单元测试

执行单元测试时spring boot应用程序是不是需要连接到数据库

spring boot 集成测试(业务层)

Spring Boot使用单元测试

spring-boot 单元测试获取应用程序属性

基于spring-boot的应用程序的单元+集成测试方案