DEV Community

Cover image for Mocking Interface with jest-mock-extended
Gabriel Valin
Gabriel Valin

Posted on

Mocking Interface with jest-mock-extended

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

flowsystem

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[]>
}

Enter fullscreen mode Exit fullscreen mode

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
    }
}
Enter fullscreen mode Exit fullscreen mode

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)
    })
})
Enter fullscreen mode Exit fullscreen mode

Valeu!

Referências: Manguinho

CÓDIGO-FONTE: https://github.com/Gabriel-Valin/clean-arch-express-based

Top comments (5)

Collapse
 
joaocrulhas profile image
João Pedro Rubira Crulhas

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 :).

Collapse
 
gvt3ch profile image
Gabriel Valin • Edited

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 !!

Collapse
 
joaocrulhas profile image
João Pedro Rubira Crulhas

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.

Collapse
 
rodolphonetto profile image
Rodolpho Netto

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?

Collapse
 
gvt3ch profile image
Gabriel Valin

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)