本文介绍了无法使用 Mockito 模拟 Spring-Data-JPA 存储库的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试为服务编写测试.但是我没有成功模拟 repository 依赖项.成功模拟了其他非存储库依赖项.存储库实例始终是实际实现,而不是模拟实例.

I am trying to write tests for a service. But I am unsuccessful at mocking the repository dependency. Other non-repository dependencies are successfully mocked. The repository instance is always the actual implementation and not a mock instance.

我正在使用 Spring Boot 和 Spring Data JPA 来构建应用程序.Mockito 用于模拟.我设法将问题提炼到一个测试项目中.完整的测试项目位于 GitHub.以下是来自测试项目的代码片段;以下是 PersonServiceTest 类.

I am using Spring Boot and Spring Data JPA to build the application. Mockito is used for mocking. I managed to distil the problem into a test project. The complete test project is on GitHub. Below are snippets of code from the test project; the following is the PersonServiceTest class.

更新 1: before() 代码应该检查 personRepository 而不是 personService

Update 1: before() code should be checking personRepository not personService

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(App.class)
@WebAppConfiguration
@TestExecutionListeners({ServletTestExecutionListener.class, DirtiesContextBeforeModesTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class, TransactionalTestExecutionListener.class, SqlScriptsTestExecutionListener.class})
@Transactional
@ActiveProfiles({"mock-some-bean", "mock-person-repository"})
public class PersonServiceTest {

    @Inject
    private SomeBean someBean;
    @Inject
    private PersonRepository personRepository;
    @Inject
    private PersonService personService;

    @Before
    public void before() {
        assertThat(mockingDetails(someBean).isMock(), is(true));
        assertThat(mockingDetails(personRepository).isMock(), is(true));
    }

    @Test
    public void doSomething() throws Exception { ... }
}

测试类使用两个配置文件:mock-some-beanmock-person-repository.基本上,我使用配置文件来确定应该模拟什么.在进行任何测试之前,我声明 someBeanpersonService 是模拟实例.someBean 被正确模拟,但 personService 总是失败.以下代码是来自 TestConfig 类的代码.

The test class uses two profiles: mock-some-bean and mock-person-repository. Basically, I am using profiles to determine what should be mocked. Before doing any tests, I assert that the someBean and personService are mocked instances. someBean is mocked properly but personService always fails. The following code is the code from the TestConfig class.

@Configuration
public class TestConfig {

    private static final Logger logger = LoggerFactory.getLogger(TestConfig.class);

    @Bean
    @Profile("mock-some-bean")
    public SomeBean someBean() {
        logger.info("Mocking: {}", SomeBean.class);
        return mock(SomeBean.class);
    }

    @Bean
    @Profile("mock-person-repository")
    public PersonRepository personRepository() {
        logger.info("Mocking: {}", PersonRepository.class);
        return mock(PersonRepository.class);
    }
}

更新 2:问题更清楚

我错过了什么?Spring Data JPA 似乎总是在创建一个实例并忽略 TestConfig 类中定义的 @Bean.我如何告诉"Spring Data JPA 不要创建实例?我很感激我能得到的任何帮助来解决这个问题.

What am I missing? It appears that Spring Data JPA is always creating an instance and ignoring the @Bean defined in the TestConfig class. How do I "tell" Spring Data JPA not to create an instance? I appreciate any help I can get to resolve this problem.

更新 3:仍在寻找理想的解决方案

Update 3: still looking for ideal solution

我仍然希望有一个解决方案.尽管我已将解决方案标记为已接受,但建议的解决方案并不理想.因为有不同级别的集成测试(从端到端测试到具有一小组依赖项的非常狭窄的测试范围).

I would still appreciate a solution. Although I have marked the solution as accepted, the suggested solution is not ideal. Because there are varying levels of integration tests (from end-to-end testing to a very narrow scope of testing with a small set of dependencies).

推荐答案

这里有几个问题.我假设您正在尝试验证 PersonRepository 是否被模拟.但是,在您的测试中,您写道:

There are a few issues here. I assume that you're trying to verify if PersonRepository is mocked or not. However, in your test you wrote:

assertThat(mockingDetails(personService).isMock(), is(true));

你不是在嘲笑 PersonService,所以它会失败这个断言是有道理的.

You're not mocking PersonService so it makes sense that it would fail this assertion.

另一个问题是 Spring Data JPA 还将创建一个名为 personRepository 的 bean.因此,除非您更改其名称,否则您的模拟存储库 bean 将被忽略:

Another issue is that Spring Data JPA will also create a bean called personRepository. So your mocked repository bean will be ignored unless you change its name:

@Bean
@Profile("mock-person-repository")
// Change method name to change bean name
public PersonRepository personRepositoryMock() {
    logger.info("Mocking: {}", PersonRepository.class);
    return mock(PersonRepository.class);
}

但是如果你这样做,那么将会有两个 PersonRepository 类型的 bean,所以将它自动装配到你的服务中将会失败.要解决此问题,您必须阻止它使用 Spring Data JPA 创建存储库.

But if you do that, then there will be two beans of the type PersonRepository, so autowiring it into your service will fail. To fix that you would have to prevent it from creating the repositories with Spring Data JPA.

无论如何,更简洁的解决方案是使用 MockitoJUnitRunner 代替 Springs 测试运行器.您所要做的就是使用 @InjectMocks 注释要测试的类以及要使用 @Mock 模拟和注入的所有依赖项:像这样:

Anyhow, a much cleaner solution would be to use MockitoJUnitRunner in stead of Springs test runner. All you would have to do is annotate the class you want to test with @InjectMocks and all dependencies you want to mock and inject with @Mock: like this:

@InjectMocks
private PersonService personService;
@Mock
private SomeBean someBean;
@Mock
private PersonRepository repository;

然后您可以删除 TestConfig 并删除测试中的所有注释并将它们替换为 @RunWith(MockitoJUnitRunner.class).

And then you can delete TestConfig and remove all annotations on your test and replace them with @RunWith(MockitoJUnitRunner.class).

这篇关于无法使用 Mockito 模拟 Spring-Data-JPA 存储库的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-29 21:08
查看更多