Spiki
Spiki

Reputation: 11

Spring web server considers part of uri as path variable during testing

There are two kind of similar endpoints, let's assume: POST devices/{uuid}/{imei} and POST devices/{uuid}/device-info. The first one is to update IMEI (delivered via path variable) of device specified by UUID and the second one is to update its other parameters (delivered with request as json body).

While server is working "normally" from a jar file, both endpoints works properly how it is described above, which was tested by Postman. But when I run integration tests (with maven or directly through IntelliJ), sending POST request to devices/{uuid}/device-info is interpret on server side as a request to devices/{uuid}/{imei}, where phrase "device-info" is treated as IMEI number.

For integration tests I use autoconfigured MockMvc class and SpringBootTest + Mockito + JUnit4 utilities. webEnvironment is set as SpringBootTest.WebEnvironment.MOCK and everything is ran with SpringRunner.

I was looking for solutions, but actually found nothing. Has anyone met with something similar?

EDIT: I'm adding API declarations if it can help.

@ResponseStatus(value = HttpStatus.NO_CONTENT, reason = "Device info successfully updated")
@PutMapping(value = "/devices/{deviceUuid}/device-info", consumes = {"application/json"})
ResponseEntity<Void> updateDeviceInfo(@Valid @RequestBody DeviceInfo deviceInfo);

@ResponseStatus(value = HttpStatus.NO_CONTENT, reason = "Device IMEI successfully updated")
@PutMapping(value = "/devices/{deviceUuid}/{imei}")
ResponseEntity<Void> updateDeviceImei(@PathVariable("deviceUuid") UUID deviceUuid, @PathVariable("imei") String imei);

The test itself is as simple as it can be:

DeviceInfo deviceInfo = this.prepareDeviceInfo();
String url = String.format("/v3/devices/%s/device-info", super.firstDeviceUuid);
mvc.perform(put(url)
        .content(asJsonString(deviceInfo)))
        .andExpect(status().is(204));

where asJsonString is simple helper method to prepare JSON from an object with Jackson methods.

Upvotes: 1

Views: 402

Answers (2)

Spiki
Spiki

Reputation: 11

I've finally found an answer. When I just commented devices/{uuid}/{imei} endpoint handler in controller, test's result status was 415, so it looked like no handler was found in controller. Then I found this solution: Spring MVC testing results in 415 error which worked for me perfectly.

I just set in my test case a content type to MediaType.APPLICATION_JSON_UTF8 as below and thanks to that it was correctly interpret on the server side.

mvc.perform(put(url)
        .content(mapper.writeValueAsString(deviceInfo))
        .contentType(MediaType.APPLICATION_JSON_UTF8))
        .andExpect(status().is(204));

EDIT: MediaType.APPLICATION_JSON works well too.

Upvotes: 0

Vipul Kumar
Vipul Kumar

Reputation: 479

Not sure what is the problem in your case. But I tried this code and it works for me

@RestController
@Slf4j
public class DeviceController {
    @ResponseStatus(value = HttpStatus.NO_CONTENT, reason = "Device info successfully updated")
    @PutMapping(value = "/devices/{deviceUuid}/device-info", consumes = {"application/json"})
    ResponseEntity<Void> updateDeviceInfo(@RequestBody Product product, @PathVariable("deviceUuid") UUID deviceUuid){
      log.info("Inside updateDeviceInfo");
        return ResponseEntity.ok().build();
    };

    @ResponseStatus(value = HttpStatus.NO_CONTENT, reason = "Device IMEI successfully updated")
    @PutMapping(value = "/devices/{deviceUuid}/{imei}")
    ResponseEntity<Void> updateDeviceImei(@PathVariable("deviceUuid") UUID deviceUuid, @PathVariable("imei") String imei){
        log.info("Inside updateDeviceInfo");
        return ResponseEntity.ok().build();
    };
}

For test cases

@SpringBootTest
@AutoConfigureMockMvc
public class DeviceControllerTest {
    @Autowired
    private MockMvc mvc;
    @Autowired
    private ObjectMapper objectMapper;
    @Test
    public void test() throws Exception {
        Product product = new Product();
        String url = String.format("/devices/%s/device-info", UUID.randomUUID().toString());
        mvc.perform(put(url)
                .content(objectMapper.writeValueAsString(product)))
                .andExpect(status().is(204));

    }

    @Test
    public void test2() throws Exception {
        Product product = new Product();
        String url = String.format("/devices/%s/%s", UUID.randomUUID().toString(),UUID.randomUUID().toString());
        mvc.perform(put(url))
                .andExpect(status().is(204));
    }
}

Upvotes: 1

Related Questions