Jest é um framework de teste unitário de código aberto em JavaScript criado pelo Facebook a partir do framework Jasmine.
A cobertura de testes indica quais partes de seu código estão sendo executadas.
Se temos um código de 10 linhas e uma cobertura de 50%, isso quer dizer que apenas metade das linhas desse código, ou seja, 5 linhas estão sendo executadas ao longo dos testes.
Algumas ferramentas geram alguns relatórios visuais em HTML ou no terminal.
----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
---|---|---|---|---|---|
All files | 100 | 100 | 100 | 100 | |
data.js | 100 | 100 | 100 | 100 | |
---------- | ---------- | ---------- | ---------- | ---------- | ------------------- |
Arquivo (ou file): todos arquivos que foram testados
Declarações (ou statements/stmts): indica se declaração como variáveis e imports foram ou não cobertos
Ramificações (ou branch): se trechos como blocos if/else, switch/case ou ternários foram executadas
Funções (ou functions/funcs): indica se as funções do seu código foram executadas
Linhas (ou lines): indica quantas linhas do código estão cobertas
Linhas não cobertas (ou uncovered lines): indica quantas linhas do código não estão cobertas
Suítes de teste é um termo utilizado pra exemplificar um agrupamento de testes.
Temos uma estrutura opcional, describe
que é exclusivamente para realizar um agrupamento mais específico de testes relacionados.
Para realizar um teste usamos a função test
ou o seu apelido it
describe('Calculadora', () => {
it('Soma', () => {
});
// ou com test
test('Soma', () => {
});
});
Asserção significa afirmação. É com as asserções que iremos afirmar o comportamento esperado do nosso código.
Podemos pensar na utilização das asserções em duas partes:
-
valor atual (ou de entrada/esperado);
-
valor a ser comparado (ou de saída/resultado).
Usamos a função expect
para fazer uma asserção
Outra estrutura que as ferramentas que o jest disponibiliza
Podemos utilizar as funções:
beforeAll
: para executar algo antes de todos os testes;
afterAll
: para executar algo após todos os testes finalizarem;
beforeEach
: para executar algo antes de cada um dos testes;
afterEach
: para executar algo após cada um dos testes executarem.
É comum que alguns arquivos de teste sejam escritos dentro de um diretório como __test__
ou então com o sufixo .test
ou .spec
(que quer dizer especificação).
npm init -y
npm i -D jest
package.json
{
"scripts": {
"test": "jest"
},
"devDependencies": {
"jest": "^29.0.0"
}
}
desconto1.js
function freteGratis (valor) {
return valor >= 150
}
module.exports = { freteGratis }
desconto1.test.js ou desconto.spec.js
describe('Desconto 1', () => {
const desconto1 = require('./desconto1')
const freteGratis = desconto1.freteGratis
test('Frete Gratis maior que 150', () => {
expect(freteGratis(151)).toBeTruthy()
})
})
Executar os testes
npm test
ou npx jest
Podemos configurar algumas opções do jest:
npx jest --init
Ele vai gerar o arquivo:
jest.config.js
module.exports = {
clearMocks: true,
collectCoverage: true,
coverageDirectory: "coverage"
};
Apartir de agora ele irá mostrar o relatorio de cobertura de testes, pois irá pegar nossa configuração.
Podemos executar um teste em uma pasta especifica:
npx jest ./src
ou colocar em nosso arquivo de configuração jest.config.js a seguinte linha:
"roots": ["./src", "./src1"],
Em conjunto podemos determinar quais tipos de arquivos restringimos o nosso testes:
"testMatch": ["**/*.spec.js"]
Assim em qualquer pasta que tem um arquivo terminado com spec.js
será executado o test.
Para pegar uma pasta em especificas e seus subdiretorios:
"testMatch": ["**/src/**/*.spec.js"]
Podemos ver mais detalhes dos teste usando npx jest --verbose
Vamos instalar o typescript e um pacote de tipagem para o jest, @types/jest
e ts-jest
npm i -D typescript @types/node jest ts-jest @types/jest
npx tsc --init
Vai ser instalado as versões:
"@types/jest": "^28.1.8",
"jest": "^28.1.3",
"ts-jest": "^28.0.8",
Precisamos configurar o preset no jest.config.js
module.exports = {
preset: 'ts-jest',
};
A versão "jest": "^29.0.0",
do jest não funciona mais com o ts-jest
Para rodar os testes teriamos que compilar o typescript para javascript.
Vamos utilizar uma ferramenta da Vercel construida em Rust que faz a compilação de typescript para javascript.
npm i -D @swc/jest @swc/core
Modificar o jest.config.ts
para fazer transformações, toda vez que ele encontrar um arquivo typescript e vai aplicar swc para fazer a compilação:
jest.config.js
module.exports = {
transform: {
"^.+\\.ts?$": ["@swc/jest"]
}
};
podemos ter um arquivo typescript para configurar o jest:
jest.config.ts
export default {
transform: {
"^.+\\.ts?$": ["@swc/jest"]
}
}
Exemplo:
export default {
roots: ['<rootDir>/src'],
clearMocks: true,
collectCoverage: true,
collectCoverageFrom: ['<rootDir>/src/**/*.ts'],
coverageDirectory: "coverage",
testEnvironment: 'node',
transform: {
"^.+\\.ts$": ["@swc/jest"]
}
}
Mas neste caso precisamos colocar a lib ts-node
npm i -D ts-node
npx jest
Os matchers podem ser do tipo:
Comuns: usados para testar igualdade de valores de forma exata;
Veracidade: usados para distinguir de forma explícita entre undefined, null e false;
Number: usados para comparar números equivalentes;
String: usados para verificar expressões regulares;
Arrays e iteráveis: usados para verificar a inclusão de um item em um array ou iterável;
Exceções: usado para testar se uma função lança um erro quando chamada;
Clique aqui, para acessar a lista completa de matchers.
toEqual
- Útil para verificar objetos e suas propriedades
toBeFalsy
- Útil para verificar valores que podem ser convertidos para false por meio da coerção do JavaScript.
toBeTruthy
- Útil para verificar valores que podem ser convertidos para true por meio da coerção do JavaScript.
toContain
- Útil para verificar arrays com valores primitivos.
toContainEqual
- Útil para verificar arrays com objetos.
toMatch
- Útil para verificar strings e expressões regulares (Regex).
toThrow
- Útil para verificar se uma função lançou uma exceção.
Podemos instalar um plugin jest runner
da firsttris para rodar os testes automaticamente
Duas funções para “simular” chamadas a outras funções: spyOn()
e jest.fn()
. Mas existe alguma diferença teórica entre elas? Posso usar tanto uma quanto outra?
Como vimos, chamamos de mocking o processo de “substituir” (ou criar “dublês”) módulos. Substituímos a implementação original destes módulos por código que podemos testar - normalmente objetos que retornam dados úteis para os nossos testes.
Isso foi feito de duas formas durante o curso, substituindo o retorno da função com jest.fn() e chamando a função com spyOn(). Então qual a diferença?
Podemos utilizar jest.fn() quando a implementação original da função - ou seja, o código que ela executa - não é importante para o teste, e pode ser substituída pelo que definimos durante o teste, normalmente retornando um objeto.
E por que a implementação não é importante, ou em que casos isso ocorre? Por exemplo, quando o módulo que estamos testando em nossos testes unitários executa internamente uma outra função. Por se tratar de um teste unitário de nosso código, muitas vezes não temos interesse neste momento em saber da execução de outra função interna, especialmente quando se trata de bibliotecas ou módulos externos ao nosso código.
No caso de jest.spyOn(), não há substituição da implementação original da função e queremos testar se, por exemplo, a função está sendo “chamada”, se está recebendo determinado parâmetro, etc. Nesse caso, apenas executar a função com jest.spyOn() ainda vai executar a função “original” e o código dentro dela. Porém, também é possível “mocar” (ou seja, substituir a implementação original de uma função).
Para ver todos os métodos do Jest relativo a mock de funções, você pode consultar a lista na documentação oficial.