springunit-testingmockito

Object reference changing during Mockito Test


I have just started working with Mockito and am having a problem with one of the tests failing, yet the actual code works correctly in a live environment. The controller being tested is:

@Controller
@RequestMapping("/notes")
public class NotesController {

private NoteRepository noteRepository;
private MyUserRepository userRepository;

@RequestMapping(value = "/add", method = POST)
public String postNote(@Valid Note note, BindingResult errors, Principal principal){
    String username = principal.getName();
    MyUser user = userRepository.findUserByUsername(username);
    note.setMyUser(user);
    note.setTime(new Date());
    noteRepository.save(note);
    return "redirect:/notes";    }
}

The test is here:

@Test
public void testShouldAddValidNote() throws Exception {
    MyUser testing = new MyUser();
    Note note = new Note();
    NoteRepository noteRepository = mock(NoteRepository.class);
    when(noteRepository.save(note)).thenReturn(note);

    MyUserRepository userRepository = mock(MyUserRepository.class);
    when(userRepository.findUserByUsername("testing")).thenReturn(testing);

    Principal mockPrincipal = mock(Principal.class);
    when(mockPrincipal.getName()).thenReturn("testing");


    NoteController controller = new NoteController(noteRepository);
    controller.setMyUserRepository(userRepository);
    MockMvc mockMvc = standaloneSetup(controller).build();

    mockMvc.perform(post("/notes/add")
            .requestAttr("note", note)
            .principal(mockPrincipal))
            .andExpect(view().name("redirect:/notes"));

    verify(noteRepository,times(1)).save(note);
    verify(note,times(1)).setMyUser(testing);
    verify(note,times(1)).setTime((Date)anyObject());
    verify(userRepository,times(1)).findUserByUsername("testing");
    verify(mockPrincipal,times(1)).getName();
}

The first verify test fails, with the message:

Argument(s) are different! Wanted:
noteRepository.save(projectName.Note@5ae9);

Actual invocation has different arguments:
noteRepository.save(projectName.Note@c079ae45

Clearly the Note object passed in to the method has changed, but I thought that using .requestAttr("note", note) would pass in the reference and the same object should therefore be in the method (and later returned). Like I said, it works perfectly in the live web Container, so what am I getting wrong with the Mockito test please?


Solution

  • This is just a wild guess could the issue this code faces comes from MockMvc / MockMvcRequestBuilders where the Note is somehow serialized / deserialized between the request configuration and the actual request ?

    Note that note is a real object so calling verify(note).... won't work.

    Anyway I suggest the use of the combination of a mockito captor and AssertJ in this case :

    // if field instantiation if using the mockito runner / rule or MockitoAnnotations.initMocks(this)
    @Captor ArgumentCaptor<Note> noteCaptor; 
    
    // if created in the test method
    ArgumentCaptor<Note> noteCaptor = ArgumentCaptor.forClass(Note.class);
    
    // ...
    
    verify(noteRepository,times(1)).save(noteCaptor.capture());
    assertThat(noteCaptor.getValue().geMyUser()).isEqualTo(testing);
    assertThat(noteCaptor.getValue().geTime()).isCloseTo(someDate);
    

    Note I'm on a phone