0

I'm unable to mock my NoteService layer when testing my NoteController. When running, the real method noteService.findById(Long id, String login)) is called, instead of mocked one, so I run into NullPointerException. I use Spring Boot 1.33.

Here id my NoteController

@Controller
@RequestMapping("/notes")
public class NoteController {
    @Autowired
    private NoteService noteService;

    @ModelAttribute("noteForm")
    public NoteForm noteForm() {
        return new NoteForm();
    }

    @RequestMapping(value = "/edit/{id}", method = RequestMethod.GET)
    public String editNote(@PathVariable("id") Long id, NoteForm noteForm, Principal principal) {
        Note note = noteService.findById(id, principal.getName());
        noteForm.setNote(note);
        noteForm.setAddress(note.getAddress());
        return "edit";
    }       
}

My NoteControllerTest class

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = PhonebookApplication.class)
@WebAppConfiguration
public class NoteControllerTest {

    @Mock
    NoteService noteService;
    @Mock
    View mockView;

    @InjectMocks
    NoteController controller;

    MockMvc mockMvc;

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
        mockMvc = standaloneSetup(controller).setSingleView(mockView).build();

    }


    @Test
    public void editNote() throws Exception {
        Long id = 1L;
        Note note = new Note();
        when(noteService.findById(id, "user")).thenReturn(note);
        mockMvc.perform(get("/notes/edit/{id}", id)).andExpect(status().isOk()).andExpect(view().name("edit"));

    }
}

Exception stack trace

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.NullPointerException
at com.lardi.controller.NoteController.editNote(NoteController.java:46)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:817)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:731)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:968)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:859)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:844)
at org.springframework.test.web.servlet.TestDispatcherServlet.service(TestDispatcherServlet.java:65)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
at org.springframework.mock.web.MockFilterChain$ServletFilterProxy.doFilter(MockFilterChain.java:167)
at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134)
at org.springframework.test.web.servlet.MockMvc.perform(MockMvc.java:155)
at com.lardi.controller.NoteControllerTest.editNote(NoteControllerTest.java:62)

PhonebookApplication - is my main class. (NoteController.java:46) corresponds to my controller line Note note = noteService.findById(id, principal.getName()); I have read this post, but didn't find solution.

Thanks.

Update

I've found the reason. It is my when(noteService.findById(id, "user")).thenReturn(note); stub. It doesn't intercept controllers Note note = noteService.findById(id, principal.getName()); call. But still I don't know how to overcome it, changing when(noteServiceMock.findById(id, any(String.class))).thenReturn(note); causes The method any(Class<String>) is ambiguous for the type NoteControllerTest error. I need to intersect that call properly.

Community
  • 1
  • 1
Oleg Kuts
  • 769
  • 1
  • 13
  • 26
  • 1
    `@Autowired private` fields are always a pain to test, have you tried passing the `noteService` via constructor? – ESala Apr 24 '16 at 20:18
  • @ESala, just tried - the same result. – Oleg Kuts Apr 24 '16 at 20:29
  • Try this: when(noteServiceMock.findById(eq(id), any(String.class))).thenReturn(note); – Tom Van Rossom Apr 25 '16 at 13:41
  • @TomVanRossom, this causes "The method any(Class) is ambiguous for the type NoteControllerTest" compile error. And changing any(String.class) to anyString(), causes no compile error, but doesn't much method call, and triggers real service; – Oleg Kuts Apr 25 '16 at 17:09
  • 1
    @TomVanRossom, damn it, I knew i was missing something. `import static org.mockito.Mockito.*;import static org.hamcrest.Matchers.*;` this is the reason, why "The method any(Class) is ambiguous " occur. Need delete `Matchers.*`, or specify like this `org.mockito.Mockito.any(Long.class)`. And my initial problem is - Principal object. Mocking like this `findById(anyLong(), any(Principal.class)` solves my problem, but I need to change methods in the controler like this `noteService.findById(id,principal);` Another way I want to try - is to mock Principal object. – Oleg Kuts Apr 25 '16 at 18:31
  • @olegKuts did you solve it? – quento Oct 26 '16 at 11:26

2 Answers2

1

If I remember correctly if your are using the @Mock and @InjectMocks annotations you must use the MockitoJUnitRunner see the javadocs here: http://docs.mockito.googlecode.com/hg/1.9.5/org/mockito/runners/MockitoJUnitRunner.html

so change: @RunWith(SpringJUnit4ClassRunner.class)

to: @RunWith(MockitoJUnitRunner.class)

ndrone
  • 3,524
  • 2
  • 23
  • 37
  • 1
    I've changed this. But the reason of error is, that my stub method when() does not intercept real service call, due to wrong mocking, I think. – Oleg Kuts Apr 25 '16 at 17:12
  • If that is the case then your Mocked object is not getting inserted into the controller. You can validate that buy setting break points in your test case and in the controller and see that `NoteService` is actually different. – ndrone Apr 25 '16 at 17:33
  • It inercepts less complicated method, like `when(noteServiceMock.findById(id))`. I think I need to mock arguments in the right way. Or the issue may be due to me using Principal object, and that should be taken into account. Working on it. – Oleg Kuts Apr 25 '16 at 17:41
  • Yeah you may want to mock the Principal object as well. – ndrone Apr 25 '16 at 17:43
0

Use @MockBean instead with SpringJUnit4ClassRunner. Basically the object in the controller context changes . When you do MockBean that is taken care .

@MockBean NoteService noteService;