#1 Testes unitários: Entendendo o conceito por trás da definição

Paula Grangeiro
Paula Grangeiro
Published in
3 min readMay 30, 2019

--

Ontem durante o Tech Radar #20 rolou uma apresentação supimpa da Angélica de Oliveira e do Rafael Toledo sobre Mockk, uma ferramenta para construir objetos mocks na linguagem Kotlin. O que mais me chamou atenção na apresentação (e o que motivou esse texto) foi o prológo sobre o que são mocks e porque nos utilizamos disto ao realizar testes unitários.

Na minha experiência, boa parte dos questionamentos sobre a utilização de mocks está relacionada a falta de entendimento do objetivo dos testes unitários. É interessante notar o quanto já estamos familiarizados com a técnica, o quanto somos capazes de aplicá-la, mas ainda assim não a compreendemos. E se não compreendemos somos incapazes de advogar em favor dela.

Gostaria de enfatizar que apesar de soar arrogante, este não é o meu objetivo. Eu tenho plena consciência de técnicas que sou capaz de aplicar sem sequer ter conhecimento profundo sobre o tema. Porém, por experiência própria, acredito que entender os porquês por trás de toda resposta é um processo que faz parte do aprendizado.

Pensando nisso, decidi criar essa série de pequenos textos com algumas coisas que já vi, li e falei sobre o assunto. Ainda não sei quantos textos serão, mas a ideia será explicar sobre o que são testes unitários, porquê escrevê-los e quando se utilizar de mocks.

O que são testes unitários?

Testes unitários são porções de código responsáveis por validar o comportamento de unidades funcionais de código. Nesse contexto, entende-se como unidade funcional qualquer porção de código que através de algum estímulo seja capaz de gerar um comportamento esperado.

A função soma, em kotlin, é capaz de através chamada com dois parâmetros (estímulo) retornar a soma dos mesmos (comportamento esperado).

Isso inclui funções, properties, construtores… Tudo que de alguma maneira processa um comportamento de valor e que você deseja garantir que apesar das alterações à nível de código o comportamento siga sendo o mesmo (a não ser que, por uma questão de negócio, ele explicitamente mude).

Testes unitários devem ser orientados à comportamento. Isso garante que ao haver refatoração os testes não quebrem.

A partir do momento que temos conhecimento de que testes unitários são frutos da relação estímilo versus comportamento, fica claro afirmar que uma mesma porção de código deve possuir um ou mais testes unitários. Logo, se por exemplo uma função possui N parâmetros de entrada e a partir deles pode gerar N retornos, serão necessários N testes unitários para garantir comportamento de todos os seus possíveis N fluxos. Complicou não é mesmo? E é por isso que testes unitários também podem ser utilizados como um termômetro de bad smells do código funcional a ser testado.

A complexidade de se testar unitáriamente determinada porção de código funcional indica o nível de coesão e acoplamento do mesmo.

A função soma, agora escrita em Python, possui dois possíveis compartamentos: um caso de sucesso, quando é possível somar os dois parâmetros; e um caso de falha quando a soma dos dois parâmetros lança uma exceção do tipo TypeError.
Exemplo do único teste unitário para a função soma escrita em kotlin. Como Kotlin é uma linguagem compilada de tipagem estática, não corremos o risco de enviar parâmetros de tipos indesejados para a função soma.

Testes unitários: uma analogia ao relógio

Digamos que você como pessoa desenvolvedora de software em um universo paralelo seja um relojoeiro. A sua responsabilidade é confeccionar relógios de qualidade desde o zero, construindo desde as menores peças. Ao confeccionar as centenas de engrenagens, molas e parafusos que irão constituir seu relógio, você deve seguir especificações. Cada unidade de engrenagens, molas e parafusos ao serem confeccionadas são testadas individualmente, medidas e verificadas, para garantir que irão funcionar conforme o esperado. Engrenagens devem suavemente girar, molas devem ter uma certa resistência, e parafusos devem ser de diferentes espessuras e tamanhos, e por aí vai. Tudo a depender de onde irão se encaixar dentro do relógio.

O ato de criar essas pequenas unidades e verificar os seus comportamentos individuais correspondem à escrever código funcional com testes unitários.

Ainda assim, construir esssas pequenas unidades e verificar o seu comportamento individualmente não garante que o relógio irá funcionar perfeitamente após ser montado. A mesma coisa ocorre com nosso código coberto por testes unitários e esse será o assunto do próximo post.

  • No texto, ao utilizar a expressão código funcional estou me referindo a uma porção de código que possui uma funcionalidade e não à programação funcional. Perdão se soou confuso de alguma maneira.
  • Você pode conferir a transmissão do Tech Radar #20 aqui. A apresentação da Angélica e do Toledo começa a partir de 1h:24m do vídeo.

Dúvidas ou sugestões? Não hesite em comentar ou contactar! Isto fica feliz em ser útil.

--

--

Programadora por profissão, desenhista nas horas vagas e colecionadora de gatos.