Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
April 16, 2024 12:10 am GMT

Reduzindo a complexidade e facilitando a criao de testes com Fail Fast e Early Return

Ao longo desses 3 anos como desenvolvedor de software pude perceber que muitos desenvolvedores no gostam de implementar testes, sejam eles unitrios ou de integrao, e que esse assunto tedioso e trs dor de cabea, afinal, quem nunca teve que criar casos de testes do zero ou at mesmo ajustar uma base inteira de testes aps uma nova implementao? No entanto esquecemos que testes so primordiais para garantirmos a qualidade do nosso sistema, pois um bom cdigo um cdigo bem testado, o design apenas perfumaria.

Apesar desta introduo, esta leitura no para discutirmos sobre testes e sim mostrar que um bom design pode nos auxiliar na escrita dos mesmo e assim entender o sistema, a lgica de negcio ou ramificaes com pouco esforo cognitivo.

Vamos fazer o seguinte, partiremos da premissa de que testes visam validar o comportamento de um sistema resultando e em um estado positivo (esperado) e negativo (inesperado) e que para cada ramificao (if or else) ser preciso criar cenrios que contemplam tais comportamentos.

Tendo isso em mente vamos analisar o cdigo e entend-lo o que ele faz para criarmos cenrios de testes para ele:

@RestController@RequestMapping("/ecommerce/v1")class ProdutoController(       private val produtoRepository: ProdutoRepository,) {   @PostMapping("/produtos")   fun helloWorld(           @RequestBody solicitacaoCompraProduto: SolicitacaoCompraProduto   ): ResponseEntity<Any> {       val possivelProduto = produtoRepository.buscar(solicitacaoCompraProduto.identificador)       if (possivelProduto.isPresent) {           val produto = possivelProduto.get()           if (produto.validade.isAfter(LocalDate.now())) {               if (solicitacaoCompraProduto.lote == produto.lote) {                   if (solicitacaoCompraProduto.quantidade < produto.quantidade) {                       if (solicitacaoCompraProduto.precoUnitario  ==  produto.precoUnitario) {                           produto.quantidade - solicitacaoCompraProduto.quantidade                           produtoRepository.atualizar(produto)                           return ResponseEntity.status(201).build()                       }                   }               }           }       }       return ResponseEntity.internalServerError().build()   }}

Podemos notar que a experincia de leitura de cdigo pssima e o esforo para entend-lo grande, o que no condiz com o seu tamanho de bloco j que ele pequeno. E depois de alguns minutos consigo compreender que objetivo deste cdigo fornecer uma API Rest fornecendo um ponto de entrada para que seja possvel a compra de um produto, validar as entradas do sistema e caso elas estejam todas de acordo com esperado executado uma ao de reduo de produto no estoque, consumado a compra, caso contrrio retornado um resposta de erro ao cliente.

Agora vamos refatorar um cdigo para obtermos uma experincia melhor de leitura:

@RestController@RequestMapping("/ecommerce/v1")class ProdutoController(       private val produtoRepository: ProdutoRepository,) {   @PostMapping("/produtos")   fun helloWorld(           @RequestBody solicitacaoCompraProduto: SolicitacaoCompraProduto   ): ResponseEntity<Any> {       val possivelProduto = produtoRepository.buscar(solicitacaoCompraProduto.identificador)       if (!possivelProduto.isPresent) {           return ResponseEntity.notFound().build()       }       val produto = possivelProduto.get()       try {           produto.deduzir(               quantidade = solicitacaoCompraProduto.quantidade,               lote = solicitacaoCompraProduto.lote,               precoUnitario = solicitacaoCompraProduto.precoUnitario           )       } catch (ex: Exception) {           return ResponseEntity.badRequest().body(ex.message)       }       produtoRepository.atualizar( produto)       return ResponseEntity.ok().build()   }}
data class Produto(       val identificador: Long,       val nome: String,       val precoUnitario: BigDecimal,       val quantidade: Int,       val validade: LocalDate,       val lote: String) {       val estaVencido get() = validade.isAfter(LocalDate.now())       fun deduzir(               quantidade: Int,               lote: String,               precoUnitario: BigDecimal       ) {               if (estaVencido) {                       throw Exception("O produto est vencido")               }               if (this.lote != lote) {                       throw Exception("O lote  diferente")               }               if (this.quantidade < quantidade) {                       throw Exception("Quantidade acima da capacidade")               }               if (precoUnitario != this.precoUnitario) {                       throw Exception("Preo divergente")               }               this.quantidade - quantidade       }}

Podemos observar que temos um cdigo limpo e objetivo. Consigo quase que de maneira intuitiva entender o que ele faz e visualizar suas regras para deduzir um produto.

Olhando para este cdigo eu consigo contemplar diversos cenrios de testes:

Caso o produto no seja encontrado, retorne um status http 404
Caso o produto esteja vencido, retorne um status http 400 com a mensagem: O produto est vencido
Caso o lote do produto seja diferente, retorne um status http 400 com a mensagem: O lote diferente

E assim sucessivamente para os demais casos! E observe que os testes so em cima dos comportamento inesperado do sistema, pois quando todos os dados estiverem okay ocorrer o comportamento esperado, e a sim surge o caso de sucesso:

Caso todas as entradas estejam corretas, retorne um http status 200

E com casos de testes mapeados podemos escrever testes baseando-se neles! Simples, n?

Mas claro, essa refatorao segue trs tcnicas de programao, Tell Dont ask, Fail Fast e Early Return.

Quero focar somente no Fail Fast e Early Return, dado que so to simples e eficazes.

Basicamente Fail Fast, ou falha rpida, visa detectar e reportar erros do sistema imediatamente impedindo que ele prossiga com dados incorretos. Isso envolve verificaes de condies de erro.

Podemos ver essa abordagem no mtodo deduzir da classe Produto

fun deduzir(               quantidade: Int,               lote: String,               precoUnitario: BigDecimal       ) {               if (estaVencido) {                       throw Exception("O produto est vencido")               }               if (this.lote != lote) {                       throw Exception("O lote  diferente")               }               if (this.quantidade < quantidade) {                       throw Exception("Quantidade acima da capacidade")               }               if (precoUnitario != this.precoUnitario) {                       throw Exception("Preo divergente")               }               this.quantidade - quantidade       }

J Early Return, ou retorno antecipado, retorna imediatamente um condio quando ela atendida, evitando ifs aninhados e deixando o cdigo mais objetivo.

Essa abordagem foi aplicada na classe ProdutoController quando verificamos se o produto no est presente na base

val possivelProduto = produtoRepository.buscar(solicitacaoCompraProduto.identificador)if (!possivelProduto.isPresent) {   return ResponseEntity.notFound().build()}

Portanto, essas tcnicas nos auxiliam reduzir a carga cognitiva para entender a complexidade de um sistema, nos ajudam nas escritas de testes j que por intermdio deles conseguimos compreender melhor os cenrios para test-los e de brinde temos um bom design cdigo. Lembrando que design perfuma e que a qualidade de um cdigo esto relacionados a sua base de testes e agora voc tem armas para poder facilitar a criao da sua.

Se ficou curioso para saber sobre Tell Dont Ask eu falo sobre ele nesta publicao https://dev.to/joaopolira/contribuindo-para-coesao-e-encapsulamento-atraves-do-design-de-codigo-tell-dont-ask-5gfi


Original Link: https://dev.to/joaopolira/reduzindo-a-complexidade-e-facilitando-a-criacao-de-testes-com-fail-fast-e-early-return-4l28

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To