Автор оригинала: Marcin Piczkowski.
MockMvc-это класс Spring, используемый для модульного тестирования контроллеров без необходимости запуска сервера. Он имеет свободный API для подтверждения ответных сообщений. Например: для ответа JSON мы можем написать утверждения, подобные этому:
this.mockMvc.perform(get("/test")) .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) .andExpect(status().isOk()) .andExpect(jsonPath("$.value", is("Hello!")));
Под ним используется JSONPath
.
Это хорошо работает для утверждений строковых значений, но может вести себя странно с числовыми значениями.
Вот пример:
При тестировании метода контроллера:
public static final double VALUE = 0.07185454505642408; ... @GetMapping(value = "/test", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) public ResponseEntity get() { Dto dto = new Dto(); dto.setValue(VALUE); return new ResponseEntity(dto, HttpStatus.OK); }
использование теста:
@RunWith(SpringRunner.class) @WebMvcTest(ExampleController.class) public class ExampleControllerTest { @Autowired private MockMvc mockMvc; @Test public void should_get_metadata_when_video_processed() throws Exception { this.mockMvc.perform(get("/test")) .andDo(print()) .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) .andExpect(status().isOk()) .andExpect(jsonPath("$.value", is(ExampleController.VALUE))); } }
тест завершается неудачей с:
java.lang.AssertionError: JSON path "$.value" Expected: is <0.07185454505642408> but: was <0.07185454505642408> Expected :is <0.07185454505642408> Actual :<0.07185454505642408>
Ух ты! Оба напечатанных значения, ожидаемое и фактическое, одинаковы. Что происходит?
На самом деле проблема заключается в типе ожидаемого и фактического. Ожидаемое-это Двойной
но на самом деле это Большая десятичная
.
Я бы не узнал об этом, если бы не отладил его.
Причина в том, что MockMvc использует Json Smart JsonProvider
для сериализации.
Когда значение невелико, например, для Long оно может быть уменьшено до целого числа, для BigDecimal – до двойного, то так работает JsonSmartJsonProvider
.
Это приводит к неожиданным ситуациям, когда мы сериализуем значения определенного типа в JSON но он не может быть десериализован обратно в тот же числовой тип.
Что же тогда является решением?
Вместо этого можно использовать другой JsonProvider – JacksonJsonProvider
, который является прогнозирующим, потому что вы можете настроить его так, чтобы он всегда десериализовывал числовые значения в длинные и двойные.
Жаль, что это не реализация по умолчанию, используемая в JSONPath.
Вот небольшой проект на Github с некоторыми тестами, демонстрирующими проблемы с JsonSmartJsonProvider
и как их решить с помощью JacksonJsonProvider
В двух словах, решение простое. Вам нужно добавить эту конфигурацию при инициализации тестов:
final ObjectMapper objectMapper = new ObjectMapper(); objectMapper.enable(DeserializationFeature.USE_LONG_FOR_INTS); objectMapper.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS); Configuration.setDefaults(new Configuration.Defaults() { private final JsonProvider jsonProvider = new JacksonJsonProvider(objectMapper); private final MappingProvider mappingProvider = new JacksonMappingProvider(objectMapper); @Override public JsonProvider jsonProvider() { return jsonProvider; } @Override public MappingProvider mappingProvider() { return mappingProvider; } @Override public Set
Установив Характеристика десериализации
вы получаете целочисленные значения, десериализованные всегда в Long
и плавающие всегда в Большая десятичная
.
Надеюсь, это поможет вам сэкономить время на решении странных ошибок, с которыми я столкнулся.
Оригинал: “https://www.codementor.io/@marcinpiczkowski/json-conversion-errors-with-spring-mockmvc-o2vmtb42y”