Como vimos na Parte 1, testar detalhes de implementação no front-end pode ser uma armadilha que pode acabar mascarando erros gerando falso-positivos ou falso-negativos ou até mesmo atrapalhando a produtividade por serem muito frágeis.
Vamos então abordar de maneira prática um caminho para se pensar em testes no front-end de forma que eles se tornem mais confiáveis e com mais qualidade.
O que será abordado:
- Testando Casos de Uso
- Cobertura de Código
- Cobertura de Casos de Uso
- Quando a Cobertura de Código oculta os Casos de Uso
Testando Casos de Uso
"Quanto mais seus testes se assemelham à forma como seu software é usado, mais confiança eles podem lhe dar." - Kent C. Dodds
Essa é a natureza dos testes de casos de uso, pensar em escrever testes que atendam a forma que nossos dois usuários** (usuário-final e usuário-desenvolvedor) usam e interagem com nossa aplicação.
**ver Detalhes de implementação da parte 1
Escrevemos testes para ter certeza de que nosso aplicativo funcionará quando o usuário os usar e principalmente melhorar a confiança da aplicação.
"Pense menos no código que você está testando e mais nos casos de uso que o código suporta." - Kent C. Dodds
Cobertura de Código
Cobertura de código é a métrica que nos mostra quais linhas do nosso código estão rodando durante o teste. Vamos utilizar esse código como exemplo:
function verificaArray(array) {
if (Array.isArray(array)) return array
else if (!array) return []
else return [array]
}
Sem incluir teste para essa função, o relatório vai indicar que 0% de Cobertura de código nos dando a ideia de quanta parte do código ainda falta cobrir.
O relatório de Cobertura de código não diz exatamente qual a importância da função verificaArray
ou nem mesmo diz os casos de uso que ela atende.
Mas quando consideramos a aplicação inteira, mesmo o relatório de Cobertura de código não nos dando uma a visão completa, ele nos ajuda a identificar quais testes poderiam ser adicionados para atender os casos de uso da aplicação.
Cobertura de Casos de Uso
A Cobertura de Casos de Uso nos diz quantos casos de uso nosso teste atende. Voltando ao código de exemplo, podemos identificar o primeiro caso de uso da função verificaArray
:
- "Retorna um array se o argumento for um array".
Obs: Há uma convenção de escrever os títulos dos testes em inglês, entretanto neste estudo, vou escrevê-los em português-BR por uma questão didática, beleza?
test('Retorna um array se o argumento for um array', () => {
expect(verificaArray(['Elefante', 'Girafa']))
.toEqual(['Elefante', 'Girafa'])
})
E com esse teste escrito, nosso relatório de Cobertura de Código vai parecer com algo do tipo:
function verificaArray(array) { // coberto
if (Array.isArray(array)) return array // coberto
else if (!array) return []
else return [array]
}
Assim, se olharmos para as linhas que faltam, vamos concluir que são mais dois casos de Uso que faltam ser cobertos:
- "Retorna um array vazio se o argumento tiver um valor falso"
- "Retorna um array se o argumento não for um array e não for falso"
Vamos adicionar os testes para esses casos de uso, e ver como isso afeta a Cobertura de Código
test(
'Retorna um array vazio se o argumento tiver um valor falso', () => {
expect(verificaArray()).toEqual([])
})
test(
`Retorna um array se o argumento não for um array e não for falso`,
() => {
expect(verificaArray('Leopardo')).toEqual(['Leopardo'])
})
Cobertura:
function verificaArray(array) { // coberto
if (Array.isArray(array)) return array // coberto
else if (!array) return [] // coberto
else return [array] // coberto
}
Ótimo! Agora temos 100% de cobertura de código, mas também 100% de cobertura de casos de uso e podemos confiar que, enquanto não mudarmos os casos de uso dessa função, nossos testes vão continuar passando.
Quando a cobertura de código oculta os casos de uso
As vezes, o relatório de Cobertura de código pode indicar 100% de cobertura, mas não 100% de cobertura de casos de uso.
Esse é o motivo que é bom ter a prática de pensar em todos os casos de uso possíveis antes mesmo de começar a escrever os testes.
Por exemplo, vamos imaginar que a function verificaArray
foi implementada desse jeito:
function verificaArray(array) {
if (Array.isArray(array)) return array
else return [array].filter(Boolean)
}
Com isso, nós temos 100% de cobertura com apenas os 2 casos de uso:
- Retorna um array se o argumento for um array
- Retorna um array se o argumento não for um array
Entretanto, se olharmos para o relatório de Cobertura de caso de uso, podemos perceber que está faltando um caso de uso:
- Retorna um array vazio se o argumento tiver um valor falso
E isso é um mau sinal, pois mesmo se o usuário user a função verificaArray
sem passar nenhum argumento, os testes vão continuar passando, mas o código vai quebrar.
E essa é justamente uma das razões que implementamos testes, garantir que o código continue atendendo aos casos de uso que desejamos incluir.
Por exemplo, se um outro dev remover o .filter(Boolean)
, os testes ainda continuam passando, mas qualquer código que dependia do resultado da function como sendo "falso", vai quebrar.
👉 Continue lendo na parte 3
Referências:
"How to know what to test" - Kent C. Dodds
https://kentcdodds.com/blog/how-to-know-what-to-test
"Avoid the Test User" - Kent C. Dodds
https://kentcdodds.com/blog/avoid-the-test-user
"Testing Implementation Details" - Kent C. Dodds
https://kentcdodds.com/blog/testing-implementation-details
Top comments (0)