Injecting Mockito Mocks into Spring Beans – 将Mockito Mocks注入Spring Bean中

最后修改: 2016年 1月 19日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’ll discuss how to use dependency injection to insert Mockito mocks into Spring Beans for unit testing.

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

In real-world applications, where components often depend on accessing external systems, it’s important to provide proper test isolation, so that we can focus on testing the functionality of a given unit without having to involve the whole class hierarchy for each test.

在现实世界的应用中,组件经常依赖于访问外部系统,提供适当的测试隔离是很重要的,这样我们就可以专注于测试某个单元的功能,而不必为每个测试涉及整个类的层次结构。

Injecting a mock is a clean way to introduce such isolation.

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

2. Maven Dependencies

2.Maven的依赖性

We need the following Maven dependencies for the unit tests and mock objects:

我们需要为单元测试和模拟对象提供以下Maven依赖项。

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

We decided to use Spring Boot for this example, but classic Spring will also work fine.

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

3. Writing the Test

3.编写测试

3.1. The Business Logic

3.1.业务逻辑

First, let’s create a simple service that we’ll be testing:

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

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

Then we’ll inject it into the UserService class:

然后我们将其注入到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);
    }
}

For this article, the given classes return a single name regardless of the id provided. This is done so that we don’t get distracted by testing any complex logic.

在本文中,无论提供的id是什么,给定的类都会返回一个单一的名字。这样做是为了让我们不因测试任何复杂的逻辑而分心。

We’ll also need a standard Spring Boot main class to scan the beans and initialize the application:

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

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

3.2. The Tests

3.2.测试

Now let’s move on to the test logic. First of all, we have to configure the application context for the tests:

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

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

The @Profile annotation tells Spring to apply this configuration only when the “test” profile is active. The @Primary annotation is there to make sure this instance is used instead of a real one for autowiring. The method itself creates and returns a Mockito mock of our NameService class.

@Profile注解告诉Spring只有在 “测试 “配置文件处于活动状态时才应用此配置。@Primary注解是为了确保这个实例被用于自动布线,而不是一个真正的实例。该方法本身创建并返回一个NameService类的Mockito模拟。

Now we can write the unit test:

现在我们可以写单元测试了。

@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);
    }
}

We use the @ActiveProfiles annotation to enable the “test” profile and activate the mock configuration we wrote earlier. As a result, Spring autowires a real instance of the UserService class, but a mock of the NameService class. The test itself is a fairly typical JUnit+Mockito test. We configure the desired behavior of the mock, then call the method that we want to test, and assert that it returns the value we expect.

我们使用@ActiveProfiles注解来启用 “测试 “配置文件并激活我们之前写的模拟配置。结果,Spring自动连接了UserService类的一个真实实例,但却是NameService类的一个模拟。该测试本身是一个相当典型的JUnit+Mockito测试。我们配置所需的模拟行为,然后调用我们要测试的方法,并断言它返回我们期望的值。

It’s also possible (though not recommended) to avoid using environment profiles in such tests. To do so, we remove the @Profile and @ActiveProfiles annotations, and add an @ContextConfiguration(classes = NameServiceTestConfiguration.class) annotation to the UserServiceTest class.

在这种测试中避免使用环境配置文件也是可能的(尽管不推荐)。为此,我们删除@Profile@ActiveProfiles注解,并在UserServiceTestConfiguration(classes = NameServiceTestConfiguration.class) 注解中添加@ContextConfiguration(classes = NameServiceTestConfiguration.class)

4. Conclusion

4.结论

In this brief article, we learned how easy it is to inject Mockito mocks into Spring Beans.

在这篇简短的文章中,我们了解到将Mockito模拟注入Spring Beans是多么容易。

As usual, all the code samples are available over on GitHub.

像往常一样,所有的代码样本都可以在GitHub上找到。