问题描述
我正在尝试为服务编写测试.但是我没有成功模拟 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-bean
和 mock-person-repository
.基本上,我使用配置文件来确定应该模拟什么.在进行任何测试之前,我声明 someBean
和 personService
是模拟实例.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 存储库的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!