Testes de integração com Nestjs, Jest, Supertest, Prisma e Testcontainers
Tags: testing, node, docker, nestjs
Published: 2023-08-24 13:20:14

## Motivação

Bem, não tenho muito costume em fazer esses tipos de publicação, mas como não achei nada especifico com a stack que utilizo no meu dia a dia, então resolvi trazer esse step by step.

## Nossa API

Para esse cenário de teste, resolvi criar uma API Rest utilizando o Nestjs juntamente com o Prisma para realizar a persistência dos dados no DB.

### Setup para realizar os testes
Certifique-se de ter o Docker e Node rodando na sua máquina

```typescript

import { INestApplication } from '@nestjs/common';
import { Test } from '@nestjs/testing';
import { PrismaClient } from '@prisma/client';
import {
  PostgreSqlContainer,
  StartedPostgreSqlContainer,
} from '@testcontainers/postgresql';
import { execSync } from 'child_process';
import { AppModule } from 'src/app.module';
import * as request from 'supertest';

describe('test POST /users', () => {
  let prisma: PrismaClient;
  let app: INestApplication;
  let container: StartedPostgreSqlContainer;

 beforeAll(async () => {
    // iniciando o container docker
    container = await new PostgreSqlContainer().start();

    // configurando a URL de conexão do prisma
    const urlConnection = `postgresql://${container.getUsername()}:${container.getPassword()}@${container.getHost()}:${container.getPort()}/${container.getDatabase()}?schema=public`;

    // definir a URL de conexão para conexãp do prisma
    process.env.DATABASE_URL = urlConnection;

    // criar as tabelas definidas no prisma no banco de dados
    execSync('npx prisma db push', {
      env: {
        ...process.env,
        DATABASE_URL: urlConnection,
      },
    });

    // importar o modúlo que queremos testar
    const moduleRef = await Test.createTestingModule({
      imports: [AppModule],
    }).compile();

    // criar a aplicação
    app = moduleRef.createNestApplication();

    // Instanciar o prisma client com a URL de conexão
    prisma = new PrismaClient({
      datasources: {
        db: {
          url: urlConnection,
        },
      },
    });

    // Iniciar a aplicação
    await app.init();
  });
```
Feito isso já temos nosso setup pronto para iniciar os testes. A biblioteca **@testcontainers/postgresql** vai abstrair toda parte de inicialização do banco de dados.

### Casos de teste
```typescript
  it('deve criar um novo usuário', async () => {
    // Act
    const response = await request(await app.getHttpServer())
      .post('/users')
      .send({
        email: 'test@mail.com',
        name: 'John Doe',
      });

    // Assert
    const userDb = await prisma.users.findFirst();
    expect(response.statusCode).toBe(201);
    expect(userDb.email).toBe('test@mail.com');
    expect(userDb.name).toBe('John Doe');
  });

  it('deve lançar um erro porque já existe um usuário com esse email', async () => {
    // Act
    const response = await request(await app.getHttpServer())
      .post('/users')
      .send({
        email: 'test@mail.com',
        name: 'John Doe',
      });

    // Assert
    expect(response.statusCode).toBe(422);
  });

  it('deve lançar um erro porque não foram enviados os dados para criação do usuário', async () => {
    // Act
    const response = await request(await app.getHttpServer()).post('/users');

    // Assert
    expect(response.statusCode).toBe(400);
  });
```

### Depois de executarmos todos os testes...
```typescript
  afterAll(async () => {
    // encerrar o container em execução
    await container.stop();
  });
```

### Executar os testes
```
npm run test
```
#### Nosso terminal
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9k4vkikdjxra56ial8zj.png)

#### Containers no docker
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i4qsfoximf7wgcn1a8xl.png)


### Links

**Documentação do testcontainers para o Node:** https://node.testcontainers.org/modules/postgresql/

**Meu repositório no github:**
https://github.com/daviArttur/nestjs-testcontainers