├── .github
└── workflows
│ └── unit-test.yml
├── .gitignore
├── README.md
├── __mocks__
└── axios.js
├── package-lock.json
├── package.json
├── src
├── index.js
└── promiseTestModule.js
└── tests
├── mock.test.js
└── spy.test.js
/.github/workflows/unit-test.yml:
--------------------------------------------------------------------------------
1 | name: Testes unitários
2 |
3 | on:
4 | push:
5 | pull_request:
6 | workflow_dispatch:
7 | inputs:
8 | jest-cli:
9 | description: 'Opção de Jest CLI'
10 | required: false
11 | default: ''
12 |
13 | jobs:
14 | unit-test:
15 |
16 | runs-on: ubuntu-18.04
17 |
18 | steps:
19 | - name: Checkout do repositório
20 | uses: actions/checkout@v2
21 | - name: Setup do Node.js
22 | uses: actions/setup-node@v2.1.1
23 | - name: Instalar as dependências do projeto
24 | run: npm ci
25 | - name: Executar os testes de unidade ${{ github.event.inputs.jest-cli }}
26 | run: npm test -- ${{ github.event.inputs.jest-cli }}
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | coverage/
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Dublês de teste com [Jest](https://www.npmjs.com/package/jest) [](https://github.com/PauloGoncalvesBH/dubles-de-teste-com-jest/actions)
2 |
3 |
4 |
Esse material é quase de graça, basta deixar uma star ⭐ no repositório.
5 |
6 |
7 | ## Sobre
8 |
9 | Esse projeto é para servir de material pessoal de consulta de como implementar dublês de testes utilizando Jest.
10 |
11 | > **Observação:** As asserções não seguem as boas práticas de cada dublê pois foi preciso validar se as alterações de comportamento estão funcionando.
12 |
13 | Os testes foram implementados em [`/tests`](/tests), consumindo os métodos de [`/src`](/src) e com mock manual em [`/__mocks__`](/__mocks__).
14 |
15 | ## Dublês implementados
16 |
17 | - Mock, em [./tests/mock.test.js](tests/mock.test.js)
18 | - [`Manual Mock`](https://jestjs.io/docs/en/manual-mocks) - Mockando um módulo manualmente
19 | - [`mockResolvedValue()`](https://jestjs.io/docs/en/mock-functions#mocking-modules) - Resolvendo uma Promise e mockando um módulo
20 | - [`mockRejectedValue()`](https://jestjs.io/docs/en/mock-function-api#mockfnmockrejectedvaluevalue) - Rejeitando uma Promise e mockando um módulo
21 | - [`mockFn.mockImplementation(fn)`](https://jestjs.io/docs/en/mock-function-api#mockfnmockimplementationfn) - Mockando um método
22 | - [`mockFn.mockImplementationOnce(fn)`](https://jestjs.io/docs/en/mock-function-api#mockfnmockimplementationoncefn) - Mockando um método na primeira
23 | e segunda chamada
24 | - Spy, em [./tests/spy.test.js](tests/spy.test.js)
25 | - [`jest.spyOn(object, methodName)`](https://jestjs.io/docs/en/jest-object#jestspyonobject-methodname) - Visualizando comportamento interno de um método
26 | - [`jest.spyOn(object, methodName, accessType?)`](https://jestjs.io/docs/en/jest-object#jestspyonobject-methodname-accesstype) - Visualizando comportamento interno de um método com tipo de acesso
27 |
28 | ## Execução
29 |
30 | Instale as dependências e execute os testes com:
31 | ```
32 | npm it
33 | ```
34 |
35 | ## Materiais de apoio
36 |
37 | - [Estudo de como é a implementação interna de um spy, mock, expect e estrutura dos testes](https://github.com/PauloGoncalvesBH/my-framework-test)
38 |
39 | ### Documentação do Jest
40 | - [Jest Object](https://jestjs.io/docs/en/jest-object)
41 | - [Expect](https://jestjs.io/docs/en/expect)
42 | - [Manual Mocks](https://jestjs.io/docs/en/manual-mocks)
43 | - [Mock Functions](https://jestjs.io/docs/en/mock-function-api)
44 |
45 | ## Outros textos
46 |
47 | - [Test Doubles (Mocks, Stubs, Fakes, Spies e Dummies) - PT-BR](https://medium.com/rd-shipit/test-doubles-mocks-stubs-fakes-spies-e-dummies-a5cdafcd0daf)
48 | - [Test Doubles - Fakes, Mocks and Stubs - EN](https://blog.pragmatists.com/test-doubles-fakes-mocks-and-stubs-1a7491dfa3da)
49 | - [Vamos falar de dublês de teste (Test Double) para testes unitários](https://dev.to/henriquepalote/vamos-falar-de-dubles-de-teste-test-double-para-testes-unitarios-2725)
50 |
--------------------------------------------------------------------------------
/__mocks__/axios.js:
--------------------------------------------------------------------------------
1 | // mock de módulo utilizado em mock.test.js e método getUserByEmail
2 |
3 | module.exports = {
4 | get: jest.fn((rota, { params }) => Promise.resolve({
5 | data: {
6 | quantidade: 5,
7 | usuarios: [
8 | {
9 | nome: 'Fulano',
10 | email: params.email
11 | }
12 | ]
13 | }
14 | }))
15 | }
16 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dubles-de-teste-com-jest",
3 | "author": "Paulo Gonçalves (https://www.linkedin.com/in/paulo-goncalves/)",
4 | "license": "MIT",
5 | "private": true,
6 | "repository": {
7 | "type": "git",
8 | "url": "git+https://github.com/PauloGoncalvesBH/dubles-de-teste-com-jest.git"
9 | },
10 | "scripts": {
11 | "test": "jest --collectCoverage true --silent",
12 | "test:watch": "jest --watch"
13 | },
14 | "dependencies": {
15 | "axios": "^0.21.1",
16 | "jest": "^26.6.3"
17 | },
18 | "jest": {
19 | "verbose": true,
20 | "testMatch": [
21 | "**/*.test.js"
22 | ],
23 | "collectCoverageFrom": [
24 | "src/*.js"
25 | ]
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | const axios = require('axios')
2 |
3 | const promiseTest = require('./promiseTestModule')
4 |
5 | // métodos utilizados em spy.test.js
6 |
7 | function exception () {
8 | console.error('Testes unitários em JS\t\nOcorreu um problema desconhecido.\n')
9 | process.exit(1)
10 | }
11 |
12 | const pessoa = {
13 | get nacionalidade () {
14 | return 'brasileiro'
15 | }
16 | }
17 |
18 | // métodos utilizados em mock.test.js
19 |
20 | const getUserByEmail = async (email) => {
21 | const { data } = await axios.get('https://serverest.dev/usuarios', {
22 | params: {
23 | email
24 | }
25 | })
26 | return data
27 | }
28 |
29 | const getQuantidadeFromUserByEmail = async (email) => {
30 | const { quantidade } = await getUserByEmail(email)
31 | return quantidade
32 | }
33 |
34 | const consumirPromise = resolver => {
35 | promiseTest(resolver)
36 | .then((e) => console.log(e))
37 | .catch((error) => console.error(error))
38 | }
39 |
40 | module.exports = {
41 | exception,
42 | pessoa,
43 | getUserByEmail,
44 | getQuantidadeFromUserByEmail,
45 | consumirPromise
46 | }
47 |
--------------------------------------------------------------------------------
/src/promiseTestModule.js:
--------------------------------------------------------------------------------
1 |
2 | function promiseTest(resolver) {
3 | return new Promise((resolve, reject) => {
4 | if (resolver)
5 | resolve('Promise resolvida')
6 | reject('Deu erro')
7 | })
8 | }
9 |
10 | module.exports = promiseTest
--------------------------------------------------------------------------------
/tests/mock.test.js:
--------------------------------------------------------------------------------
1 | const src = require('../src')
2 | const axios = require('axios')
3 |
4 | jest.mock('../src/promiseTestModule')
5 |
6 | describe('Mock', () => {
7 | beforeEach(() => jest.clearAllMocks())
8 |
9 | /*
10 | Mockando um módulo manualmente
11 | Manual Mocks - https://jestjs.io/docs/en/manual-mocks
12 | */
13 | test('Manual Mock - Mockando um módulo manualmente', async () => {
14 | const user = await src.getUserByEmail('teste@qa.com')
15 |
16 | expect(axios.get).toHaveBeenCalledTimes(1)
17 | expect(axios.get).toHaveBeenCalledWith('https://serverest.dev/usuarios', {
18 | params: {
19 | email: 'teste@qa.com'
20 | }
21 | })
22 | expect(user.quantidade).toBe(5)
23 | expect(user.usuarios[0].email).toBe('teste@qa.com')
24 | })
25 |
26 | /*
27 | Resolvendo uma Promise e mockando um módulo
28 | Mocking Modules - https://jestjs.io/docs/en/mock-functions#mocking-modules
29 | */
30 | test('mockResolvedValue() - Resolvendo uma Promise e mockando um módulo', async () => {
31 | axios.get.mockResolvedValueOnce({
32 | data: {
33 | quantidade: 102030,
34 | usuarios: [
35 | {
36 | nome: 'Fulano da Silva',
37 | email: 'fulano.teste@qa.com'
38 | }
39 | ]
40 | }
41 | })
42 |
43 | const user = await src.getUserByEmail('fulano.teste@qa.com')
44 |
45 | expect(axios.get).toHaveBeenCalledTimes(1)
46 | expect(axios.get).toHaveBeenCalledWith('https://serverest.dev/usuarios', {
47 | params: {
48 | email: 'fulano.teste@qa.com'
49 | }
50 | })
51 | expect(user.quantidade).toBe(102030)
52 | expect(user.usuarios[0].email).toBe('fulano.teste@qa.com')
53 | })
54 |
55 | /*
56 | Rejeitando uma Promise e mockando um módulo
57 | Mocking Modules - https://jestjs.io/docs/en/mock-function-api#mockfnmockrejectedvaluevalue
58 | */
59 | test('mockRejectedValue() - Rejeitando uma Promise e mockando um módulo', () => {
60 | const promiseTest = require('../src/promiseTestModule')
61 | promiseTest.mockRejectedValueOnce('Promise mockada')
62 |
63 | src.consumirPromise(true)
64 | })
65 |
66 | /*
67 | Mockando um método
68 | mockFn.mockImplementation(fn) - https://jestjs.io/docs/en/mock-function-api#mockfnmockimplementationfn
69 | */
70 | test('mockFn.mockImplementation(fn) - Mockando um método', async () => {
71 | const mock = jest.spyOn(src, 'getQuantidadeFromUserByEmail').mockImplementationOnce(() => 99999)
72 |
73 | const quantidadeDeUsuarios = await src.getQuantidadeFromUserByEmail('teste@qa.com')
74 |
75 | expect(mock).toHaveBeenCalledTimes(1)
76 | expect(mock).toHaveBeenCalledWith('teste@qa.com')
77 | expect(quantidadeDeUsuarios).toBe(99999)
78 | })
79 |
80 | /*
81 | Mockando um método na primeira e segunda chamada
82 | mockFn.mockImplementationOnce(fn) - https://jestjs.io/docs/en/mock-function-api#mockfnmockimplementationoncefn
83 | */
84 | test('mockFn.mockImplementationOnce(fn) - Mockando um método na primeira e segunda chamada', async () => {
85 | const mock = jest
86 | .spyOn(src, 'getQuantidadeFromUserByEmail')
87 | .mockImplementationOnce(() => 654321)
88 | .mockImplementationOnce(() => 123456)
89 |
90 | const quantidadeDeUsuariosPrimeiraChamada = await src.getQuantidadeFromUserByEmail('teste@qa.com')
91 | const quantidadeDeUsuariosSegundaChamada = await src.getQuantidadeFromUserByEmail('teste@qa.com')
92 | const quantidadeDeUsuariosTerceiraChamada = await src.getQuantidadeFromUserByEmail('teste@qa.com')
93 |
94 | expect(mock).toHaveBeenCalledTimes(3)
95 | expect(quantidadeDeUsuariosPrimeiraChamada).toBe(654321)
96 | expect(quantidadeDeUsuariosSegundaChamada).toBe(123456)
97 | expect(quantidadeDeUsuariosTerceiraChamada).toBe(5)
98 | })
99 | })
100 |
--------------------------------------------------------------------------------
/tests/spy.test.js:
--------------------------------------------------------------------------------
1 | const src = require('../src')
2 |
3 | describe('Spy', () => {
4 | /*
5 | Visualizando comportamento interno de um método
6 | jest.spyOn(object, methodName) - https://jestjs.io/docs/en/jest-object#jestspyonobject-methodname
7 | */
8 | test('jest.spyOn(object, methodName) - Visualizando comportamento interno de um método', () => {
9 | const consoleSpy = jest.spyOn(console, 'error')
10 | const exitMock = jest.spyOn(process, 'exit').mockImplementation()
11 |
12 | src.exception()
13 |
14 | expect(consoleSpy).toHaveBeenCalledTimes(1)
15 | expect(consoleSpy).toHaveBeenCalledWith('Testes unitários em JS\t\nOcorreu um problema desconhecido.\n')
16 | expect(exitMock).toHaveBeenCalledWith(1)
17 | })
18 |
19 | /*
20 | Visualizando comportamento interno de um método com tipo de acesso
21 | jest.spyOn(object, methodName, accessType?) - https://jestjs.io/docs/en/jest-object#jestspyonobject-methodname-accesstype
22 | */
23 | test('jest.spyOn(object, methodName, accessType?) - Visualizando comportamento interno de um método com tipo de acesso', () => {
24 | const spy = jest.spyOn(src.pessoa, 'nacionalidade', 'get')
25 |
26 | const nacionalidade = src.pessoa.nacionalidade
27 |
28 | expect(spy).toHaveBeenCalled()
29 | expect(nacionalidade).toBe('brasileiro')
30 | })
31 | })
32 |
--------------------------------------------------------------------------------