Mockito and Javax annotation @PostConstruct
I've modified your test slightly, creating MyService
via Mockito
and making myMethod
return the id:
@ExtendWith(SpringExtension.class)
class MyServiceTest {
private static final Integer ID = 42;
@InjectMocks
MyService myService;
@Test
public void myTest() {
// Given.
MyObject myObj = new MyObject();
myObj.setId(ID);
MyRepository myRepository = Mockito.mock(MyRepository.class);
Mockito.when(myRepository.findByName(anyString())).thenReturn(myObj);
// Test runs here.
assertThat(myService.myMethod()).isEqualTo(ID);
}
}
When I run this, I get:
java.lang.NullPointerException: Cannot invoke "java.lang.Integer.intValue()" because "this.myId" is null
at MyService.myMethod(MyService.java:24)
at MyServiceTest.myTest(MyServiceTest.java:28)
...
So clearly our mocking of findByName
hasn't worked.
That's because myRepository
is just a local variable in our test -- there's nothing to get it injected into MyService
.
One way to fix this is not to use spring test infrastructure, just plain Mockito. We use the standard @Mock/@InjectMocks annotations to create our mock and the object we are testing. The drawback here is that we must remember to call postInit
ourselves:
@ExtendWith(MockitoExtension.class)
class MyServiceTest {
private static final Integer ID = 42;
@Mock
MyRepository myRepository;
@InjectMocks
MyService myService;
@Test
public void myTest() {
// Given.
MyObject myObj = new MyObject();
myObj.setId(ID);
Mockito.when(myRepository.findByName(anyString())).thenReturn(myObj);
myService.postInit();
// Test runs here.
assertThat(myService.myMethod()).isEqualTo(ID);
}
}
To use Spring to call postInit you can do this: (I'm not sure if there's a more idiomatic way to create the mock MyRepository instance)
@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = {MyService.class})
@ContextConfiguration(classes = {MyServiceTest.Configuration.class})
class MyServiceTest {
@TestConfiguration
static class Configuration {
@Bean
public MyRepository myRepository() {
MyRepository myRepository = mock(MyRepository.class);
MyObject myObj = new MyObject();
myObj.setId(ID);
Mockito.when(myRepository.findByName(anyString())).thenReturn(myObj);
return myRepository;
}
}
private static final Integer ID = 42;
@Autowired
MyService myService;
@Test
public void myTest() {
assertThat(myService.myMethod()).isEqualTo(ID);
}
}
One way to avoid using @SpringBootTest
is to create the MyService
instance in our test configuration (I've converted it to constructor injection too):
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = {MyServiceTest.Configuration.class})
class MyServiceTest {
@TestConfiguration
static class Configuration {
@Bean
public MyRepository myRepository() {
MyRepository myRepository = mock(MyRepository.class);
MyObject myObj = new MyObject();
myObj.setId(ID);
Mockito.when(myRepository.findByName(anyString())).thenReturn(myObj);
return myRepository;
}
@Bean
public MyService myService(MyRepository myRepository) {
return new MyService(myRepository);
}
}
private static final Integer ID = 42;
@Autowired
MyService myService;
@Test
public void myTest() {
assertThat(myService.myMethod()).isEqualTo(ID);
}
}
Mockito @Before method is called after @PostConstruct
Using @TestConfiguration
rather than using the @MockBean
might help to solve this problem.
@TestConfiguration
static class Configuration {
@Bean
public BeanToMock name() {
// return mock object
}
}
And annotating the test class with @ContextConfiguration
:
@ContextConfiguration(classes = TestClassName.Configuration.class)
Related Topics
How to Decrypt Whatsapp Database File
Using Streams to Convert a List of Objects into a String Obtained from the Tostring Method
How to Exclude Property from Lombok Builder
Calculating the Sum of Number and Its Reverse in Java
Alternative to Switch Case in Java
Connection Java - Mysql:Public Key Retrieval Is Not Allowed
Bootstrap.Yml Not Loading in Spring Boot 2
Chrome Browser Is Not Opening in Selenium Webdriver
How to Handle Incorrect User Input into a Scanner
Selenium Webdriver: Modifying Navigator.Webdriver Flag to Prevent Selenium Detection
How to Run Java Program in Terminal With External Library Jar
How to Store User Input into an Existing Array
In Java: How to Zip File from Byte[] Array
Eclipse: the Resource Is Not on the Build Path of a Java Project
Forwarding the Request from One Jsp to Another Jsp With All Request Parameters