Fazendo o stub de dependências com Sinon

Muitas vezes queremos testar funções que tem dependências externas, como por exemplo uma função que faz o request para uma API. Nesse post vou mostrar como podemos criar stubs dessas dependências para facilitar os nossos testes e até mesmo conseguir testar todos os tipos de comportamentos dessas dependências. O que são Stubs? Stubs em resumo
Fazendo o stub de dependências com Sinon

Muitas vezes queremos testar funções que tem dependências externas, como por exemplo uma função que faz o request para uma API.

Nesse post vou mostrar como podemos criar stubs dessas dependências para facilitar os nossos testes e até mesmo conseguir testar todos os tipos de comportamentos dessas dependências.

O que são Stubs?

Stubs em resumo são funções com um comportamento pré-programável que será fixo e previsível.

Nos nossos testes podemos utilizar stubs principalmente em dois casos:

  • Quando queremos evitar a chamada de alguma dependência, por exemplo uma chamada a uma API externa.
  • Quando queremos testar determinados caminhos específicos na nossa aplicação, e com isso conseguir testar os diversos cenários, por exemplo retornar null na chamada de determinada função.

O Sinon é um pacote open-source que fornece diversas funcionalidades (como mocks, spies e stubs) que facilitam os nossos testes no JavaScript.

O Sinon por sí só não é um framework de testes, ele deve ser utilizado em conjunto com outra ferramenta para execução de testes, como por exemplo o Mocha ou Ava.

A documentação do Sinon é bem completa com diversos exemplos práticos e pode ser encontrada no site oficial, lá também podemos encontrar o passo a passo para realizar a instalação do pacote no nosso projeto.

Exemplo Prático

Para esse exemplo, vamos tomar como base as seguintes funções:

// Apenas como exemplo, o arquivo contendo essas funções estaria no path: ./src/users.js

const got = require('got')

function getUsers () {
  return got.get('https://api.com.br/v1/users')
    .then(({body}) => body)
}

async function findUser (username) {
  const users = await getUsers()
  if (users.length === 0) return undefined

  return users.find(u => u.username === username)
}

module.exports = {
  getUsers,
  findUser
}

Agora vamos pensar nos testes apenas da função findUser, de cara podemos querer testar dois cenários:

  • A função getUsers que faz a requisição para a API retornar um array vazio
  • A função getUsers retornar um array de usuários

Perceba que para fazer esses testes acima na função findUser, precisamos obrigatoriamente mudar o comportamento da função getUsers e é aí que podemos utilizar todo o poder que os stubs nos fornecem:

// Apenas como exemplo, o arquivo contendo esses testes estaria no path: ./test/users.test.js
// Vamos assumir também que estamos utilizando Mocha + Chai para realizar os testes

const sandbox = require('sinon').createSandbox()
const users = require('../src/users')

describe('Test findUser', () => {
  afterEach(() => {
    sandbox.restore()
  })
  
  it('should return undefined if no users', async () => {
    // Na linha abaixo estou fazendo o stub da função getUsers, para simular o retorno 
    // de um array vazio e assim testar o comportamento desejado
    sandbox.stub(users, 'getUsers').returns([])
    
    const user = await users.findUser('Pedro')
 
    sandbox.assert.calledOnce(users.getUsers)
    assert(user).to.be.undefined
  })
  
  it('shuld return user by username', async () => {
    // Na linha abaixo estou fazendo o stub da função getUsers, para simular o retorno 
    // de um array contendo usuários e assim testar o comportamento desejado
    sandbox.stub(users, 'getUsers').returns([{ username: 'Maria' }, { username: 'Pedro' }])
    
    const user = await users.findUser('Pedro')
    
    sandbox.assert.calledOnce(users.getUsers)
    assert(user).to.deep.equal({username: 'Pedro'});
  })
})

Reparem que em ambos os testes precisávamos mudar o retorno da função getUsers para que fosse possível testar a função findUser e foi nesse momento que utilizamos o stub do Sinon para mudar o retorno esperado.

Observações importantes:

  • Após cada teste estamos chamando a função restore() no afterEach, isso é necessário para restaurar o comportamento padrão das funções.
  • Nesse exemplo estou utilizando o sandbox que o pacote Sinon fornece. Eu particularmente prefiro sempre utilizar o sandboxes ao invés do stub direto, pois dessa forma facilitamos o cleanup depois dos testes.

Conclusão

Esse post foi para mostrar o básico de como podemos realizar o stub de funções através do pacote Sinon, e assim facilitar a nossa vida na hora dos testes.

Esse pacote fornece diversas outras funções bem utéis e por isso recomendo fortemente que para quem achou interessante que acesse todos os exemplos práticos que estão disponíveis na documentação oficial.

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *