Reputation: 7995
I have a spring-boot application which exposes a REST interface via a controller. This is an example of my controller:
@RestController
public class Controller {
@Autowired
private Processor processor;
@RequestMapping("/magic")
public void handleRequest() {
// process the POST request
processor.process();
}
}
I am trying to write unit tests for this class and I have to mock the processor (since the processing takes very long time and I am trying to avoid this step during testing the controller behavior). Please note, that the provided example is simplified for the sake of this question.
I am trying to use the mockito framework for this task:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = App.class)
@WebAppConfiguration
@ActiveProfiles("test")
public class ControllerTest {
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
@Before
public void setUp() throws Exception {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
Processor processor = Mockito.mock(Processor.class);
ReflectionTestUtils.setField(Controller.class, "processor", processor);
}
@Test
public void testControllerEmptyBody() throws Exception {
this.mockMvc.perform(post("/magic")).andExpect(status().isOk());
}
}
However, this fails with
java.lang.IllegalArgumentException: Could not find field [processor] of type [null] on target [class org.company.Controller]
at org.springframework.test.util.ReflectionTestUtils.setField(ReflectionTestUtils.java:112)
...
Could please someone give me a hint, how this mock could be injected in my controller?
Upvotes: 3
Views: 10636
Reputation: 14806
You can remove servlet context class in SpringApplicationConfiguration
and mock servlet context. There is no need for injection of WebApplicationContext
and ReflectionTestUtils
.
Basically, your code should look something like this:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = MockServletContext.class)
@WebAppConfiguration
@ActiveProfiles("test")
public class ControllerTest {
@InjectMocks
private MyController controller;
@Mock
private Processor processor;
private MockMvc mockMvc;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
@Test
public void testControllerEmptyBody() throws Exception {
when(proessor.process()).thenReturn(<yourValue>);
this.mockMvc.perform(post("/magic")).andExpect(status().isOk());
verify(processor, times(<number of times>)).process();
}
}
Processor
will be mocked and mock will be injected into controller.
Upvotes: 0
Reputation: 77167
Rework your controller to use constructor injection instead of field injection. This makes the dependency explicit and makes your test setup drastically simpler.
Upvotes: 1
Reputation: 3509
@Before
public void setUp() throws Exception {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
Processor processor = Mockito.mock(Processor.class);
//This line should be added to perform mock for Processor.
Mockito.when(processor.process()).thenReturn(<Your returned value>);
//ReflectionTestUtils.setField(Controller.class, "processor", processor);
}
In above put the your returned value for "Your returned value" and in test use this value to verify your output.
Upvotes: 0
Reputation: 20375
Shouldn't you be passing an instance to set the field on, rather than the class, e.g.:
...
@Autowired
private Controller controller;
...
@Before
public void setUp() throws Exception {
...
Processor processor = Mockito.mock(Processor.class);
ReflectionTestUtils.setField(controller, "processor", processor);
}
Upvotes: 6
Reputation: 5168
I think that you can inject directly the mock like:
@InjectMocks
private ProcessorImpl processorMock;
And remove this line:
ReflectionTestUtils.setField(Controller.class, "processor", processor);
Upvotes: 1