MSW - Mock Service Worker - Escopo
Tags: javascript, react, node, testing
Published: 2022-09-20 13:17:01

No [artigo anterior](https://dev.to/deesouza/mocking-com-o-msw-mock-service-worker-16n3) vimos um exemplo de como podemos usar o MSW para interceptar uma requisição HTTP e retornar informações fictícias para usar em testes unitários. Quando fazemos isso é importante termos em mente que a requisição real não é executada, pois ela é interceptada pelo `msw`.

Neste artigo, vamos ver um exemplo de como deixar as funções do `msw` em um escopo maior para que os testes - `it` - levem em conta o `mock`. Assim, evitamos de a cada teste - `it` - inserir o código novamente, isto é, o `DRY` - **Don't Repeat Yourself**.

### Show me the code

Imaginem o seguinte cenário: precisamos criar 2 testes para verificarmos se os dados que estão vindo da API estão corretos e o que fazemos com estes dados estão de acordo com a regra de negócio que definimos. Então, concluímos que a resposta da API deve ser sempre de sucesso, ou seja, HTTP Status Code 2xx - 200, 201, 204...

```js
// getUserInfoGithub.js
import axios from 'axios';

export const getUserInfoGithub = async ({ username }) => {
  const { data } = await axios.get(
     `https://api.github.com/users/${username}`
  );

   return {
      ...data,
      hasTwitter: Boolean(data.twitter_username),
      hasBlog: Boolean(data.blog),
   };
};
```

```js
const { setupServer } = require('msw/node');
const { rest } = require('msw');

const getUserInfoGithub = require('./getUserInfoGithub');

const endpoint = "https://api.github.com/users/:username";

const createServer = () => 
   setupServer(
      rest.get(endpoint, (request, response, ctx) =>
         response(
            ctx.status(200),
            ctx.json({
               id: 12345678,
               twitter_username: '@TwitterUser',
               blog: 'https://gist.github.com/username',
            })
         )
      )
   );

beforeAll(() => createServer.listen());
afterEach(() => createServer.resetHandlers());
afterAll(() => createServer.close());

describe('Testing User Service', () => {
   it('should return user info twitter username', async () => {
      const response = await getUserInfoGithub('MockUser');

      expect(response.hasTwitter).toBeTruthy();
   });

   it('should return user info link blog', async () => {
      const response = await getUserInfoGithub('MockUser');

      expect(response.hasBlog).toBeTruthy();
   });
});
```

Sobre o código acima, vamos as explicações:

A diferença desse código para o código do artigo anterior é o use do `beforeAll` do Jest. Traduzindo, o `beforeAll` vai executar o código dentro dele **antes de todos os testes rodarem**. E o código que será executado é a função `createServer`.

Então, antes do primeiro `it` executar, o servidor _mock_ será executado e ficará ouvindo - `listen` - as requisições que serão feitas realmente.

O `afterEach` significa que **depois de cada teste** - `it` - o `msw` vai limpar ou _resetar_ os manipuladores de requisição para que não afete outros testes unitários do projeto. 

E o `afterAll` determina o que vai acontecer **depois que todos os testes** forem executados, nesse caso irá limpar todas os `mocks` que foram abertos e fechar a conexão com o servidor fictício.

### Pontos de Atenção

Ao fazermos isso, todos os testes de dentro desse arquivo que na realidade farão uma requisição `GET` para esse endpoint vão obedecer esse _mock_. Isso é importante saber, porquê se em algum momento você precisar testar uma resposta HTTP Status Code diferente de 2XX, não vai dar certo. Nesse caso você precisará criar o escopo de outra forma. Segue abaixo as diferenças:


Note que deixamos a variável `createServer` flexível para ser usada em outros lugares e invocar outros métodos do `setupServer`. Assim, podemos criar outro teste para testarmos uma resposta diferente de `200` - **OK**.

```js
it('should return user info link blog', async () => {
   createServer.use(
      rest.get(endpoint, (request, response, ctx) =>
         response(ctx.status(400))
      )
   );

   await expect(getUserInfoGithub('MockUser')).rejects.toThrow();
});
```

Nesse cenário usamos a função `use` do `setupServer` onde conseguimos encadear outro _mock_, agora com uma resposta diferente, o HTTP Status Code `400` - **BAD REQUEST**.

E assim conseguimos testar o erro usando o `await` antes do `expect`, por se tratar de uma `Promise` no retorno da função. E o `expect` estende o método `rejects` - que lembra o `then` - para recuperar o retorno.

Diante disso temos os resultados:

![Imagem mostrando que todos os testes passaram com 100% de cobertura](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b290omqjcle2fkzwam64.png)

Outro ponto de atenção é que esse código que fizemos vai funcionar dentro do escopo desse arquivo de teste unitário. Mas você pode como que escalar ainda mais o escopo dele. Geralmente no projeto, tem um arquivo chamado `jest.setup.js` ou `setupTests.js`.

Dentro dele, você pode criar um servidor _mock_ e colocar as requisições que você deseja interceptar para todos os arquivos de testes, como abaixo:

```js
const { setupServer } = require('msw/node');
const { rest } = require('msw');

const createServer = 
   setupServer(
      rest.get(endpoint, (req, res, ctx) => ()),
      rest.post(endpoint, (req, res, ctx) => ()),
      rest.put(endpoint, (req, res, ctx) => ()),
      rest.delete(endpoint, (req, res, ctx) => ()),
   );

beforeAll(() => createServer.listen())
afterEach(() => createServer.resetHandlers())
afterAll(() => createServer.close())
```

E depois aplicar essa configuração no arquivo `jest.config.js`:

```js
// jest.config.js
module.exports = {
   setupFilesAfterEnv: ['./jest.setup.js'],
}
```

---

### Referências

[Documentação do MSW](https://mswjs.io/docs/getting-started/integrate/node#configure-server)