Como configurar o Jest, SWC e TypeScript para testar na velocidade da luz

2021-12-18

Vamos começar criando um repositório base. Aqui vou usar o [[PNPM]] para gerenciar as dependências do projeto.

$ mkdir super-dope && cd super-dope && pnpm init -y

Ao executar a linha acima, você deverá ver algo mais ou menos assim no seu terminal:

$ mkdir super-dope && cd super-dope && pnpm init -y
Wrote to /tmp/super-dope/package.json:

{
  "name": "super-dope",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

Legal, com o projeto criado agora vamos configurar as ferramentas necessárias.

##Instalando o Jest

[[Jest]] é um *test runner* (executor de testes). É o Jest o responsável por ler o nosso código, transformar em algo que o Node.js consiga executar e então executar o código.

O Jest também se encarrega de gerenciar mocks e hooks. Por exemplo, caso você queira sobrescrever a definição de alguma função em algum módulo, você pode criar um mock para aquela função; você também pode querer executar algum código antes ou depois de cada teste, o Jest te oferece essa possibilidade através dos hooks beforeEach, beforeAll, afterEach e afterAll.

Vamos instalar o Jest via linha de comando:

super-dope$ pnpm i -D jest

Agora vamos inicializar as configurações do Jest:

super-dope$ pnpm exec jest --init

The following questions will help Jest to create a suitable configuration for your project

✔ Would you like to use Jest when running "test" script in "package.json"? … yes
✔ Would you like to use Typescript for the configuration file? … yes
✔ Choose the test environment that will be used for testing › node
✔ Do you want Jest to add coverage reports? … no
✔ Which provider should be used to instrument code for coverage? › v8
✔ Automatically clear mock calls, instances and results before every test? … yes

✏️  Modified /tmp/super-dope/package.json

📝  Configuration file created at /tmp/super-dope/jest.config.ts

##Configurando o Jest com SWC

O [[SWC]] é um compilador em Rust super rápido, que consegue transformar código em JavaScript moderno ou TypeScript em JavaScript que possa ser executado no Node.js. O Next.js 12 usa o SWC por baixo dos panos para compilar o projeto, então estamos em boas mãos aderindo ao SWC.

Vamos instalar o @swc/core e @swc/jest como dependências de desenvolvimento:

super-dope$ pnpm i -D @swc/core @swc/jest

Agora precisamos indicar que o Jest deve usar o SWC como compilador, para isso vamos editar o arquivo jest.config.ts e adicionar o trecho abaixo:

export default {
  // ...

  // A map from regular expressions to paths to transformers
  transform: {
    "^.+\\.(t|j)sx?$": ["@swc/jest"],
  },

  // ...
}

Vamos criar um arquivo de teste simples para verificar se está tudo certo até agora. Vou usar a linha de comando novamente:

super-dope$ mkdir src && touch src/main.test.ts

No arquivo main.test.ts, vamos adicionar um teste bobinho:

src/main.test.ts
test('Esse teste deve passar', () => {
  expect(true).toBe(true)
})

Ao executar o teste, vamos um graaaaaande problema:

super-dope$ pnpm test

> super-dope@1.0.0 test /tmp/super-dope
> jest

Error: Jest: Failed to parse the TypeScript config file /tmp/super-dope/jest.config.ts
  Error: Jest: 'ts-node' is required for the TypeScript configuration files. Make sure it is installed
Error: Cannot find module 'ts-node'
Require stack:
- /tmp/super-dope/node_modules/.pnpm/jest-config@27.4.5/node_modules/jest-config/build/readConfigFileAndSetRootDir.js
- /tmp/super-dope/node_modules/.pnpm/jest-config@27.4.5/node_modules/jest-config/build/index.js
- /tmp/super-dope/node_modules/.pnpm/jest-cli@27.4.5/node_modules/jest-cli/build/init/index.js
- /tmp/super-dope/node_modules/.pnpm/jest-cli@27.4.5/node_modules/jest-cli/build/cli/index.js
- /tmp/super-dope/node_modules/.pnpm/jest-cli@27.4.5/node_modules/jest-cli/bin/jest.js
- /tmp/super-dope/node_modules/.pnpm/jest@27.4.5/node_modules/jest/bin/jest.js
    at readConfigFileAndSetRootDir (/tmp/super-dope/node_modules/.pnpm/jest-config@27.4.5/node_modules/jest-config/build/readConfigFileAndSetRootDir.js:118:13)
    at async readConfig (/tmp/super-dope/node_modules/.pnpm/jest-config@27.4.5/node_modules/jest-config/build/index.js:233:18)
    at async readConfigs (/tmp/super-dope/node_modules/.pnpm/jest-config@27.4.5/node_modules/jest-config/build/index.js:420:26)
    at async runCLI (/tmp/super-dope/node_modules/.pnpm/@jest+core@27.4.5/node_modules/@jest/core/build/cli/index.js:132:59)
    at async Object.run (/tmp/super-dope/node_modules/.pnpm/jest-cli@27.4.5/node_modules/jest-cli/build/cli/index.js:155:37)
 ELIFECYCLE  Test failed. See above for more details.

Isso aconteceu porque embora o Jest esteja usando o SWC para compilar o código de teste, o Jest é meio burrão e não consegue usar o SWC para compilar o próprio arquivo de configuração, o jest.config.ts. Note que é um arquivo TypeScript.

Podemos resolver isso instalando o compilador do typescript e o ts-node como dependência de desenvolvimento:

super-dope$ pnpm i -D typescript ts-node

Vou aproveitar e já inicializar o TypeScript no repositório executando pnpm exec tsc --init. Agora se tentarmos executar o teste novamente, tudo deve funcionar certinho:

super-dope$ pnpm test

> super-dope@1.0.0 test /tmp/super-dope
> jest

 PASS  src/main.test.ts
  ✓ Esse teste deve passar (2 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        0.341 s
Ran all test suites

Importante: note que o SWC não faz verificações de tipo em arquivos TypeScript no momento da compilação. O trabalho do SWC é basicamente traduzir o código TypeScript em algo executável no Node. Por esse motivo, é sempre bom também executar o próprio compilador do TypeScript em paralelo, exemplo:

super-dope$ pnpm exec tsc --noEmit
src/main.test.ts:1:1 - error TS2582: Cannot find name 'test'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.

1 test('Esse teste deve passar', () => {
  ~~~~

src/main.test.ts:2:3 - error TS2304: Cannot find name 'expect'.

2   expect(true).toBe(true)
    ~~~~~~


Found 2 errors.

Oops, parece que faltam tipos. Podemos instalar como dependências de desenvolvimento também:

super-dope$ pnpm i -D @types/jest @types/node

Agora se executarmos o comando do tsc novamente, nada será impresso no terminal, isso significa que o nosso projeto está correto em termos de tipos.

##Resumo

O [[Jest]] por padrão usa o Babel para compilar código em JavaScript moderno ou Typescripe em algo que possa ser executado no Node.js. O [[SWC]] é uma plataforma de desenvolvimento web super performática que pode ser usado junto com o Jest.

Com o objetivo de ter feedback rápido durante a execução dos testes, a união de Jest e SWC é mais uma killer feature do ecossistema Node. Ambas são excelentes ferramentas para você ter no seu cinto de utilidades.

##Saiba mais

  • A documentação do Jest é excelente! Dê uma olhada em jestjs.io.
  • Vale a pena também entender como o SWC funciona, veja em swc.rs
  • Na minha opinião o [[PNPM]] é o melhor gerenciador de pacotes no mundo Node.js, veja mais em pnpm.io.
#jest#swc#typescript#testes