0

I have a simple spring boot app with controller (example)

@RestController
@RequestMapping("/person")
public class PersonController {
    @Autowired
    PersonService personService;
    @PostMapping
    ResponseEntity<Void> createNewMessage(@RequestBody Person itemDto, UriComponentsBuilder uriComponentsBuilder){
        
        final Person itemInfo = personService.createPerson(itemDto)
                .orElseThrow(()-> new DuplicateException("ALREADY_EXISTS"));
        //NOTE: assume there will be a get end point
        UriComponents uriComponents = uriComponentsBuilder.path("/person/find/{id}").buildAndExpand(itemInfo.getId());

        return ResponseEntity.created(uriComponents.toUri()).build();
    }
}

Goal: To be able to "Unit" test DuplicateException in the endpoint using MockMvc - Mockito - "Arrange using when"

Source: https://github.com/kswat/demo.git

This test fails ( Note If "any" is used the test passes - any(Person.class) ) , but what is wrong?

@Test
void given_exact_person_save() throws Exception {
    var inPerson = new Person("First","Last");

    when(personService.createPerson(ArgumentMatchers.eq(inPerson))).thenReturn(Optional.of(inPerson));

    mockMvc.perform(post("/person")
                    .contentType(MediaType.APPLICATION_JSON)
                    .content(objectMapper.writeValueAsString(inPerson)))
            .andExpect(status().isCreated());
}

Service - dummy implementation

@Service
public class PersonService {

    public Optional<Person> createPerson(Person itemDto) {
        return Optional.empty();
    }
}

How to complete my test ?

may be have 2 Arrange statements

when(personService.createPerson(ArgumentMatchers.eq(inPerson))).thenReturn(...);
when(personService.createPerson( any(Person.class)) ).thenReturn(...);
2
  • 1
    a) PersonService is not mocked; consequently its methods cannot be stubbed b) Does Person override equals? If not, it cannot be matched (because it is serialized, then deserialized, resulting in a different instance). Commented Jul 27, 2024 at 17:09
  • service is already mockbean. Adding equals and hashcode did work. Shame that I was a little conservative and not implementing equals and hash ( simple annotation in lombok) Commented Jul 27, 2024 at 21:45

1 Answer 1

0

Let's have a look at each piece of your code in detail:

@PostMapping
ResponseEntity<Void> createNewMessage(@RequestBody Person itemDto, UriComponentsBuilder uriComponentsBuilder) {

deserializes itemDto from your request's (JSON) payload.

mockMvc.perform(post("/person")
        .contentType(MediaType.APPLICATION_JSON)
        .content(objectMapper.writeValueAsString(inPerson)))

very explicitly serializes your inPerson instance to a JSON string (objectMapper.writeValueAsString(inPerson)). This JSON string is completely detached from any existing Java object instance.

var inPerson = new Person("First","Last");

when(personService.createPerson(ArgumentMatchers.eq(inPerson)))
  .thenReturn(Optional.of(inPerson));

matches a call to createPerson with an argument that is equal to inPerson. That means either:

  • If your Person class does not override the equals method: the argument must be the same reference as inPerson (i.e. argument == inPerson, both instances have the same identity hashcode). Note that it is still calling equals behind the scenes, but the default implementation of Object#equals is boolean equals(Object other) { return this == other; }.
  • Your Person class overrides equals: the argument must have value equality to inPerson (i.e. Objects.equals(argument, inPerson)).
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.