DEV Community

Cover image for Testes com NestJs e Prisma
Vitor Martins
Vitor Martins

Posted on

17 3

Testes com NestJs e Prisma

O NestJs é uma ferramenta incrível que descobri não faz muito tempo. Como não sou muito familiarizado com o backend, tive um pouco de dificuldade para desenvolver alguns testes com o NestJs e também o Prisma. Por isso, quero compartilhar esse pequeno passo a passo para que você consiga testar suas aplicações mais facilmente.

Pra esse tutorial pularei a parte de instalação e configuração do Nest e do Prisma. Vamos considerar que temos uma API que precisa acessar um banco de dados SQL e entregar postagens de um blog para o client.


Criando o modulo posts

Vamos começar primeiramente criando o nosso modulo chamado posts, rodando o seguinte comando:

nest generate resource posts

Com esse comando o Nest criará automaticamente um módulo CRUD pronto para ser usado. Vamos então criar nossa entidade chamada Post onde será mapeado a tabela da base de dados

import { Prisma } from '@prisma/client';

export class Post implements Prisma.PostUncheckedCreateInput {
  id?: number;
  titulo: string;
  conteudo?: string;
  publicado?: boolean;
  autor: string;

  constructor(partial: Partial<Post>) {
    Object.assign(this, partial);
  }
}
Enter fullscreen mode Exit fullscreen mode

Criando os serviços

Agora, vamos criar a nossa camada de regras de negócio e interação com a base de dados. No nosso arquivo posts.service.ts teremos um CRUD básico, com métodos para retornar criar, listar, atualizar e deletar posts do nosso blog.

import { Injectable, NotFoundException } from '@nestjs/common';
import { PrismaService } from 'src/prisma/prisma.service';
import { CreatePostDto } from './dto/create-post.dto';
import { UpdatePostDto } from './dto/update-post.dto';

@Injectable()
export class PostsService {
  constructor(private readonly prisma: PrismaService) {}

  create(createPostDto: CreatePostDto) {
    return this.prisma.post.create({ data: createPostDto });
  }

  findAll() {
    return this.prisma.post.findMany();
  }

  findOne(id: number) {
    return this.prisma.post.findUnique({ where: { id } });
  }

  async update(id: number, updatePostDto: UpdatePostDto) {
    try {
      return await this.prisma.post.update({
        where: { id },
        data: updatePostDto,
      });
    } catch (error) {
      throw new NotFoundException();
    }
  }

  async remove(id: number) {
    try {
      await this.prisma.post.delete({ where: { id } });
    } catch (error) {
      throw new NotFoundException();
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Repare que para interagir com a base de dados precisamos declarar o serviço do Prisma no construtor da classe.

Criando nossos testes com o Prisma

Agora, finalmente vamos começar os nossos testes. Começando pela camada de serviços, que onde mais devemos ter testes.
Primeiramente precisamos mockar os retornos dos métodos do Prisma. Para isso é necessário chamar a função jest.fn().mockReturnValue(). Podemos fazer da seguinte maneira:

// Criamos primeiro nos dados fícticios para serem retornados do Prisma
const fakePosts = [
  {
    id: 1,
    titulo: 'Primeiro post do blog',
    conteudo: 'Apenas um teste.',
    publicado: true,
    autor: 'João',
  },
  {
    id: 2,
    titulo: 'Testes unitários',
    conteudo: 'Conteúdo sobre testes unitários.',
    publicado: true,
    autor: 'Vitor',
  },
  {
    id: 3,
    titulo: 'Javascript',
    conteudo: 'Conteúdo sobre testes Javascript.',
    publicado: false,
    autor: 'Teste',
  },
];

// E depois nosso objeto de mock do Prisma, retornando os dados falsos
const prismaMock = {
  post: {
    create: jest.fn().mockReturnValue(fakePosts[0]),
    findMany: jest.fn().mockResolvedValue(fakePosts),
    findUnique: jest.fn().mockResolvedValue(fakePosts[0]),
    update: jest.fn().mockResolvedValue(fakePosts[0]),
    delete: jest.fn(), // O método delete não retorna nada
  },
};
Enter fullscreen mode Exit fullscreen mode

Depois de criar os mocks, vamos configurar o testing module do Nest.

describe('PostsService', () => {
  let service: PostsService;
  let prisma: PrismaService;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        PostsService,
        { provide: PrismaService, useValue: prismaMock },
      ],
    }).compile();

    service = module.get<PostsService>(PostsService);
    prisma = module.get<PrismaService>(PrismaService);
  });
});
Enter fullscreen mode Exit fullscreen mode

Primeiro declaramos a variável para o service do Prisma. Depois adicionamos um provider mockado do PrismaService e usavamos como valor o mock que criamos anteriormente. E por último inicializamos a variável do serviço e do Prisma.

Logo após a declaração do beforeEach vamos declarar o afterEach para limparmos os mocks apos a execução de cada teste.

  afterEach(() => {
    jest.clearAllMocks();
  });
Enter fullscreen mode Exit fullscreen mode

Agora vamos para o nosso primeiro cenário de testes que vai ser o em cima do método findAll

  describe('findAll', () => {
    it(`should return an array of posts`, async () => {
      const response = await service.findAll();

      expect(response).toEqual(fakePosts);
      expect(prisma.post.findMany).toHaveBeenCalledTimes(1);
      expect(prisma.post.findMany).toHaveBeenCalledWith(/* nothing */);
    });
  });
Enter fullscreen mode Exit fullscreen mode

Não temos nada de difícil aqui. Primeiro nós precisamos chamar nosso método do service e depois criar nossos expects para validar o que for necessário. Podemos validar um método do Prisma através do objeto que criamos no começo para ele (prisma.post.findMany) para esse caso.

Para o cenário de findOne teremos:

  describe('findOne', () => {
    it(`should return a single post`, async () => {
      const response = await service.findOne(1);

      expect(response).toEqual(fakePosts[0]);
      expect(prisma.post.findUnique).toHaveBeenCalledTimes(1);
      expect(prisma.post.findUnique).toHaveBeenCalledWith({
        where: { id: 1 },
      });
    });

    it(`should return nothing when post is not found`, async () => {
      jest.spyOn(prisma.post, 'findUnique').mockResolvedValue(undefined);

      const response = await service.findOne(99);

      expect(response).toBeUndefined();
      expect(prisma.post.findUnique).toHaveBeenCalledTimes(1);
      expect(prisma.post.findUnique).toHaveBeenCalledWith({
        where: { id: 99 },
      });
    });
  });
Enter fullscreen mode Exit fullscreen mode

Repare que para o segundo it usei a função spyOn do Jest. Precisamos usar ela para sobrepor o mock que criamos no começo, pois para esse caso estamos testando se o serviço retorna undefined quando não é encontrado nenhuma postagem com um determinado id. Então, com o spyOn podemos mockar que o retorno do método findUnique será undefined.

Para o resto dos métodos teremos os mesmos conceitos:

  describe('create', () => {
    it(`should create a new post`, async () => {
      const response = await service.create(fakePosts[0]);

      expect(response).toBe(fakePosts[0]);
      expect(prisma.post.create).toHaveBeenCalledTimes(1);
      expect(prisma.post.create).toHaveBeenCalledWith({
        data: fakePosts[0],
      });
    });
  });

  describe('updateOne', () => {
    it(`should update a post`, async () => {
      const response = await service.update(1, fakePosts[0]);

      expect(response).toEqual(fakePosts[0]);
      expect(prisma.post.update).toHaveBeenCalledTimes(1);
      expect(prisma.post.update).toHaveBeenCalledWith({
        where: { id: 1 },
        data: fakePosts[0],
      });
    });

    it(`should return NotFoundException when no post is found`, async () => {
      const unexistingPost = {
        id: 42,
        titulo: 'teste',
        conteudo: 'Conteudo Teste',
        publicado: false,
        autor: 'Teste',
      };

      jest.spyOn(prisma.post, 'update').mockRejectedValue(new Error());

      try {
        await service.update(42, unexistingPost);
      } catch (error) {
        expect(error).toEqual(new NotFoundException());
      }

      expect(prisma.post.update).toHaveBeenCalledWith({
        where: { id: 42 },
        data: unexistingPost,
      });
    });
  });

  describe('deleteOne', () => {
    it(`should delete post and return empty body`, async () => {
      expect(await service.remove(1)).toBeUndefined();
      expect(prisma.post.delete).toHaveBeenCalledTimes(1);
      expect(prisma.post.delete).toHaveBeenCalledWith({ where: { id: 1 } });
    });

    it(`should return NotFoundException if post does not exist`, async () => {
      jest.spyOn(prisma.post, 'delete').mockRejectedValue(new Error());

      try {
        await service.remove(99);
      } catch (error) {
        expect(error).toEqual(new NotFoundException());
      }

      expect(prisma.post.delete).toHaveBeenCalledTimes(1);
      expect(prisma.post.delete).toHaveBeenCalledWith({
        where: { id: 99 },
      });
    });
  });
Enter fullscreen mode Exit fullscreen mode

Conclusão

Espero que tenham gostado desse pequeno tutorial sobre testes com Nest e Prisma, não cobre tudo mas é um bom ponto de partida para criar os primeiros testes com essas ferramentas.
Não passei muito pelo controller e outras seções do modulo do Nest, mas podemos utilizar o controller que o Nest gera automaticamente com a CLI. Você pode conferir no meu repositório do GitHub como fazer os testes para o controller (é bem simples).

Link do repositório: https://github.com/mrtins/nest-blog-posts

Top comments (5)

Collapse
 
elmanoneto profile image
Elmano Neto • Edited

Muito top, @mrtinsvitor!

Comecei a estudar Nest com Prisma e o material foi bem util.

Valeu!

Collapse
 
suzanamiceli profile image
suzanaMiceli

Cara, muito obrigada!!!! Vc e ajudou infinitamente!!!!

Collapse
 
hamzalak profile image
LAKHAL Hamza

It sames that the article is interessant, can you provide an English version :D

Collapse
 
mrtinsvitor profile image
Vitor Martins

of course, I can work on that as soon as I can

Collapse
 
thgoas profile image
Thiago Andrade

pra min deu esse error: Matcher error: received value must be a mock or spy function

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay