无法使用 Mockito 模拟 Spring-Data-JPA 存储库
Unable to mock Spring-Data-JPA repositories with Mockito
我正在尝试为服务编写测试。但是我没有成功地嘲笑 repository
依赖。其他非存储库依赖项已成功模拟。存储库实例始终是实际实现而不是模拟实例。
我正在使用 Spring Boot 和 Spring Data JPA 来构建应用程序。 Mockito 用于模拟。我设法将问题提炼成一个测试项目。完整的测试项目在 GitHub。以下是测试项目的代码片段;下面是PersonServiceTest
class.
更新 1: before()
代码应该检查 personRepository
而不是 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 { ... }
}
测试 class 使用两个配置文件:mock-some-bean
和 mock-person-repository
。基本上,我使用配置文件来确定应该模拟什么。在进行任何测试之前,我断言 someBean
和 personService
是模拟实例。 someBean
被正确模拟,但 personService
总是失败。下面的代码是来自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
class 中定义的 @Bean
。我如何"tell" Spring Data JPA 不创建实例?我很感激我能得到解决这个问题的任何帮助。
更新 3:仍在寻找理想的解决方案
我仍然希望有解决方案。尽管我已将解决方案标记为已接受,但建议的解决方案并不理想。因为有不同级别的集成测试(从端到端测试到具有少量依赖项的非常狭窄范围的测试)。
这里有一些问题。我假设您正在尝试验证 PersonRepository
是否被嘲笑。然而,在你的测试中你写道:
assertThat(mockingDetails(personService).isMock(), is(true));
你不是在嘲笑 PersonService
所以它会失败这个断言是有道理的。
另一个问题是 Spring Data JPA 还将创建一个名为 personRepository
的 bean。因此,除非您更改其名称,否则您的模拟存储库 bean 将被忽略:
@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 创建存储库。
无论如何,一个更简洁的解决方案是使用 MockitoJUnitRunner
代替 Spring 的测试运行器。您所要做的就是注释要使用 @InjectMocks
测试的 class 以及要使用 @Mock
模拟和注入的所有依赖项:像这样:
@InjectMocks
private PersonService personService;
@Mock
private SomeBean someBean;
@Mock
private PersonRepository repository;
然后您可以删除 TestConfig
并删除测试中的所有注释并将其替换为 @RunWith(MockitoJUnitRunner.class)
。
我正在尝试为服务编写测试。但是我没有成功地嘲笑 repository
依赖。其他非存储库依赖项已成功模拟。存储库实例始终是实际实现而不是模拟实例。
我正在使用 Spring Boot 和 Spring Data JPA 来构建应用程序。 Mockito 用于模拟。我设法将问题提炼成一个测试项目。完整的测试项目在 GitHub。以下是测试项目的代码片段;下面是PersonServiceTest
class.
更新 1: before()
代码应该检查 personRepository
而不是 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 { ... }
}
测试 class 使用两个配置文件:mock-some-bean
和 mock-person-repository
。基本上,我使用配置文件来确定应该模拟什么。在进行任何测试之前,我断言 someBean
和 personService
是模拟实例。 someBean
被正确模拟,但 personService
总是失败。下面的代码是来自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
class 中定义的 @Bean
。我如何"tell" Spring Data JPA 不创建实例?我很感激我能得到解决这个问题的任何帮助。
更新 3:仍在寻找理想的解决方案
我仍然希望有解决方案。尽管我已将解决方案标记为已接受,但建议的解决方案并不理想。因为有不同级别的集成测试(从端到端测试到具有少量依赖项的非常狭窄范围的测试)。
这里有一些问题。我假设您正在尝试验证 PersonRepository
是否被嘲笑。然而,在你的测试中你写道:
assertThat(mockingDetails(personService).isMock(), is(true));
你不是在嘲笑 PersonService
所以它会失败这个断言是有道理的。
另一个问题是 Spring Data JPA 还将创建一个名为 personRepository
的 bean。因此,除非您更改其名称,否则您的模拟存储库 bean 将被忽略:
@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 创建存储库。
无论如何,一个更简洁的解决方案是使用 MockitoJUnitRunner
代替 Spring 的测试运行器。您所要做的就是注释要使用 @InjectMocks
测试的 class 以及要使用 @Mock
模拟和注入的所有依赖项:像这样:
@InjectMocks
private PersonService personService;
@Mock
private SomeBean someBean;
@Mock
private PersonRepository repository;
然后您可以删除 TestConfig
并删除测试中的所有注释并将其替换为 @RunWith(MockitoJUnitRunner.class)
。