Issue
I have a Spring MVC application . I want to test this controller:
@RunWith(SpringRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations = {
"classpath:testDatabaseContext.xml"
})
public class TimeControllerTests {
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
@Mock
private AutorisationService autorisationService;
@Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
@Test
public void should_OK() throws Exception {
doReturn(User.builder().build()).when(autorisationService).findById(any(Date.class),anyLong(), 1L);
mockMvc.perform(get("/time/2")
.contentType(APPLICATION_JSON))
.andExpect(status().isOk());
}
}
but when I run the test I have this error:
org.mockito.exceptions.misusing.NullInsteadOfMockException:
Argument passed to when() is null!
Example of correct stubbing:
doThrow(new RuntimeException()).when(mock).someMethod();
Also, if you use @Mock annotation don't miss openMocks()
Solution
I see several reasons why this does not work :
A - @Mock alone is not enough to initialize a mock. You need either :
- Call MockitoAnnotations.initMocks() in setup
- Remove @Mock and call Mockito.mock(UserRepository.class) in setup
However I don't think this will be enough, which brings me to :
B - Your controller looks managed by spring, whereas your mocked AutorisationService is not.
I am assuming there that your controller uses an AutorisationService supposedly injected by spring.
You have several options here :
- Option 1 - Don't use spring at all for your test, and manage your controller and service with mockito. This should look like :
@InjectMocks
private SomeController someController;
@Mock
private AutorisationService autorisationService;
@Before
public void setup() {
MockitoAnnotations.initMocks();
this.mockMvc = MockMvcBuilders.standaloneSetup(someController).build();
}
Note the standaloneSetup method : the idea here is that you are not using a spring context (annotations at the top can then be removed).
- Option 2 : Use spring without springboot, with a mocked service.
Kind of a hack, but I use it on regular basis.
You have to provide a custom spring context, with a bean of type AutorisationService, but provided by mockito.
// All all other beans necessary
// You can use whatever you need in your context : @ComponentScan("somepackage"), @Import(Config1.class, Config2.class), @ImportResource("context.xml"), @Bean methods ...
@Configuration
class SpringWithMockitoConfig {
@Bean
AutorisationService autorisationService() {
return Mockito.mock(AutorisationService.class);
}
}
You will then have two constraint on your tests :
- Import the custom config : @ContextConfiguration(classes = SpringWithMockitoConfig.class)
- Reset the mocks on every test in the setup method : Mockito.reset(autorisationService);
With this solution you don't need the beans your real AutorisationService relies on (I assume Dao's, DataSource and such).
Basicly only the mvc part.
- Option 3 : use SpringBoot and the @MockBean annotation - which I am not familiar with yet
Answered By - jmaniquet
Answer Checked By - Cary Denson (JavaFixing Admin)