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);
}
}
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();
}
}
}
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
},
};
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);
});
});
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();
});
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 */);
});
});
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 },
});
});
});
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 },
});
});
});
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)
Muito top, @mrtinsvitor!
Comecei a estudar Nest com Prisma e o material foi bem util.
Valeu!
Cara, muito obrigada!!!! Vc e ajudou infinitamente!!!!
It sames that the article is interessant, can you provide an English version :D
of course, I can work on that as soon as I can
pra min deu esse error: Matcher error: received value must be a mock or spy function