Prueba de la unidad de serialización Json en Spring Boot



Introduccion


Una de las tareas principales de cada servicio web es devolver el modelo al lado del cliente y, en este caso, Spring Boot proporciona un nivel conveniente de abstracción, lo que permite al desarrollador permanecer en el nivel de trabajo con los modelos y dejar el proceso de serialización del modelo fuera del código fuente del programa. Pero, ¿qué sucede si la serialización en sí se convierte en parte de la lógica comercial de la aplicación y, por lo tanto, requiere cobertura de caso de prueba?

Este artículo examinará uno de los escenarios cuando tengamos que tener en cuenta las peculiaridades de la lógica empresarial de la aplicación durante la serialización (el escenario de redondeo de cantidades de dinero), en cuyo ejemplo encontraremos el mecanismo de serialización en Spring Boot, y también describiremos un posible método de prueba.

Declaración del problema.


Deje que nuestro servicio web sea responsable de proporcionar información sobre los gastos de los clientes, y debemos proporcionar datos con una precisión configurable. Una solución lógica sería realizar todas las transformaciones del modelo en la periferia del servicio, mientras se conserva la visibilidad de aplicar la lógica de redondeo.

Considere una posible solución


Imagine el controlador de nuestra aplicación, que devolverá el modelo deseado.

@RestController public class AccountController { //        , //       . @Autowired private AccountService accountService; @RequestMapping(value = "/account/{clientId}", method = RequestMethod.GET, produces = "application/json") public Account getAccount(@PathVariable long clientId) throws Exception { Account result = accountService.getAccount(clientId); //  ,    - //      ,    json, //    . return result; } } 

Ahora echemos un vistazo a nuestro modelo.

 public class Account { private Long clientId; //    Spring Boot   FasterXML/jackson, //    API  ,   . // ,       //     MoneySerializer @JsonSerialize(using = MoneySerializer.class) private BigDecimal value; //    getter'  setter'    } 

Es posible que ya haya tenido que lidiar con otras anotaciones para la personalización. Una característica de esta anotación es la capacidad de determinar su servicio responsable de serializar el campo modelo anotado.

Antes de ver de qué se trata un serializador, complicaremos la tarea: dejar que los parámetros de redondeo sean configurables a través de algún servicio interno que abstraiga cualquier manifestación de resolución dinámica de parámetros.

Esta complicación es nuestro punto clave que queremos considerar. Como podemos ver en la implementación del modelo, la API de nuestro marco toma una clase de serialización en el argumento, lo que significa que el ciclo de vida del serializador queda bajo el control del marco del serializador. Esto plantea la pregunta, ¿qué hacer si queremos inyectar la dependencia del contexto de nuestra aplicación en el serializador? Para hacer esto, considere la implementación del serializador anterior.

 //     , //  Jackson   Spring, // API    //    Spring DI @JsonComponent public class MoneySerializer extends JsonSerializer<BigDecimal> { //  ,   , //    Spring Boot    Bean'. private RoundingHolder roundingHolder; @Autowired public MoneySerializer(RoundingHolder roundingHolder) { this.roundingHolder = roundingHolder; } //       , // ,   ,      - //      . @Override public void serialize(BigDecimal value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { jsonGenerator.writeNumber(value.setScale(roundingHolder.getPrecision(), roundingHolder.getRoundingMode())); } } 

Nuestro servicio está listo, pero como desarrolladores responsables, queremos asegurarnos de que la cocina que ensamblamos funcione.

Pasemos a las pruebas.


Veamos qué nos ofrece la API de framework de prueba.

 //     ,   //    Spring,       . //     JsonTest,      //      , //    JSON-. @JsonTest @ContextConfiguration(classes = {AccountSerializationTest.Config.class}) @RunWith(SpringRunner.class) public class AccountSerializationTest { //  ,      //     ObjectMapper,    . //       . //    , //      . @Autowired private ObjectMapper objectMapper; @Test public void testAccountMoneyRounding() throws Exception { Account account = new Account(); account.setClientId(1L); account.setValue(BigDecimal.valueOf(1.123456789)); String expectedResult = "{\"clientId\":1,\"value\":\1.123\}"; // ,          JSON, //     -. assertEquals(expectedResult, objectMapper.writeValueAsString(account)); } //   MoneySerializer   API  //    ,       //    Jackson.   ,   , //   Spring , ,  //      . @TestConfiguration public static class Config { @Bean public static RoundingHolder roundingHolder() { RoundingHolder roundingHolder = Mockito.mock(RoundingHolder.class); //   ,         Mockito.when(roundingHolder.getMathContext()).thenReturn(new MathContext(3, RoundingMode.HALF_EVEN)); return roundingHolder; } } } 

Detengámonos en este momento con más detalle. Jackson usa la clase ObjectMapper para serializar y deserializar modelos. Este es exactamente el objeto de contexto responsable de la transformación de los modelos, por lo tanto, para asegurarse de cómo se presentará el modelo, debe verificar cómo ObjectMapper lo procesa desde el contexto.

Si desea crear su propio ObjectMapper personalizado, puede encontrar el siguiente ejemplo típico: ObjectMapper mapper = new ObjectMapper . Sin embargo, observe cómo Spring Boot crea una instancia de esta clase de forma predeterminada. Para hacer esto, pasamos al código original de autoconfiguración JacksonAutoConfiguration , que es responsable de crear el objeto:

 @Bean public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) { return builder.createXmlMapper(false).build(); } 

Y si vamos más allá y miramos build () , encontramos que para la serialización, a la que podríamos acostumbrarnos cuando trabajamos con el mapeador predeterminado (como inyectar servicios en un serializador personalizado), no es suficiente crear un mapeador Bean, debe recurrir al constructor proporcionado . Por cierto, en la propia documentación de Spring Boot, esto se establece explícitamente.

Por digresión, me gustaría agregar una referencia a JacksonTester . Como representante del shell para las pruebas de serialización de BDD en el contexto de Mockito.

Para resumir


  • Spring Boot proporciona la capacidad de personalizar la serialización del modelo a través de anotaciones JsonSerializer
  • Para probar la serialización, use el asignador en la misma configuración que en la aplicación
  • Al anular un bean de la configuración automática de Spring Boot, preste atención a cómo este bean crea Spring Boot, para no perder las oportunidades que tenía el bean predeterminado
  • Puede usar la anotación JsonTest para especificar el contexto limitado requerido para probar la serialización.

Conclusión


Gracias por su atencion! Este ejemplo será relevante para la versión actual de Spring Boot 2.1.x, así como para versiones anteriores hasta 1.4.x. Además, la técnica es adecuada para situaciones con deserialización del modelo. Mire bajo el capó de sus marcos centrales para una mejor comprensión del mecanismo de operación de la aplicación y adopte un enfoque responsable para las pruebas.

Source: https://habr.com/ru/post/452188/


All Articles