├── .gitignore ├── .mocharc.js ├── README.md ├── config └── index.js ├── package-lock.json ├── package.json ├── test ├── produtos │ ├── post.test.js │ └── put.test.js └── usuarios │ ├── delete.test.js │ ├── get.test.js │ └── post.test.js └── utils ├── global.js └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | report/ 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /.mocharc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | spec: ["test/**/*.test.js"], 3 | require: 'utils/global.js', 4 | reporter: 'mochawesome', 5 | reporterOptions: 'json=false,reportDir=report,reportFilename=automacao-api' 6 | } 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 📖 (Curso) Automação de API com SuperTest 2 | 3 | #### Um curso do [Bora QA](http://boraqa.org) para a comunidade de testes ❤️ 4 | ___ 5 | 6 | ## O que irei aprender? 7 | 8 | 1. Automatizar os verbos HTTP _GET, POST, PUT_ e _DELETE_ em Javascript com SuperTest. 9 | 1. Gerar report HTML com o resultado. 10 | 1. Utilizar dados fakes. 11 | 1. Executar o mesmo teste em ambientes diferentes. 12 | 1. Manter um código bem estruturado. 13 | 14 | ## Tirando dúvidas 15 | 16 | As dúvidas devem ser tiradas na [aba de issues](https://github.com/Bora-QA/curso-api-com-supertest) do repositório. 17 | 18 | ## O que preciso saber antes de iniciar? 19 | 20 | - Básico de Javascript e NPM. 21 | - Teste manual em API Rest 22 | - Saber o que é API 23 | - Saber fazer uma request (pelo Postman, SoapUI ou outra ferramenta com interface) 24 | - Utilizar os verbos HTTP _get, post, put_ e _delete_ 25 | - Saber o que é JSON 26 | - Conhecer as partes de uma request, como _body_ e _header_ 27 | - Status code (_200, 404, 500_, etc) 28 | 29 | ### Material de apoio para adquirir os pré-requisitos 30 | 31 | #### Javascript e NPM 32 | - [Vídeo | Javascript Essencial - Conceitos iniciais](https://youtu.be/ipHuSfOYhwA?list=PLInBAd9OZCzxl38aAYdyoMHVg0xCgxrRx) 33 | - [Vídeo | Javascript Essencial - Variáveis e tipos de dados](https://youtu.be/ZW02MWzZXPE?list=PLInBAd9OZCzxl38aAYdyoMHVg0xCgxrRx) 34 | - [Vídeo | Javascript Essencial - Funções](https://youtu.be/TWmlIbvTjRo?list=PLInBAd9OZCzxl38aAYdyoMHVg0xCgxrRx) 35 | - [Texto | O guia completo do package.json do Node.js](https://www.luiztools.com.br/post/o-guia-completo-do-package-json-do-node-js/) 36 | - [Texto (inglês) | Creating a package.json file](https://docs.npmjs.com/creating-a-package-json-file) 37 | 38 | #### Teste em API Rest 39 | - [Playlist | Testes de API Rest | Júlio de Lima](https://www.youtube.com/watch?v=VA7uEDtMdBM&list=PLf8x7B3nFTl1hYsgnXaZnXa4V5DHDd4fa) 40 | - [Texto | Automatizando testes de sua API com Postman | Danilo José](https://medium.com/assertqualityassurance/automatizando-sua-api-com-postman-64a72185e1e6) 41 | - [Vídeo | API // Dicionário do programador](https://www.youtube.com/watch?v=vGuqKIRWosk) 42 | - [Vídeo | O QUE É UMA API? (Na prática, não na teoria)](https://www.youtube.com/watch?v=3LHSyha0xN0) 43 | 44 | ## O que preciso ter na máquina? 45 | 46 | - [Visual Studio Code](https://code.visualstudio.com/) ou outra IDE de sua preferência. 47 | - [Node](https://nodejs.org/en/download/) 48 | - Para verificar se a instalação foi feita com sucesso, execute `npm -v` e `node -v` no terminal. Se ambos os comandos retornarem número de versão, então está tudo certo. 49 | 50 | 51 | ## Ferramentas que serão utilizadas 52 | 53 | 1. **Mocha** (runner, estrutura) 54 | - [Site](https://mochajs.org/) 55 | - [NPM](https://www.npmjs.com/package/mocha) 56 | 1. [Mochawesome](https://www.npmjs.com/package/mochawesome) (mochawesome) 57 | 1. **Chai** (asserção) 58 | - [Site](https://www.chaijs.com/) 59 | - [NPM](https://www.npmjs.com/package/chai) 60 | 1. [SuperTest](https://www.npmjs.com/package/supertest) (request) 61 | 1. **Faker** (dados fakes) 62 | - [Site](http://marak.github.io/faker.js/) 63 | - [NPM](https://www.npmjs.com/package/faker) 64 | 1. [Standard](https://www.npmjs.com/package/standard) (lint) 65 | 1. [ServeRest](https://github.com/ServeRest/ServeRest) (API a ser testada) 66 | 67 | ## Grade 68 | 69 |
Expanda para ver a grade 70 | 71 | 1. Introdução 72 | - Qual o objetivo desse curso 73 | - curso para todos os níveis, desde que cumpram com os pré-requisitos de básico de JS e saiba testar webAPI REST manualmente 74 | 75 | 1. Iniciando o projeto e organizando a estrutura dos testes 76 | - npm init - iniciar projeto 77 | - npm install mocha 78 | - configurar .mocharc.js 79 | - inserir execução dos testes no script 'npm t' 80 | - explicar `describe` e `it` 81 | 82 | 1. Fazendo GET 83 | - npm install supertest 84 | - explicar serverest 85 | - primeiro teste 86 | - get em usuarios 87 | - GET com query string 88 | - usar chai para validar o retorno 89 | - npm install chai 90 | 91 | 1. Fazendo POST 92 | - fazer POST usuarios 93 | - cenarios: cadastro com sucesso E bad request 94 | - refatorar GET /usuarios para cadastrar usuario no inicio 95 | 96 | 1. DESAFIO 1 97 | - Em POST de /usuarios, criar automação que valide que o e-mail já está sendo utilizado. 98 | 99 | 1. Usando dados aleatórios 100 | - faker - usar dados aleatórios em POST e GET usuarios 101 | - npm install faker 102 | 103 | 1. Fazendo um pouco mais de POST enviando header 104 | - POST login e enviar auth no header de POST produtos 105 | 106 | 1. Organizando o código antes de aprendermos mais 107 | - estruturar os testes dentro de test/{usuarios, produtos} 108 | - criar o global.js para centralizar o supertest 109 | 110 | 1. Configurando report HTML 111 | - report HTML - instalar mochawesome 112 | - lembrar para inserir report/ no .gitignore 113 | 114 | 1. Fazendo DELETE 115 | - DELETE usuarios 116 | 117 | 1. Reaproveitando código 118 | - criar index com POST usuarios 119 | - refatorar DELETE /usuarios - colocar p criar usuario antes de cada teste 120 | - refatorar GET /usuarios - colocar p criar usuario antes de cada teste 121 | 122 | 1. DESAFIO 2 123 | - Reescrever POST de /produtos, reaproveitando /login e POST /usuarios em `index` 124 | 125 | 1. Fazendo PUT 126 | - atualizar dados - PUT produtos 127 | - ordem das requests > post login, post produtos, put produto 128 | - inserir /cadastro e /login no beforeEach 129 | 130 | 1. DESAFIO 3 131 | - Reescrever o PUT de /produtos para que fique enxuto, levando alguns códigos para `index` 132 | 133 | 1. Executar os testes em ambientes diferentes 134 | - criar diretório config 135 | - ajustar global.js 136 | 137 | 1. Padronizar o código 138 | - install standard 139 | 140 | 1. Sobrescrever configuração do Mocha no terminal 141 | 142 | 1. Encerramento do curso 143 | 144 |
145 | 146 | ## Material complementar 147 | 148 | - [Destructuring assignment](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment) 149 | - [Exemplo de automação com SuperTest](https://github.com/PauloGoncalvesBH/sample-supertest) 150 | 151 | ## Liceça 152 | 153 | A licença do material está em [Bora-QA/Licença](https://github.com/Bora-QA/Sobre/blob/main/LICENSE). 154 | -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | const dev = { 2 | url: 'http://localhost:3000' 3 | } 4 | 5 | const prod = { 6 | url: 'https://serverest.dev' 7 | } 8 | 9 | module.exports = process.env.NODE_ENV === 'dev' ? dev : prod 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "curso-supertest-bora-qa", 3 | "private": true, 4 | "author": "Paulo Gonçalves (https://github.com/PauloGoncalvesBH)", 5 | "description": "Bora QA - Automação de API com SuperTest", 6 | "scripts": { 7 | "test": "NODE_ENV=dev mocha", 8 | "test:prod": "NODE_ENV=prod mocha", 9 | "lint": "standard", 10 | "lint:fix": "standard --fix" 11 | }, 12 | "dependencies": { 13 | "chai": "4.3.4", 14 | "faker": "5.5.3", 15 | "mocha": "9.0.3", 16 | "mochawesome": "6.2.2", 17 | "standard": "16.0.3", 18 | "supertest": "6.1.5" 19 | }, 20 | "standard": { 21 | "globals": [ 22 | "beforeEach", 23 | "describe", 24 | "it", 25 | "request" 26 | ] 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/produtos/post.test.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai') 2 | const faker = require('faker') 3 | 4 | const { cadastrarUsuario, login } = require('../../utils') 5 | 6 | describe('POST /produtos', () => { 7 | it('Cadastro com sucesso', async () => { 8 | const { email, password } = await cadastrarUsuario({ administrador: 'true' }) 9 | const { authorization } = await login(email, password) 10 | 11 | const { body } = await request.post('/produtos').send({ 12 | nome: faker.commerce.productName() + faker.datatype.number(), 13 | preco: faker.datatype.number(), 14 | descricao: faker.random.words(), 15 | quantidade: faker.datatype.number() 16 | }).set('authorization', authorization).expect(201) 17 | 18 | chai.assert.deepEqual(body, { message: 'Cadastro realizado com sucesso', _id: body._id }) 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /test/produtos/put.test.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai') 2 | const faker = require('faker') 3 | 4 | const { cadastrarProduto, cadastrarUsuario, login } = require('../../utils') 5 | 6 | const rotaProdutos = '/produtos' 7 | 8 | describe('PUT /produtos', () => { 9 | beforeEach(async () => { 10 | const { email, password } = await cadastrarUsuario({ administrador: 'true' }) 11 | const { authorization } = await login(email, password) 12 | this.authorization = authorization 13 | }) 14 | 15 | it('Alterar todos os dados de produto com sucesso', async () => { 16 | const produto = await cadastrarProduto({ authorization: this.authorization }) 17 | 18 | const { body } = await request 19 | .put(`${rotaProdutos}/${produto._id}`) 20 | .send({ 21 | nome: faker.commerce.productName() + faker.datatype.number(), 22 | preco: produto.preco, 23 | descricao: produto.descricao, 24 | quantidade: produto.quantidade 25 | }) 26 | .set('authorization', this.authorization) 27 | .expect(200) 28 | 29 | chai.assert.deepEqual(body, { message: 'Registro alterado com sucesso' }) 30 | }) 31 | 32 | it('Cadastro com sucesso', async () => { 33 | const { body } = await request.put(rotaProdutos + '/a').send({ 34 | nome: faker.commerce.productName() + faker.datatype.number(), 35 | preco: faker.datatype.number(), 36 | descricao: faker.random.words(), 37 | quantidade: faker.datatype.number() 38 | }).set('authorization', this.authorization) 39 | .expect(201) 40 | 41 | chai.assert.deepEqual(body, { message: 'Cadastro realizado com sucesso', _id: body._id }) 42 | }) 43 | }) 44 | -------------------------------------------------------------------------------- /test/usuarios/delete.test.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai') 2 | 3 | const { cadastrarUsuario } = require('../../utils') 4 | 5 | const rotaUsuarios = '/usuarios' 6 | 7 | describe('DELETE /usuarios', () => { 8 | it('Registro excluído com sucesso', async () => { 9 | const { _id: idUsuarioCadastrado } = await cadastrarUsuario() 10 | 11 | const { body: bodyDel } = await request.del(`${rotaUsuarios}/${idUsuarioCadastrado}`).expect(200) 12 | const { body: bodyGet } = await request.get(rotaUsuarios).query({ _id: idUsuarioCadastrado }) 13 | 14 | chai.assert.deepEqual(bodyDel, { message: 'Registro excluído com sucesso' }) 15 | chai.assert.deepEqual(bodyGet, { quantidade: 0, usuarios: [] }) 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /test/usuarios/get.test.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai') 2 | 3 | const { cadastrarUsuario } = require('../../utils') 4 | 5 | const rotaUsuarios = '/usuarios' 6 | 7 | describe('GET /usuarios', () => { 8 | describe('Query string', () => { 9 | it('Retornar usuário com sucesso ao buscar usuário com as chaves \'nome\', \'email\', \'password\', \'administrador\' e \'_id\'', async () => { 10 | const { nome, email, password, administrador, _id } = await cadastrarUsuario() 11 | 12 | const { body } = await request.get(rotaUsuarios).query({ _id }).expect(200) 13 | 14 | chai.assert.deepEqual(body, { 15 | quantidade: 1, 16 | usuarios: [ 17 | { 18 | nome, 19 | email, 20 | password, 21 | administrador, 22 | _id 23 | } 24 | ] 25 | }) 26 | }) 27 | 28 | it('Deve retornar nenhum usuário ao passar chave \'_id\' com valor inexistente', async () => { 29 | const { body } = await request.get(rotaUsuarios).query({ _id: 'a' }).expect(200) 30 | chai.assert.deepEqual(body, { quantidade: 0, usuarios: [] }) 31 | }) 32 | 33 | it('Deve retornar mensagem de erro \'testando não é permitido\' ao passar chave inexistente', async () => { 34 | const { body } = await request.get(rotaUsuarios).query({ testando: 'a' }).expect(400) 35 | 36 | chai.assert.deepEqual(body, { 37 | testando: 'testando não é permitido' 38 | }) 39 | }) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /test/usuarios/post.test.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai') 2 | const faker = require('faker') 3 | 4 | const rotaUsuarios = '/usuarios' 5 | 6 | describe(`Validar verbo POST na rota ${rotaUsuarios}`, () => { 7 | it('Cadastro com sucesso', async () => { 8 | const { body } = await request.post(rotaUsuarios).send({ 9 | nome: faker.name.firstName() + ' ' + faker.name.lastName(), 10 | email: faker.internet.email(), 11 | password: faker.internet.password(), 12 | administrador: `${faker.datatype.boolean()}` 13 | }).expect(201) 14 | 15 | chai.assert.deepEqual(body, { message: 'Cadastro realizado com sucesso', _id: body._id }) 16 | }) 17 | 18 | it('Deve retornar erro ao enviar POST sem as propriedades obrigatórias', async () => { 19 | const { body } = await request.post(rotaUsuarios).send({ inexistente: '1' }).expect(400) 20 | 21 | chai.assert.deepEqual(body, { 22 | nome: 'nome é obrigatório', 23 | email: 'email é obrigatório', 24 | password: 'password é obrigatório', 25 | administrador: 'administrador é obrigatório', 26 | inexistente: 'inexistente não é permitido' 27 | }) 28 | }) 29 | }) 30 | -------------------------------------------------------------------------------- /utils/global.js: -------------------------------------------------------------------------------- 1 | const supertest = require('supertest') 2 | 3 | const { url } = require('../config') 4 | 5 | global.request = supertest(url) 6 | -------------------------------------------------------------------------------- /utils/index.js: -------------------------------------------------------------------------------- 1 | const faker = require('faker') 2 | 3 | async function cadastrarUsuario ({ 4 | nome = faker.name.firstName() + ' ' + faker.name.lastName(), 5 | email = faker.internet.email(), 6 | password = faker.internet.password(), 7 | administrador = `${faker.datatype.boolean()}` 8 | } = {}) { 9 | const { body } = await request.post('/usuarios').send({ 10 | nome, 11 | email, 12 | password, 13 | administrador 14 | }).expect(201) 15 | return { 16 | nome, 17 | email, 18 | password, 19 | administrador, 20 | _id: body._id 21 | } 22 | } 23 | 24 | async function login (email, password) { 25 | const { body } = await request.post('/login').send({ 26 | email, 27 | password 28 | }).expect(200) 29 | return body 30 | } 31 | 32 | async function cadastrarProduto ({ 33 | authorization, 34 | nome = faker.commerce.productName() + faker.datatype.number(), 35 | preco = faker.datatype.number(), 36 | descricao = faker.random.words(), 37 | quantidade = faker.datatype.number() 38 | }) { 39 | const { body } = await request 40 | .post('/produtos') 41 | .send({ 42 | nome, 43 | preco, 44 | descricao, 45 | quantidade 46 | }) 47 | .set('authorization', authorization) 48 | .expect(201) 49 | return { 50 | _id: body._id, 51 | nome, 52 | preco, 53 | descricao, 54 | quantidade 55 | } 56 | } 57 | 58 | module.exports = { 59 | cadastrarProduto, 60 | cadastrarUsuario, 61 | login 62 | } 63 | --------------------------------------------------------------------------------