0

I'm trying to run a test on my Spring Boot endpoint, which accepts is supposed to take information from a client-side form, map the input fields to the DTO and persists it into the DB, but I can't get the test schema to accept it. From my understanding, when you have a controller endpoint defined like this:

@PostMapping(path = "/newContact")
public @ResponseBody ContactDTO createNewContact(
        @ModelAttribute ContactDTO newContact) { 
    //persists newContact to the data tier 
}

the @ModelAttribute tag will automatically search for the name of the fields of the newContact DTO in the incoming JSON name, and then map the json values to fill the fields of the DTO.

Here is my ContactDTO class:

public class ContactDTO {

  private BigInteger userId;
  private BigInteger contactId;
  private String name;
  private String email;
  private String bday;
  private String address;
  private String city;
  private String state;
  private List<PhoneDTO> phones;
  private MultipartFile contactImgUpload;

   //getters and setters

}

Firstly, is this a correct understanding?

So, I'm trying to test that my endpoint works, by making the expected DTO, but converting it to JSON, and then POSTing that JSON to the controller endpoint:

 @Autowired
  ObjectMapper objectMapper;

  @Test
  public void saveAnEntryWhenPOSTUserWithMultiplePhones() throws Exception {
    List<PhoneDTO> phones = new ArrayList<>();
    phones.add(new PhoneDTO("landline", "12312312312"));
    phones.add(new PhoneDTO("mobile", "3242523462"));

    ContactDTO cDTO = new ContactDTO();
    cDTO.setContactId(BigInteger.valueOf(555));
    cDTO.setName("Multiphone User");
    cDTO.setUserId(BigInteger.valueOf(123));
    cDTO.setEmail("test@email.com");
    cDTO.setBday("01/01/1987");
    cDTO.setState("IL");
    cDTO.setCity("Chicago");
    cDTO.setAddress("55 Jackson");
    cDTO.setPhones(phones);

    this.mockMvc.perform(post("/newContact")
            .contentType(MediaType.APPLICATION_JSON)
            .content(objectMapper.writeValueAsString(cDTO)))
            .andExpect(status().isOk())
            .andExpect(content().string(containsString("\"phoneType\":\"landline:\"")));
  }

But when I do this, it is clearly not sending the JSON in the expected way, as it fails when it attempts to save to the data tier, saying that some of the expected fields which are expected to be populated (here the "name" field) are empty:

    org.springframework.web.util.NestedServletException: Request processing failed;
 nested exception is javax.validation.ConstraintViolationException: 
Validation failed for classes [app.models.relationentities.Contact] during
 persist time for groups [javax.validation.groups.Default, ]

    List of constraint violations:[

        ConstraintViolationImpl{interpolatedMessage='may not be null', 
propertyPath=name, rootBeanClass=class app.models.relationentities.Contact, messageTemplate='{javax.validation.constraints.NotNull.message}'}
    ]

So, am I wrong with the test? How do I mock sending a filled-out form to the test?

Edit Including the phoneDTO, which is what the ContactDTO field private List<PhoneDTO> phones; holds a list of:

public class PhoneDTO {

  private String phoneType;
  private String number;
   //getters and setters
}
NateH06
  • 3,154
  • 7
  • 32
  • 56
  • AFAIK `@ModelAttribute` is for form-encoded data not JSON. So `.param("contactId", "155").param("foo", "")` etc –  Oct 11 '17 at 16:23
  • Hmm.. ok , then how would I include a list of phones to fill out the `ContactDTO's` list of phones, which is a list of the `PhoneDTO` included above? As an example, when I make a GET request for a ContactDTO, it comes back like this: `{"userId":60606,"contactId":1,"name":"Nate LastName","email":"nate@user.com","bday":"01 06 1987","address":"123 Faker Street","city":"Chicago","state":"IL","phones":[{"phoneType":"landline","number":"111456666"},{"phoneType":"mobile","number":"1114567890"}],"contactImgUpload":null,"base64ImageString":null}` – NateH06 Oct 11 '17 at 17:56
  • check this https://stackoverflow.com/a/33434317/180100 for example –  Oct 11 '17 at 18:13

1 Answers1

0

Use flashAttr method.

this.mockMvc.perform(post("/newContact")
        .contentType(APPLICATION_JSON)
        .flashAttr("newContact", cDTO))
        .andExpect(status().isOk())
        ...

You also have different urls in the controller and its test: /newContact and /users/ accordingly.

Sasha Shpota
  • 9,436
  • 14
  • 75
  • 148
  • Ah, the `/users` was a typo, fixed it, thanks. And the Spring Boot docs say to handle the form submission using @ModelAttribute : https://spring.io/guides/gs/handling-form-submission/ – NateH06 Oct 11 '17 at 17:51
  • Hmm, still not doing it, the fields of the DTO are still coming in blank when it runs: `Request processing failed; nested exception is javax.validation.ConstraintViolationException: Validation failed for classes [app.models.relationentities.Contact] during persist time for groups [javax.validation.groups.Default, ] List of constraint violations:[ ConstraintViolationImpl{interpolatedMessage='may not be null', propertyPath=name, rootBeanClass=class app.models.relationentities.Contact, messageTemplate='{javax.validation.constraints.NotNull.message}'}` – NateH06 Oct 11 '17 at 19:39
  • Strange, may be you have some other code that influences on this, but this is how it should be tested. Did you setup a breakpoint in your controller? Is it blank during debug? Also try explicitly specify name for the model attribute, eg `@ModelAttribute("test")` and then set it accordingly in `.flashAttr("test", cDTO)`. – Sasha Shpota Oct 11 '17 at 19:45
  • Nope, unfortunately. Thanks though! – NateH06 Oct 11 '17 at 19:57