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.
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.
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 { ... }
}
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);
}
}
Update 2: question made clearer
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.
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).
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));
You're not mocking PersonService
so it makes sense that it would fail this assertion.
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);
}
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.
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;
And then you can delete TestConfig
and remove all annotations on your test and replace them with @RunWith(MockitoJUnitRunner.class)
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With