Smajl
Smajl

Reputation: 7995

Mock class inside REST controller with Mockito

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

Answers (5)

Branislav Lazic
Branislav Lazic

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

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

Rahul
Rahul

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

Jonathan
Jonathan

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

JFPicard
JFPicard

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);

See Injection of a mock object into an object to be tested declared as a field in the test does not work using Mockito?

Upvotes: 1

Related Questions