Objetivo
Se você está cansado de mockar classes concretas, criar classes ou funções para usar de mock, essas duas funções abaixo vão te ajudar de uma maneira bem inteligente e prática.
Situação
Precisamos testar um serviço que vai nos retornar uma lista de usuários.
Fluxo do sistema
Testes
Muitas das vezes, criamos algum tipo de módulo/objeto para mockar dependências que seguem um "contrato" que são injetadas em um tipo de serviço. Quando usamos o princípio de Interface Segregation podemos mockar diretamente a INTERFACE usando duas funcionalidades do pacote jest-mock-extended sem se preocupar com as dependências externas (ex: Repositórios concretos)
Exemplo
Aqui temos uma interface que um repositório deve seguir.
import { UserDTO } from "../dtos/user";
export interface GetUsers {
allUsers(): Promise<UserDTO[]>
}
E no nosso serviço vamos ter uma injeção de dependência seguindo essa interface (sem dizer quem é a classe concreta que representa isso, Inversão de Dependência).
import { UserDTO } from "../dtos/user";
import { ServiceProtocol } from "../protocols/service";
import { GetUsers } from "../repos/get-users";
export default class GetAllUsersService implements ServiceProtocol<null, Promise<UserDTO[]>> {
constructor (private readonly usersRepository: GetUsers) {}
async perform(): Promise<UserDTO[]> {
const all = await this.usersRepository.allUsers()
return all
}
}
Agora nos testes, a mágica =)
Repare que na variável usersRepository estamos utilizando o MockProxy na interface e não na classe concreta e entre os testes estamos falando para que a mesma seja mockada.
Com isso, não se preocupamos com classe concreta de repositório, apenas com a camada do serviço que precisa ser testada.
import GetAllUsersService from "@/domain/services/get-all-users-service"
import { mock, MockProxy } from 'jest-mock-extended'
import { GetUsers } from "./repos/get-users"
describe('GetAllUsersService', () => {
let sut: GetAllUsersService
let usersRepository: MockProxy<GetUsers>
beforeAll(() => {
usersRepository = mock()
})
beforeEach(() => {
sut = new GetAllUsersService(usersRepository)
usersRepository.allUsers.mockResolvedValue([
{
name: 'user 1',
email: 'user 1',
age: 'user 1',
departament: 'user 1',
created_at: new Date(),
inative: false
},
])
})
it('should call allUsers one time', async () => {
await sut.perform()
expect(usersRepository.allUsers).toHaveBeenCalledTimes(1)
})
})
Valeu!
CÓDIGO-FONTE: https://github.com/Gabriel-Valin/clean-arch-express-based
Top comments (5)
Gabriel, posso estar errado, mas acho que talvez colocar o mock no beforeEach() irá ser o mock para todos os testes.
Acho que a declaração do mockResolvedValue() faria mais sentido dentro do próprio teste em si.
Imagina a situação que vc queira testar
"Should return an empty array...."
Mas parabéns pelo POST :).
o mock no beforeEach eh usado para testar o metodo quando ele obtem sucesso, pode ser colocado no beforeAll tambem, ate melhor.
no caso para o teste de falha, nos colocariamos o mockReturnValueOnce, que sobrescreve o retorno do metodo inicialmente!
nesse teste ficaria method.mockReturnValueOnce([]) e continuamos com o teste =)))
Valeu !!
Ah entendi, é que tenho usado mais o sinon para implementar meus testes.
No sinon se você tentar gerar um stub/mock de um módulo no forEach, e tentar realizar esse mesmo stub/mock, dentro de um teste, dará problemas.
Mas como você está utlizando o jest isso passa a ser possível.
Entendi o lance do mock porém como sou iniciante com testes... O que de fato está sendo testado? É a questão de saber se a service está recebendo a dependencia injetada e chamando o metodo correto?
Aqui a gente tem um teste simples, que testamos quantas vezes o método foi chamado. Garantimos a quantidade de execuções.
Mas com esse setup, podemos testar qualquer coisa... Forjando resultados, Rejeitando promises, etc...
A ideia é mais para que o setup seja construído rapidamente sem preocupações com a implantação de classes concretas (sendo spies, stubs ou de produção)