
A crescente busca para garantir a qualidade da aplicação que está sendo desenvolvida, faz com que tenhamos a necessidade de aplicar diferentes tipos de testes em nossa aplicação e implementar cada teste em sua respectiva camada. E para poder identificar qual camada melhor se aplica ao teste, podemos utilizar a conhecida pirâmide de testes.
Neste artigo, iremos tratar de um tipo de teste voltado à aplicações com arquiteturas baseadas em serviços e microsserviços. Vamos falar de Teste de Contrato de API. Mas fique tranquilo, não vem “textão” por aí, afinal, nosso objetivo é hands-on.
Apenas para contextualizar, vou passar brevemente pela parte teórica antes de colocarmos a mão na massa.
API
- API é um conjunto de rotinas e padrões de programação para acesso a um aplicativo de software ou plataforma, baseado na Web;
- A sigla API refere-se ao termo em inglês “Application Programming Interface”, que significa em tradução para o português: “Interface de Programação de Aplicativos”.
Teste de API
- Estão entre a camada de testes de UI e Unitários;
- Podem ser automatizados em paralelo com o desenvolvimento da API;
- Facilitam a validação de múltiplos cenários;
- Garantem que a estrutura do JSON de retorno esteja correta.
Payload
- É todo o conteúdo enviado por um meio de transporte. É o corpo da informação, o que é útil de tudo o que está sendo transmitido;
- Não possui um formato obrigatório. O fato de ser JSON é apenas circunstancial.
Response
- É todo o conteúdo recebido por um meio de transporte; é o corpo da informação de retorno;
Contrato de API
- Tem como objetivo garantir que o conteúdo fornecido não tenha sido modificado, podemos dizer que tem a finalidade de validar se o contrato acordado foi ou não quebrado; deve validar se o schema permanece o mesmo garantindo assim a integridade dos dados na comunicação entre client/server;
- É possível validar se os dados continuam do mesmo tipo, como os valores limites, a restrição de valores recebidos, se a estrutura foi ou não modificada, etc.
Como falei no início que seria mão na massa, bora codar!

Vamos utilizar o amado e odiado JavaScript, mas, para isso temos que preparar o nosso ambiente. Vou me restringir ao ambiente macOS, caso esteja utilizando um sistema operacional diferente, dá uma “googlada” e veja como instalar as dependências em seu SO.
$ brew update $ brew install node - $node -v $ npm install --global mocha $ npm install supertest $ npm install chai $ npm install joi $ npm install joi-assert
Agora, para dar aquele confere, veja se o Node foi instalado corretamente. No meu caso estou utilizando a versão 7.x.
$ node --version v7.9.0
Como a repetição é a melhor forma de aprender, não copie e cole os códigos abaixo, deixe a preguiça de lado e escreva você mesmo o código.
Criamos um mock que consiste em informações sobre um filme, que vai retornar o seguinte objeto:
{
"Title": "Guardians of the Galaxy Vol. 2",
"Year": 2017,
"Language": "English",
"Ratings": [{
"Source": "Rotten Tomatoes",
"Value": "83%"
}, {
"Source": "Metacritic",
"Value": "67/100"
}],
"Type": "movie",
"Production": "Walt Disney Pictures",
"Website": "https://marvel.com/guardians",
"Response": true
}
Basicamente, precisamos criar dois arquivos. Um chamado request.js e outro chamado schema.js.
request.js
'use strict';
const Joi = require('joi');
const request = require('supertest');
const expect = require('chai').expect;
const joiAssert = require('joi-assert');
const {
schemaFilmeValido,
schemanheritingRating
} = require('./schema');
const URL = 'http://www.mocky.io/';
const PATH = 'v2/5a5cb3872e00005e199f83db'
describe('Teste Contrato API', function () {
it('Validando response com joiAssert', function (done) {
request(URL)
.get(PATH)
.expect('Content-Type', /json/)
.end(function (err, res) {
expect(res.status).to.be.eql(200);
joiAssert(res.body, schemaFilmeValido);
done(err);
})
});
/* abortEarly: true. Para a validação assim que ocorre o primeiro erro,
abortEarly: false. Retorna todos os erros encontrados no schema.
Por default esse tag é true */
it('Validando response com Joi.validate', function (done) {
request(URL)
.get(PATH)
.expect('Content-Type', /json/)
.end(function (err, res) {
expect(res.status).to.be.eql(200);
Joi.validate(res.body, schemaFilmeValido, {
abortEarly: false
}, (err, data) => {
if (err) throw err;
});
done(err);
})
});
it('Validando response utilizando schema conjugado', function (done) {
request(URL)
.get(PATH)
.expect('Content-Type', /json/)
.end(function (err, res) {
expect(res.status).to.be.eql(200);
Joi.validate(res.body, schemanheritingRating, {
abortEarly: false
}, (err, data) => {
if (err) throw err;
});
done(err);
})
});
});
schema.js
'use strict';
const Joi = require('joi');
/*Neste exemplo, montamos o schema com todas as propriedades do objeto que recebemos.*/
const schemaFilmeValido = Joi.object({
Title: Joi.string().required(),
Year: Joi.number().integer().positive().required().options({
convert: false
}),
Language: Joi.string().valid('English').required(),
Ratings: Joi.array().items(Joi.object({
Source: Joi.string().required(),
Value: Joi.string().required(),
})),
Type: Joi.string().valid(['movie', 'series', 'cartoon']).required(),
Production: Joi.string().optional().allow('', null),
Website: Joi.string().required(),
Response: Joi.boolean().required(),
}).required();
/*Caso tenhamos um microserviço de rating podemos criar um schema único para ele que depois importaremos esse schema para dentro do schema de Filmes.*/
const ratingSchema = Joi.object({
Source: Joi.string().required(),
Value: Joi.string().required(),
})
/*É possível conjugar múltiplos schemas sem a necessidade de duplicar codigos no exemplo abaixo, importamos o schema ratingSchema.
Essa estrutura é util quando possuímos vários micro serviços que irão compor um único serviço final e precisamos tbm implementar testes para esses micro serviços.*/
const schemanheritingRating = Joi.object({
Title: Joi.string().required(),
Year: Joi.number().integer().positive().required().options({
convert: false
}),
Language: Joi.string().valid('English').required(),
Ratings: Joi.array().items(ratingSchema),
Type: Joi.string().valid(['movie', 'series', 'cartoon']).required(),
Production: Joi.string().optional().allow('', null),
Website: Joi.string().required(),
Response: Joi.boolean().required(),
}).required();
module.exports = {
schemaFilmeValido,
schemanheritingRating
}
Conforme o código acima, temos as seguintes validações:
- Joi.string().required() – String e Obrigatório;
- Joi.number().integer().positive().required() – Inteiro, Positivo e Obrigatório;
- Joi.string().valid(‘English’).required() – String, onde só iremos aceitar o valor ‘English’ e esse campo vai ser obrigatório;
- Joi.string().valid([‘movie’, ‘series’, ‘cartoon’]).required() – Essa opção é semelhante à mostrada acima, porém, ao invés de apenas uma opção no valor, vamos informar uma lista de possíveis valores;
- Joi.string().optional().allow(”, null) – Informamos que a propriedade é opcional e é permitido vazio;
- Response: Joi.boolean().required() – Por fim, temos a opção boolean, na qual só vamos aceitar valores true ou false.
Agora que escrevemos os cenários e os contratos, vamos rodar:
$ mocha request.js
Se tudo estiver certo, o resultado vai ser como esse abaixo:

E assim concluímos uma implementação inicial de testes de contrato de API. Fique à vontade para manipular o contrato e validar as opções não demonstradas neste artigo.
Referências
- GitHub
- NodeJS
- supertest
- chai
- Mocha
- JOI
- JOI-Assert
- http://www.restapitutorial.com/httpstatuscodes.html
***
Este artigo foi publicado originalmente em: https://www.concrete.com.br/2018/02/14/testes-de-contrato-de-api/




