Teste de unidade de serialização Json no Spring Boot



1. Introdução


Uma das principais tarefas de cada serviço da web é retornar o modelo para o lado do cliente e, nesse caso, o Spring Boot fornece um nível conveniente de abstração, permitindo que o desenvolvedor permaneça no nível de trabalho com modelos e deixe o processo de serialização do modelo fora do código-fonte do programa. Mas e se a própria serialização se tornar parte da lógica de negócios do aplicativo e, portanto, exigir cobertura de caso de teste?

Este artigo examinará um dos cenários em que precisaremos levar em consideração as peculiaridades da lógica de negócios do aplicativo durante a serialização (o cenário de arredondamento de valores monetários), no exemplo do qual encontraremos o mecanismo de serialização no Spring Boot e também descreverá um possível método de teste.

Declaração do problema


Deixe nosso serviço da Web ser responsável por fornecer informações sobre as despesas do cliente e precisamos fornecer dados com precisão configurável. Uma solução lógica seria fazer todas as transformações de modelo na periferia do serviço, mantendo a visibilidade da aplicação da lógica de arredondamento.

Considere uma solução possível


Imagine o controlador do nosso aplicativo, que retornará o modelo desejado.

@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; } } 

Agora vamos olhar para o nosso modelo.

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

Você já pode ter que lidar com outras anotações para personalização. Um recurso desta anotação é a capacidade de determinar seu serviço responsável por serializar o campo do modelo anotado.

Antes de analisarmos o que é um serializador, complicaremos a tarefa: permita que os parâmetros de arredondamento sejam configuráveis ​​por meio de algum serviço interno que abstraia qualquer manifestação da resolução dinâmica de parâmetros.

Essa complicação é o nosso ponto principal que queremos considerar. Como podemos ver na implementação do modelo, a API da nossa estrutura adota uma classe de serialização no argumento, o que significa que o ciclo de vida do serializador fica sob o controle da estrutura do serializador. Isso levanta a questão: o que fazer se quisermos injetar a dependência do contexto de nossa aplicação no serializador? Para fazer isso, considere a implementação do serializador acima.

 //     , //  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())); } } 

Nosso serviço está pronto, mas como desenvolvedores responsáveis, queremos garantir que a cozinha que montamos funcione.

Vamos para o teste.


Vamos ver o que a API da estrutura de teste nos oferece.

 //     ,   //    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; } } } 

Vamos nos debruçar sobre este momento com mais detalhes. Jackson usa a classe ObjectMapper para serializar e desserializar modelos. Esse é exatamente o objeto de contexto responsável pela transformação dos modelos; portanto, para garantir como o modelo será apresentado, é necessário verificar como o ObjectMapper o processa a partir do contexto.

Se você deseja criar seu próprio ObjectMapper personalizado, é possível encontrar o seguinte exemplo típico: Mapeador de ObjectMapper = new ObjectMapper . No entanto, observe como o Spring Boot cria uma instância dessa classe por padrão. Para fazer isso, recorremos ao código de configuração automática JacksonAutoConfiguration original, responsável pela criação do objeto:

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

E se formos ainda mais longe e analisarmos o build () , descobrimos que, para serialização, com a qual poderíamos nos acostumar ao trabalhar com o mapeador padrão (como injetar serviços em um serializador personalizado), não basta criar um mapeador de Bean, você deve procurar o construtor fornecido . A propósito, na documentação do Spring Boot, isso é explicitamente declarado.

Com uma digressão, gostaria de adicionar uma referência ao JacksonTester . Como representante do shell para testes de serialização de BDD no contexto do Mockito.

Resumir


  • O Spring Boot fornece a capacidade de personalizar a serialização do modelo através de anotações do JsonSerializer
  • Para testar a serialização, use o mapeador na mesma configuração que no aplicativo
  • Ao substituir um bean da configuração automática do Spring Boot, preste atenção em como esse bean cria o Spring Boot, para não perder as oportunidades que o bean padrão tinha
  • Você pode usar a anotação JsonTest para especificar o contexto limitado necessário para testar a serialização.

Conclusão


Obrigado pela atenção! Este exemplo será relevante para a versão atual do Spring Boot 2.1.x, bem como para as versões anteriores até 1.4.x. Além disso, a técnica é adequada para situações com desserialização do modelo. Procure nos bastidores de suas estruturas principais uma melhor compreensão do mecanismo de operação do aplicativo e adote uma abordagem responsável aos testes.

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


All Articles