Contents

Mockito 模拟注入 Spring Bean

1. 概述

在本教程中,我们将讨论如何使用依赖注入将 Mockito 模拟插入 Spring Beans 以进行单元测试。

在实际应用程序中,组件通常依赖于访问外部系统,提供适当的测试隔离非常重要,这样我们就可以专注于测试给定单元的功能,而不必涉及每个测试的整个类层次结构。

注入模拟是引入这种隔离的一种干净的方式。

2. Maven依赖

我们需要以下 Maven 依赖项来进行单元测试和模拟对象:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <version>2.6.1</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <version>2.6.1</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>2.21.0</version>
</dependency>

我们决定在这个例子中使用 Spring Boot,但经典的 Spring 也可以正常工作。

3. 编写测试

3.1. 业务逻辑

首先,让我们创建一个我们将要测试的简单服务:

@Service
public class NameService {
    public String getUserName(String id) {
        return "Real user name";
    }
}

然后我们将它注入到UserService类中:

@Service
public class UserService {
    private NameService nameService;
    @Autowired
    public UserService(NameService nameService) {
        this.nameService = nameService;
    }
    public String getUserName(String id) {
        return nameService.getUserName(id);
    }
}

对于本文,给定的类返回一个名称,而不管提供的 id 是什么。这样做是为了让我们不会因为测试任何复杂的逻辑而分心。

我们还需要一个标准的 Spring Boot 主类来扫描 bean 并初始化应用程序:

@SpringBootApplication
public class MocksApplication {
    public static void main(String[] args) {
        SpringApplication.run(MocksApplication.class, args);
    }
}

3.2. 测试

现在让我们继续测试逻辑。首先,我们必须为测试配置应用程序上下文:

@Profile("test")
@Configuration
public class NameServiceTestConfiguration {
    @Bean
    @Primary
    public NameService nameService() {
        return Mockito.mock(NameService.class);
    }
}

@Profile注解告诉 Spring 仅在“测试”配置文件处于活动状态时应用此配置。@Primary注解用于确保使用此实例而不是真实实例来进行自动装配。该方法本身创建并返回NameService类的 Mockito 模拟。

现在我们可以编写单元测试:

@ActiveProfiles("test")
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = MocksApplication.class)
public class UserServiceUnitTest {
    @Autowired
    private UserService userService;
    @Autowired
    private NameService nameService;
    @Test
    public void whenUserIdIsProvided_thenRetrievedNameIsCorrect() {
        Mockito.when(nameService.getUserName("SomeId")).thenReturn("Mock user name");
        String testName = userService.getUserName("SomeId");
        Assert.assertEquals("Mock user name", testName);
    }
}

我们使用*@ActiveProfiles注解来启用“测试”配置文件并激活我们之前编写的模拟配置。因此,Spring 会自动装配UserService类的真实实例,但会模拟NameService*类。测试本身是一个相当典型的 JUnit+Mockito 测试。我们配置模拟的期望行为,然后调用我们想要测试的方法,并断言它返回我们期望的值。

也可以(尽管不推荐)避免在此类测试中使用环境配置文件。为此,我们删除了*@Profile@ActiveProfiles注解,并将@ContextConfiguration(classes = NameServiceTestConfiguration.class)注解添加到UserServiceTest*类。