Typescript e Jest - Escrevendo testes unitários em um projeto Node.js
Tags: javascript, typescript, jest, testing
Published: 2022-02-14 18:21:42

![Triade dos testes](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/65gh3kru28vtqiru3jnw.png)
 
Todos nós sabemos a importância de testes em nosso código, precisamos deles para evitar os temidos bugs e torna-lá cada vez mais escalavél. O objetivo deste post é mostrar como executar testes unitários com Jest e TypeScript de forma objetiva, clara e direta ao ponto.

### Primeiros passos

Para realizarmos nossos primeiros testes com Jest precisamos de criar um projeto Node.js (ou React.js mas nesse post vamos utilizar um projeto em Node.js), para isso vamos iniciar um projeto. Gosto de criar meus projetos na area de trabalho portanto faço o seguinte ao abrir o terminal:

```bash
cd desktop
```
Ao acessar o terminal realizo a criação da pasta do projeto:

```bash
mkdir tests-node-jest
```
Agora vamos acessar a pasta:

```bash
cd tests-node-jest
```
e por fim vamos iniciar o projeto em Node.js:

```bash
npm init -y ou yarn init -y
```
Como vamos utilizar o Typescript temos que instalar o mesmo em nosso projeto:

```bash
npm install typescript -D ou yarn add typescript -D
```

Precisaremos também do Jest para começar a escrever nossos testes:

```bash
npm install jest -D ou yarn add jest -D
```

Por fim já que estamos utilizando o Typescript precisaremos instalar o pacote ts-jest e outro pacote com as tipagens do Jest:

```bash
npm install ts-jest -D ou yarn add ts-jest -D
npm install @types/jest -D ou yarn add @types/jest -D
```

### Configuração

Antes de iniciar os testes você precisar configurar o preset. No Jest o preset é um conjunto de configurações que servem de base.Embora essas configurações sejam muito simples, o **ts-jest** disponibiliza um comando para criar o arquivo **jest.config.js** com o preset correto.

```bash
npx ts-jest config:init
```

### Executando Testes com Jest e TypeScript

Por padrão o Jest executa todos os arquivos que ficam na pasta **__tests__** ou que tenham os trechos `test` ou `spec` no nome do arquivo.

Digamos que você tenha o arquivo `/src/index.ts`:

```javascript
export function double(x: number): number {
  return x * 2;
}

export function concat(...args: string[]): string {
  return args.reduce((result, param) => result + param, '');
}
```

Para testar essas funções basta você criar o arquivo `__tests__/index.test.ts`:

```javascript
import { double, concat } from '../src/index';

describe('testing index file', () => {
  test('double function', () => {
    expect(double(5)).toBe(10);
  });

  test('concat function', () => {
    expect(concat('John', ' ', 'Wick')).toBe('John Wick');
  });
});
```

Este exemplo contém um grupo de testes definido pela função `describe` e dentro do grupo tem dois testes definidos com a função test. Lembrando que também é possível criar sub-grupos com a função `describe`.

Dentro da função test você precisa declarar uma expectativa seguida de um **matcher**. Neste exemplo, **expect** é a expectativa e **toBe** é o **matcher**. Ou seja, você espera que o retorno de uma função (ou uma varíavel) seja igual a um determinado valor.

Para realizar os testes, execute este comando:

```bash
npx jest
```
Agora se algum teste falhar o Jest vai mostrar uma mensagem de erro. Como estamos utilizando Jest e TypeScript, vale destacar que inconsistências de tipos também serão apresentadas.

### Aperfeiçoando seus Testes

O exemplo que mostrei é algo muito simples, mas conforme seu software cresce seus testes também crescerão e provavelmente você precisará de funcionalidades mais avançadas. O **matcher** mais comum é o **toBe** e que funciona muito bem para comparar valores primitivos, porém com o tempo você precisará de **matchers** mais avançados. Eu fiz uma lista com os **matchers** mais populares:

- **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.

Algo muito interessante sobre os **matchers**, é que se você usar o prefixo `.not` a condição será invertida. Por exemplo:

```javascript
expect(1).not.toBe(2);
```
Se você deseja ver todos os **matchers** [na documentação oficial possui mais detalhes](https://jestjs.io/docs/expect).

### Testes Assíncronos com Promises

Algo muito comum em JavaScript é testar funções assícronas. Por exemplo, digamos que você tenha a seguinte função:

```javascript
export function waitSeconds(seconds: number): Promise<string> {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(`waited ${seconds} seconds`);
    }, seconds * 1000);
  });
}
```

Basicamente essa é uma função que aguarda alguns segundos e retorna uma mensagem. Ela tem como parâmetro um número que represente a quantidade de segundos e retorna uma **Promise<string>**.

Você pode testar isso de várias maneiras, mas eu separei quatro tipos de testes para esta função:

```javascript
import { waitSeconds } from '../src/index';

describe('example of asynchronous testing', () => {
  test('testing with async/await', async () => {
    expect(await waitSeconds(1)).toBe('waited 1 seconds');
  });

  test('testing returning a promise', () => {
    return expect(waitSeconds(1.5)).resolves.toBe('waited 1.5 seconds');
  });

  test('testing returning a promise with callback function', () => {
    return waitSeconds(0.5).then((response) => {
      expect(response).toBe('waited 0.5 seconds');
    });
  });

  test('testing with callback function', (done) => {
    waitSeconds(0.8).then((response) => {
      expect(response).toBe('waited 0.8 seconds');
      done();
    });
  });
});
```

Vale destacar que embora sejam quatro tipos diferentes de testes, o resultado deles é o mesmo e você pode escolher o que for mais conveniente para seu caso. Na [documentação oficial é mencionado sobre testes assíncronos](https://jestjs.io/docs/asynchronous)

Fontes onde pesquisei esse conteúdo:

- https://jestjs.io/docs/getting-started
- https://jestjs.io/docs/getting-started#using-typescript
- https://jestjs.io/docs/asynchronous
- https://jestjs.io/docs/asynchronous