No post anterior, nós fizemos todas as implementações que tínhamos da semana 1, deixando nossa API de vídeos pronta para consumo.
Só que ainda temos desafios pela frente e nessa semana faremos a inserção de categorias, para classificarmos os nossos vídeos.
A história dessa semana é a seguinte:
Depois de alguns testes com usuários, foi definido que a próxima feature a ser desenvolvida nesse projeto é a divisão dos vídeos por categoria, para melhorar a experiência de organização da lista de vídeos pelo usuário.
Então, faremos nesse post as seguintes implementações :
- Adicionar
categoriase seus campos na base de dados; - Rotas CRUD para
/categorias; - Incluir campo
categoriaIdno modelovideo; - Escrever os testes unitários.
Vamos começar fazendo os dois primeiros pontos, e novamente usaremos o generate do Nest, para criamos o recurso Categorias.
nest generate resource categorias Após a criação, faremos a definição da nossa classe CreateCategoriaDto:
// src/categorias/dto/create-categoria.dto.ts import { Video } from '../../videos/entities/video.entity'; export class CreateCategoriaDto { id: number; titulo: string; cor: string; videos: Video[]; } e alteraremos o nosso CreateVideoDto, adicionando a categoria:
// src/videos/dto/create-video.dto.ts import { Categoria } from '../../categorias/entities/categoria.entity'; export class CreateVideoDto { ... categoria: Categoria; } Agora vamos na nossa "entity" e precisaremos fazer um pouco diferente.
Como será necessário ligar as tabelas de videos e categorias, utilizaremos o decorator @OneToMany(), que significa que teremos 1 categoria para vários vídeos:
// src/categorias/entities/categoria.entity.ts import { IsNotEmpty, IsString } from 'class-validator'; import { Video } from '../../videos/entities/video.entity'; import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from 'typeorm'; @Entity() export class Categoria { @PrimaryGeneratedColumn() id: number; @IsNotEmpty() @IsString() @Column() titulo: string; @IsNotEmpty() @IsString() @Column() cor: string; @OneToMany(() => Video, (video) => video.categoria) videos: Video[]; } Será necessário alterar nossa "entity" Video também, informando que teremos varios vídeos para cada categoria. Utilizaremos o decorator @ManyToOne() após todos os atributos que já temos:
// src/videos/entities/video.entity.ts import { PrimaryGeneratedColumn, Column, Entity, ManyToOne } from 'typeorm'; import { IsNotEmpty, IsString, IsUrl } from 'class-validator'; import { Categoria } from '../../categorias/entities/categoria.entity'; @Entity() export class Video { ... @ManyToOne(() => Categoria, (categoria) => categoria.id, { nullable: false }) categoria: Categoria; } Entidades ajustadas, vamos para o nosso "categoria.service" para implementar as funcionalidades dela.
A diferença nela está no findAll, que passamos o parâmetro indicando a relação (relations: ['videos']) e já pedindo para trazer os dados dessa relação (loadEagerRelations: true):
// src/categorias/categorias.service.ts import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { CreateCategoriaDto } from './dto/create-categoria.dto'; import { UpdateCategoriaDto } from './dto/update-categoria.dto'; import { Categoria } from './entities/categoria.entity'; @Injectable() export class CategoriasService { @InjectRepository(Categoria) private categoriaRepository: Repository<Categoria>; create(createCategoriaDto: CreateCategoriaDto) { return this.categoriaRepository.save(createCategoriaDto); } findAll() { return this.categoriaRepository.find({ relations: ['videos'], loadEagerRelations: true, }); } findOne(id: number) { return this.categoriaRepository.findOne(id); } update(id: number, updateCategoriaDto: UpdateCategoriaDto) { return this.categoriaRepository.update(id, updateCategoriaDto); } async remove(id: number) { const categoria = await this.findOne(id); return this.categoriaRepository.remove(categoria); } } Novamente, temos que ajustar nosso "video.service" também, que no caso, só terá alteração no método "findAll()":
// src/videos/videos.service.ts ... findAll() { return this.videoRepository.find({ relations: ['categoria'], loadEagerRelations: true, }); } ... Agora, para que a tabela de categorias seja criada no banco e identificada pelo Nest, precisamos ir no categorias.module e importar o TypeOrmModule passando a Categoria como parâmetro:
// src/categorias/categorias.module.ts import { Module } from '@nestjs/common'; import { CategoriasService } from './categorias.service'; import { CategoriasController } from './categorias.controller'; import { Categoria } from './entities/categoria.entity'; import { TypeOrmModule } from '@nestjs/typeorm'; @Module({ imports: [TypeOrmModule.forFeature([Categoria])], controllers: [CategoriasController], providers: [CategoriasService], }) export class CategoriasModule {} Maravilha, implementamos a categoria para os nossos videos, porém, temos um card que pede para que ao acessar a rota "/categorias/:id/videos" retorne os vídeos de uma determinada categoria:

Para que isso seja possível, vamos precisar de uma nova rota no nosso controller de categorias.
A nova rota vai ficar assim:
// src/categorias/categorias.controller.ts ... @Get(':id/videos') findVideosByCategoryId(@Param('id') id: string) { return this.categoriasService.findVideoByCategory(+id); } ... Mas espera aí, nós não temos esse método "categoriasService.findVideoByCategory()".
Precisamos criar esse método lá no nosso serviço "categorias.service":
// src/categorias/categorias.service.ts ... async findVideoByCategory(id: number): Promise<Video[]> { const categoria = await this.findOne(id); return categoria.videos; } ... Mas se tentarmos acessar a rota, veremos uma tela branca, sem retorno de nenhum dado. --'
Para que seja retornado, precisamos alterar também o nosso método findOne, passando um objeto de configuração, informando que queremos que ele faça o carregamento dos dados relacionados à essa tabela.
O método ficará assim:
// src/categorias/categorias.service.ts ... findOne(id: number) { return this.categoriaRepository.findOne(id, { relations: ['videos'], loadEagerRelations: true, }); } ... Agora sim, ao acessar nossa rota ".../categorias/1/videos", teremos nossos vídeos referentes à essa categoria.
O próximo card, tem a seguinte descrição:

Para atender a esse requisito, precisaremos alterar nosso controller e nosso service de videos:
// src/videos/videos.controller.ts ... @Get() findAll(@Query() query) { return this.videosService.findAll(query.search); } ... // src/videos/videos.service.ts ... findAll(search = '') { return this.videoRepository.find({ where: { titulo: ILike(`%${search}%`) }, relations: ['categoria'], }); } ... Como já criamos uma categoria anteriormente, vamos enviar uma requisição de update para a categoria de ID 1, alterando o título para LIVRE

E após isso, vamos no videos.service e alteraremos a lógica do método create:
// src/videos/videos.service.ts ... create(createVideoDto: CreateVideoDto) { if (!createVideoDto.categoria) return this.videoRepository.save({ ...createVideoDto, categoria: { id: 1 }, }); return this.videoRepository.save(createVideoDto); } ... Pronto, requisito atendido!
(Faça uma requisição criando um vídeo sem informar a categoria e veja se está tudo funcionando como o esperado, o retorno deve ser o vídeo criado e com a "categoria":{"id": 1})
O próximo requisito é para que se crie os testes automatizados e aí que o negócio começa a ficar legal.
Implementando testes automatizados
Para facilitar nossos mocks, vamos inserir um construtor para as nossas entidades, deixando elas assim:
// src/categorias/entities/categoria.entity.ts ... constructor(private categoria?: Partial<Categoria>) {} // src/categorias/entities/categoria.entity.ts ... constructor(private video?: Partial<Categoria>) {} Vamos criar também uma pasta common dentro de src e nela criar uma pasta test, que ficarão os arquivos necessário e comum à todos os testes. Por agora, teremos dois stubs:
videos.stub.ts (crie esse arquivo)
// src/common/test/videos.stub.ts import { Categoria } from '../../categorias/entities/categoria.entity'; import { Video } from '../../videos/entities/video.entity'; import { categoriasStub } from './categorias.stub'; export const videosStub: Video[] = [ new Video({ id: 1, titulo: 'título qualquer', descricao: 'descrição qualquer', url: 'http://url_qualquer.com', categoria: new Categoria({ id: 1, titulo: 'LIVRE', cor: 'verde' }), }), new Video({ id: 2, titulo: 'outro título qualquer', descricao: 'outra descrição qualquer', url: 'http://outra_url_qualquer.com', categoria: categoriasStub[1], }), new Video({ id: 3, titulo: 'titulo qualquer', descricao: 'descrição qualquer', url: 'http://url_qualquer.com', categoria: categoriasStub[1], }), ]; e categorias.stub.ts (crie também)
// src/common/test/categorias.stub.ts import { Categoria } from '../../categorias/entities/categoria.entity'; export const categoriasStub: Categoria[] = [ new Categoria({ id: 1, titulo: 'LIVRE', cor: 'verde' }), new Categoria({ id: 2, titulo: 'Programação', cor: 'azul' }), ]; Testando os controllers
Vamos rodas os testes e ver no que vai dar.
npm run test Que delícia, nenhum teste passou!
Isso acontece pois alteramos e implementamos tudo sem criar nenhum teste o que não é muito legal, mas enfim, vamos começar a ajustar as coisa...
Testando nosso categorias.controller
O Nestjs já deixa uma estrutura pré-montada, então, vamos até o nosso arquivo categorias.controller.spec.ts para trabalhar nele.
Nosso controller tem como dependência o service e para que ele seja disponibilizado no teste precisamos prover ele.
Faremos dessa forma:
// src/categorias/categorias.controller.spec.ts import { Test, TestingModule } from '@nestjs/testing'; import { categoriasStub } from '../common/test/categorias.stub'; import { CategoriasController } from './categorias.controller'; import { CategoriasService } from './categorias.service'; import { videosStub } from '../common/test/videos.stub'; describe('CategoriasController', () => { let controller: CategoriasController; let service: CategoriasService; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ controllers: [CategoriasController], providers: [ CategoriasService, { provide: CategoriasService, useValue: { create: jest.fn().mockResolvedValue(categoriasStub[0]), findAll: jest.fn().mockResolvedValue(categoriasStub), findOne: jest.fn().mockResolvedValue(categoriasStub[0]), findVideoByCategory: jest.fn().mockResolvedValue(videosStub), update: jest.fn().mockResolvedValue(categoriasStub[0]), remove: jest.fn().mockResolvedValue(categoriasStub[0]), }, }, ], }).compile(); controller = module.get<CategoriasController>(CategoriasController); service = module.get<CategoriasService>(CategoriasService); }); it('should be defined', () => { expect(controller).toBeDefined(); expect(service).toBeDefined(); }); }); repare que nós criamos um mock para cada função que temos no serviço e definimos o retorno delas com nosso stub da categoria.
com isso, podemos rodar novamente o comando de testes do npm, só que agora, deixaremos ele em modo watch para que ele fique observando as alterações, enquanto implementamos os testes, mas faremos somente para o arquivo que estamos trabalhando no momento:
npm run test:watch -t /home/gabriel/Documentos/alura-challenges-2/src/categorias/categorias.controller.spec.ts # substitua essa parte '/home/gabriel/Documentos' pelo caminho do seu computador, é claro. Feito isso, o teste vai rodar e faremos a implementação do restante.
No final, esse arquivo ficará assim:
// src/categorias/categorias.controller.spec.ts import { Test, TestingModule } from '@nestjs/testing'; import { categoriasStub } from '../common/test/categorias.stub'; import { CategoriasController } from './categorias.controller'; import { CategoriasService } from './categorias.service'; import { videosStub } from '../common/test/videos.stub'; describe('CategoriasController', () => { let controller: CategoriasController; let service: CategoriasService; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ controllers: [CategoriasController], providers: [ CategoriasService, { provide: CategoriasService, useValue: { create: jest.fn().mockResolvedValue(categoriasStub[0]), findAll: jest.fn().mockResolvedValue(categoriasStub), findOne: jest.fn().mockResolvedValue(categoriasStub[0]), findVideoByCategory: jest.fn().mockResolvedValue(videosStub), update: jest.fn().mockResolvedValue(categoriasStub[0]), remove: jest.fn().mockResolvedValue(categoriasStub[0]), }, }, ], }).compile(); controller = module.get<CategoriasController>(CategoriasController); service = module.get<CategoriasService>(CategoriasService); }); it('should be defined', () => { expect(controller).toBeDefined(); expect(service).toBeDefined(); }); describe('create', () => { it('should create a category', async () => { const result = await service.create(categoriasStub[0]); expect(result).toEqual(categoriasStub[0]); expect(service.create).toHaveBeenCalledTimes(1); }); it('should throw an exception', () => { jest.spyOn(service, 'create').mockRejectedValueOnce(new Error()); expect(service.create(categoriasStub[0])).rejects.toThrowError(); expect(service.create).toHaveBeenCalledTimes(1); }); }); describe('findAll', () => { it('should return a category list', async () => { const result = await service.findAll(); expect(result).toEqual(categoriasStub); expect(service.findAll).toHaveBeenCalledTimes(1); }); it('should throw an exception', () => { jest.spyOn(service, 'findAll').mockRejectedValueOnce(new Error()); expect(service.findAll()).rejects.toThrowError(); expect(service.findAll).toHaveBeenCalledTimes(1); }); }); describe('findOne', () => { it('should return a category', async () => { const result = await service.findOne(categoriasStub[0].id); expect(result).toEqual(categoriasStub[0]); expect(service.findOne).toHaveBeenCalledTimes(1); }); it('should throw an exception', () => { jest.spyOn(service, 'findOne').mockRejectedValueOnce(new Error()); expect(service.findOne(1)).rejects.toThrowError(); expect(service.findOne).toHaveBeenCalledTimes(1); }); }); describe('findVideosByCategoryId', () => { it('should return videos from a category', async () => { const result = await service.findVideoByCategory(categoriasStub[0].id); expect(result).toEqual(videosStub); expect(service.findVideoByCategory).toHaveBeenCalledTimes(1); }); it('should throw an exception', () => { jest .spyOn(service, 'findVideoByCategory') .mockRejectedValueOnce(new Error()); expect(service.findVideoByCategory(1)).rejects.toThrowError(); expect(service.findVideoByCategory).toHaveBeenCalledTimes(1); }); }); describe('update', () => { it('should return a updated category', async () => { const result = await service.update( categoriasStub[0].id, categoriasStub[0], ); expect(result).toEqual(categoriasStub[0]); expect(service.update).toHaveBeenCalledTimes(1); }); it('should throw an exception', () => { jest.spyOn(service, 'update').mockRejectedValueOnce(new Error()); expect(service.update(1, categoriasStub[0])).rejects.toThrowError(); expect(service.update).toHaveBeenCalledTimes(1); }); }); describe('remove', () => { it('should return a removed category', async () => { const result = await service.remove(categoriasStub[0].id); expect(result).toEqual(categoriasStub[0]); expect(service.remove).toHaveBeenCalledTimes(1); }); it('should throw an exception', () => { jest.spyOn(service, 'remove').mockRejectedValueOnce(new Error()); expect(service.remove(1)).rejects.toThrowError(); expect(service.remove).toHaveBeenCalledTimes(1); }); }); }); Passou todos os testes? vamos para o próximo!
Testando nosso videos.controller
npm run test:watch -t /home/gabriel/Documentos/alura-challenges-2/src/videos/videos.controller.spec.ts # substitua essa parte '/home/gabriel/Documentos' pelo caminho do seu computador, é claro. A ideia é a mesma para cá, então, esse arquivo fica assim:
// src/videos/videos.controller.spec.ts import { Test, TestingModule } from '@nestjs/testing'; import { VideosController } from './videos.controller'; import { VideosService } from './videos.service'; import { videosStub } from '../common/test/videos.stub'; describe('VideosController', () => { let controller: VideosController; let service: VideosService; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ controllers: [VideosController], providers: [ VideosService, { provide: VideosService, useValue: { create: jest.fn().mockResolvedValue(videosStub[0]), findAll: jest.fn().mockResolvedValue(videosStub), findOne: jest.fn().mockResolvedValue(videosStub[0]), update: jest.fn().mockResolvedValue(videosStub[0]), remove: jest.fn().mockResolvedValue(videosStub[0]), }, }, ], }).compile(); controller = module.get<VideosController>(VideosController); service = module.get<VideosService>(VideosService); }); it('should be defined', () => { expect(controller).toBeDefined(); expect(service).toBeDefined(); }); describe('create', () => { it('should create a video', async () => { const result = await service.create(videosStub[0]); expect(result).toEqual(videosStub[0]); expect(service.create).toHaveBeenCalledTimes(1); }); it('should throw an exception', () => { jest.spyOn(service, 'create').mockRejectedValueOnce(new Error()); expect(service.create(videosStub[0])).rejects.toThrowError(); expect(service.create).toHaveBeenCalledTimes(1); }); }); describe('findAll', () => { it('should return a video list', async () => { const result = await service.findAll(); expect(result).toEqual(videosStub); expect(service.findAll).toHaveBeenCalledTimes(1); }); it('should throw an exception', () => { jest.spyOn(service, 'findAll').mockRejectedValueOnce(new Error()); expect(service.findAll()).rejects.toThrowError(); expect(service.findAll).toHaveBeenCalledTimes(1); }); }); describe('findOne', () => { it('should return a video', async () => { const result = await service.findOne(videosStub[0].id); expect(result).toEqual(videosStub[0]); expect(service.findOne).toHaveBeenCalledTimes(1); }); it('should throw an exception', () => { jest.spyOn(service, 'findOne').mockRejectedValueOnce(new Error()); expect(service.findOne(1)).rejects.toThrowError(); expect(service.findOne).toHaveBeenCalledTimes(1); }); }); describe('update', () => { it('should return a updated video', async () => { const result = await service.update(videosStub[0].id, videosStub[0]); expect(result).toEqual(videosStub[0]); expect(service.update).toHaveBeenCalledTimes(1); }); it('should throw an exception', () => { jest.spyOn(service, 'update').mockRejectedValueOnce(new Error()); expect(service.update(1, videosStub[0])).rejects.toThrowError(); expect(service.update).toHaveBeenCalledTimes(1); }); }); describe('remove', () => { it('should return a removed video', async () => { const result = await service.remove(videosStub[0].id); expect(result).toEqual(videosStub[0]); expect(service.remove).toHaveBeenCalledTimes(1); }); it('should throw an exception', () => { jest.spyOn(service, 'remove').mockRejectedValueOnce(new Error()); expect(service.remove(1)).rejects.toThrowError(); expect(service.remove).toHaveBeenCalledTimes(1); }); }); }); Testes dos controllers ok, vamos para os services.
Testando os services
Sem muitas mudanças para cá também, faremos o mesmo que nos controllers, exceto pelo fato de que a dependência deixa de ser o serviço (óbvio, pois estamos no serviço rs) e passa a ser o nosso repository.
Trecho para prestar atenção:
// src/categorias/categorias.service.spec.ts ... beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ CategoriasService, { provide: getRepositoryToken(Categoria), useValue: { save: jest.fn().mockResolvedValue(categoriasStub[0]), find: jest.fn().mockResolvedValue(categoriasStub), findOne: jest.fn().mockResolvedValue(categoriasStub[0]), update: jest.fn().mockResolvedValue(categoriasStub[0]), remove: jest.fn().mockResolvedValue(categoriasStub[0]), }, }, ], }).compile(); service = module.get<CategoriasService>(CategoriasService); repository = module.get(getRepositoryToken(Categoria)); }); ... Dada a devida atenção para o que muda dos controllers, vamos para as implementações dos services.
Testando nosso categorias.service
npm run test:watch -t /home/gabriel/Documentos/alura-challenges-2/src/categorias/categorias.service.spec.ts # substitua essa parte '/home/gabriel/Documentos' pelo caminho do seu computador, é claro. O código para testar nosso service de categorias ficará assim:
// src/categorias/categorias.service.spec.ts import { Test, TestingModule } from '@nestjs/testing'; import { CategoriasService } from './categorias.service'; import { getRepositoryToken } from '@nestjs/typeorm'; import { Categoria } from './entities/categoria.entity'; import { categoriasStub } from '../common/test/categorias.stub'; import { Repository } from 'typeorm'; describe('CategoriasService', () => { let service: CategoriasService; let repository: Repository<Categoria>; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ CategoriasService, { provide: getRepositoryToken(Categoria), useValue: { save: jest.fn().mockResolvedValue(categoriasStub[0]), find: jest.fn().mockResolvedValue(categoriasStub), findOne: jest.fn().mockResolvedValue(categoriasStub[0]), update: jest.fn().mockResolvedValue(categoriasStub[0]), remove: jest.fn().mockResolvedValue(categoriasStub[0]), }, }, ], }).compile(); service = module.get<CategoriasService>(CategoriasService); repository = module.get(getRepositoryToken(Categoria)); }); it('should be defined', () => { expect(service).toBeDefined(); expect(repository).toBeDefined(); }); describe('save', () => { it('should create a category', async () => { const newCategory: Omit<Categoria, 'id'> = categoriasStub[0]; const result = await service.create(newCategory); expect(result).toEqual(categoriasStub[0]); expect(repository.save).toHaveBeenCalledTimes(1); }); it('should throw an exception', () => { jest.spyOn(repository, 'save').mockRejectedValueOnce(new Error()); expect(service.create(categoriasStub[0])).rejects.toThrowError(); expect(repository.save).toHaveBeenCalledTimes(1); }); }); describe('findAll', () => { it('should return a categories list', async () => { const result = await service.findAll(); expect(result).toEqual(categoriasStub); expect(repository.find).toHaveBeenCalledTimes(1); }); it('should throw an exception', () => { jest.spyOn(repository, 'find').mockRejectedValueOnce(new Error()); expect(service.findAll()).rejects.toThrowError(); expect(repository.find).toHaveBeenCalledTimes(1); }); }); describe('findOne', () => { it('should return a category', async () => { const result = await service.findOne(categoriasStub[0].id); expect(result).toEqual(categoriasStub[0]); expect(repository.findOne).toHaveBeenCalledTimes(1); }); it('should throw an exception', () => { jest.spyOn(repository, 'findOne').mockRejectedValueOnce(new Error()); expect(service.findOne(1)).rejects.toThrowError(); expect(repository.findOne).toHaveBeenCalledTimes(1); }); }); describe('update', () => { it('should return a updated category', async () => { const result = await service.update( categoriasStub[0].id, categoriasStub[0], ); expect(result).toEqual(categoriasStub[0]); expect(repository.update).toHaveBeenCalledTimes(1); }); it('should throw an exception', () => { jest.spyOn(repository, 'update').mockRejectedValueOnce(new Error()); expect(service.update(1, categoriasStub[0])).rejects.toThrowError(); expect(repository.update).toHaveBeenCalledTimes(1); }); }); describe('remove', () => { it('should return a removed category', async () => { const result = await service.remove(categoriasStub[0].id); expect(result).toEqual(categoriasStub[0]); expect(repository.remove).toHaveBeenCalledTimes(1); expect(repository.findOne).toHaveBeenCalledTimes(1); }); it('should throw an exception', () => { jest.spyOn(repository, 'remove').mockRejectedValueOnce(new Error()); expect(service.remove(1)).rejects.toThrowError(); expect(repository.findOne).toHaveBeenCalledTimes(1); }); }); }); Testando nosso videos.service
E para o nossos testes do service de videos:
npm run test:watch -t /home/gabriel/Documentos/alura-challenges-2/src/videos/videos.service.spec.ts # substitua essa parte '/home/gabriel/Documentos' pelo caminho do seu computador, é claro. // src/videos/videos.service.spec.ts import { Test, TestingModule } from '@nestjs/testing'; import { getRepositoryToken } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { videosStub } from '../common/test/videos.stub'; import { Video } from './entities/video.entity'; import { VideosService } from './videos.service'; describe('VideosService', () => { let service: VideosService; let repository: Repository<Video>; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ VideosService, { provide: getRepositoryToken(Video), useValue: { save: jest.fn().mockResolvedValue(videosStub[0]), find: jest.fn().mockResolvedValue(videosStub), findOne: jest.fn().mockResolvedValue(videosStub[0]), update: jest.fn().mockResolvedValue(videosStub[0]), remove: jest.fn().mockResolvedValue(videosStub[0]), }, }, ], }).compile(); service = module.get<VideosService>(VideosService); repository = module.get(getRepositoryToken(Video)); }); it('should be defined', () => { expect(service).toBeDefined(); expect(repository).toBeDefined(); }); describe('save', () => { const newVideo: Omit<Video, 'id'> = videosStub[0]; it('should create a video', async () => { const result = await service.create(newVideo); expect(result).toEqual(videosStub[0]); expect(repository.save).toHaveBeenCalledTimes(1); }); it('should throw an exception', () => { jest.spyOn(repository, 'save').mockRejectedValueOnce(new Error()); expect(service.create(videosStub[0])).rejects.toThrowError(); expect(repository.save).toHaveBeenCalledTimes(1); }); }); describe('findAll', () => { it('should return a videos list if search is not informed', async () => { const result = await service.findAll(); expect(result).toEqual(videosStub); expect(repository.find).toHaveBeenCalledTimes(1); }); it('should return a videos list if search is informed', async () => { //Arrange const expectedResult = videosStub.filter((video) => video.titulo?.includes('teste'), ); jest.spyOn(repository, 'find').mockResolvedValue(expectedResult); //Act const result = await service.findAll('teste'); //Assert expect(result).toEqual([]); expect(repository.find).toHaveBeenCalledTimes(1); }); it('should throw an exception', () => { jest.spyOn(repository, 'find').mockRejectedValueOnce(new Error()); expect(service.findAll()).rejects.toThrowError(); expect(repository.find).toHaveBeenCalledTimes(1); }); }); describe('findOne', () => { it('should return a video', async () => { const result = await service.findOne(videosStub[0].id); expect(result).toEqual(videosStub[0]); expect(repository.findOne).toHaveBeenCalledTimes(1); }); it('should throw an exception', () => { jest.spyOn(repository, 'findOne').mockRejectedValueOnce(new Error()); expect(service.findOne(1)).rejects.toThrowError(); expect(repository.findOne).toHaveBeenCalledTimes(1); }); }); describe('update', () => { it('should return a updated video', async () => { const result = await service.update(videosStub[0].id, videosStub[0]); expect(result).toEqual(videosStub[0]); expect(repository.update).toHaveBeenCalledTimes(1); }); it('should throw an exception', () => { jest.spyOn(repository, 'update').mockRejectedValueOnce(new Error()); expect(service.update(1, videosStub[0])).rejects.toThrowError(); expect(repository.update).toHaveBeenCalledTimes(1); }); }); describe('remove', () => { it('should return a removed video', async () => { const result = await service.remove(videosStub[0].id); expect(result).toEqual(videosStub[0]); expect(repository.remove).toHaveBeenCalledTimes(1); expect(repository.findOne).toHaveBeenCalledTimes(1); }); it('should throw an exception', () => { jest.spyOn(repository, 'remove').mockRejectedValueOnce(new Error()); expect(service.remove(1)).rejects.toThrowError(); expect(repository.findOne).toHaveBeenCalledTimes(1); }); }); }); E com isso, fechamos as implementações da segunda semana.
Aaaah, estou fazendo meus commits conforme vou implementando as coisas... (Padronizadinho, conforme configuramos no início)
Está lá no meu Github.
Abraços e até a próxima semana!

Top comments (0)