Capa do Ebook gratuito Postman na Prática: Testes de API, Coleções, Ambientes e Automação de Rotinas de QA

Postman na Prática: Testes de API, Coleções, Ambientes e Automação de Rotinas de QA

Novo curso

18 páginas

Validações de contrato: campos obrigatórios, tipos e schemas JSON

Capítulo 9

Tempo estimado de leitura: 15 minutos

Audio Icon

Ouça em áudio

0:00 / 0:00

O que são validações de contrato em APIs

Validação de contrato é a prática de verificar se uma API está entregando respostas que respeitam um acordo previamente definido entre quem consome e quem fornece o serviço. Esse acordo costuma ser expresso por regras como: quais campos devem existir, quais tipos esses campos devem ter, quais valores são permitidos, quais estruturas de objetos e listas são aceitas e como erros devem ser retornados. Em testes de API, validar contrato é diferente de validar “negócio”: você não está verificando se o cálculo do frete está correto, e sim se a resposta tem o formato esperado para que clientes (front-end, integrações, automações) não quebrem quando a API evoluir.

Na prática, validações de contrato ajudam a detectar mudanças involuntárias, como renomear um campo, trocar um tipo (por exemplo, número para string), remover um atributo que era obrigatório, ou alterar a estrutura de um objeto aninhado. Essas mudanças podem passar despercebidas se você testar apenas status code e alguns asserts pontuais. Um contrato bem validado reduz regressões e dá segurança para refatorações e versionamentos.

Campos obrigatórios: presença e consistência mínima

Uma validação de contrato frequentemente começa pelo básico: garantir que campos obrigatórios existem. “Obrigatório” aqui significa: o consumidor depende do campo para funcionar. Por exemplo, em uma resposta de “detalhe de pedido”, é comum que id, status, createdAt e items sejam essenciais. Se algum deles faltar, o cliente pode falhar, mesmo que o status code seja 200.

Há duas dimensões importantes ao validar obrigatoriedade: presença do campo e presença de valor. Um campo pode existir, mas vir como null ou string vazia. Dependendo do contrato, isso pode ser inválido. Por isso, ao definir “obrigatório”, deixe claro se o campo deve ser “presente e não nulo”, “presente e não vazio” ou apenas “presente”.

Exemplo de resposta para validar campos obrigatórios

Considere uma resposta JSON simplificada de um pedido:

Continue em nosso aplicativo

Você poderá ouvir o audiobook com a tela desligada, ganhar gratuitamente o certificado deste curso e ainda ter acesso a outros 5.000 cursos online gratuitos.

ou continue lendo abaixo...
Download App

Baixar o aplicativo

{  "id": "ord_123",  "status": "PAID",  "createdAt": "2026-01-07T10:12:00Z",  "customer": {    "id": "cus_9",    "email": "ana@exemplo.com"  },  "items": [    { "sku": "SKU-1", "quantity": 2, "price": 19.9 }  ],  "total": 39.8}

Campos obrigatórios típicos aqui: id, status, createdAt, customer.id, items, e dentro de cada item: sku, quantity, price. Note que “obrigatório” pode ser diferente em endpoints diferentes: em uma listagem, talvez customer.email não venha por performance, mas no detalhe venha.

Tipos: garantindo que o formato não muda silenciosamente

Depois de garantir presença, o próximo nível é validar tipos. Tipos errados são uma fonte comum de bugs: um campo que era número vira string, um boolean vira 0/1, uma data vira timestamp numérico, um objeto vira lista, e assim por diante. Mesmo que o valor “pareça” correto, o consumidor pode quebrar ao tentar tratar o dado.

Em JSON, os tipos básicos são: string, number, boolean, object, array e null. Em contrato, você normalmente quer restringir ainda mais: string com formato de data, string com padrão de e-mail, number inteiro, number com mínimo e máximo, array com itens de um tipo específico, objeto com propriedades obrigatórias e proibição de propriedades extras.

Armadilhas comuns de tipo

  • IDs numéricos vs strings: muitas APIs retornam id como string para evitar problemas de precisão e padronizar. Se um endpoint retornar como number, pode quebrar clientes que concatenam ou armazenam como string.

  • Datas: retornar createdAt como string ISO-8601 em um endpoint e como timestamp em outro cria inconsistência. O contrato deve definir um formato único.

  • Campos opcionais: um campo opcional pode ser omitido ou pode vir como null. O contrato deve explicitar qual comportamento é aceito.

  • Arrays vazios: items deve ser array mesmo quando vazio, e não null ou objeto. Isso é parte do contrato.

Schemas JSON: validação estrutural e semântica

Para ir além de asserts manuais, você pode usar JSON Schema para descrever o contrato de uma resposta. JSON Schema é um padrão para definir a estrutura de um JSON: tipos, propriedades, obrigatoriedade, formatos, limites, enumerações e regras de composição. Com ele, você valida o “shape” completo do payload de forma consistente e escalável.

Um schema bem feito permite detectar rapidamente mudanças como: remoção de campo obrigatório, alteração de tipo, inclusão de propriedades inesperadas (quando você decide bloquear extras), e inconsistências em objetos aninhados. Além disso, schemas podem ser reutilizados entre requests e coleções, reduzindo duplicação.

Exemplo de JSON Schema para o pedido

Abaixo um schema simplificado para a resposta do pedido. Ele exige campos principais, valida tipos e restringe alguns formatos:

{  "$schema": "http://json-schema.org/draft-07/schema#",  "type": "object",  "additionalProperties": false,  "required": ["id", "status", "createdAt", "customer", "items", "total"],  "properties": {    "id": { "type": "string", "minLength": 1 },    "status": { "type": "string", "enum": ["PENDING", "PAID", "CANCELED"] },    "createdAt": { "type": "string", "format": "date-time" },    "customer": {      "type": "object",      "additionalProperties": false,      "required": ["id", "email"],      "properties": {        "id": { "type": "string", "minLength": 1 },        "email": { "type": "string", "format": "email" }      }    },    "items": {      "type": "array",      "minItems": 1,      "items": {        "type": "object",        "additionalProperties": false,        "required": ["sku", "quantity", "price"],        "properties": {          "sku": { "type": "string", "minLength": 1 },          "quantity": { "type": "integer", "minimum": 1 },          "price": { "type": "number", "minimum": 0 }        }      }    },    "total": { "type": "number", "minimum": 0 }  }}

Alguns pontos importantes desse schema: additionalProperties: false impede campos extras. Isso é útil quando você quer detectar qualquer mudança no payload, mas pode ser rígido demais se a API evolui adicionando campos compatíveis. Uma alternativa é permitir extras no topo e ser mais rígido apenas em objetos críticos, ou validar apenas um subconjunto essencial.

Estratégias de contrato: rígido, tolerante e por camadas

Nem todo time escolhe o mesmo nível de rigidez. Em geral, você pode adotar três estratégias:

  • Contrato rígido: bloqueia campos extras e valida enumerações e formatos estritamente. Bom para APIs internas com forte governança e para detectar mudanças rapidamente. Pode gerar manutenção frequente quando a API adiciona campos.

  • Contrato tolerante: valida apenas campos essenciais e tipos principais, permitindo propriedades extras. Bom para APIs públicas ou em evolução, onde adicionar campos não deve quebrar consumidores.

  • Contrato por camadas: combina os dois. Por exemplo, valida rigidamente objetos internos usados pelo front-end e valida de forma tolerante metadados e campos opcionais.

Uma prática útil é separar “campos obrigatórios para o cliente” de “campos que a API pode adicionar sem impacto”. Assim, você evita falsos positivos e mantém o teste focado no que realmente quebra integrações.

Passo a passo prático: validando campos obrigatórios e tipos sem schema

Em alguns cenários, você pode começar com validações diretas de presença e tipo, especialmente quando o payload é pequeno ou quando você ainda não tem um schema consolidado. O objetivo aqui é criar um conjunto mínimo de garantias.

Passo 1: definir a lista de campos obrigatórios

Liste os campos que não podem faltar e, para objetos aninhados, defina o caminho. Exemplo: id, status, createdAt, customer.id, items, items[0].sku.

Passo 2: validar presença e não nulidade

Use asserts para garantir que o campo existe e não é null. Para strings, valide também minLength (não vazio). Para arrays, valide que é array e, se necessário, que tem ao menos um item.

Passo 3: validar tipos e formatos básicos

Valide que números são números, inteiros são inteiros, boolean é boolean. Para datas, valide padrão ISO-8601 com regex (quando não estiver usando schema). Para enum, valide que o valor está em uma lista permitida.

Exemplo de testes (sem schema) em JavaScript no Postman

const body = pm.response.json();pm.test("Contrato: campos obrigatórios no topo", () => {  pm.expect(body).to.have.property("id");  pm.expect(body.id).to.be.a("string").and.not.empty;  pm.expect(body).to.have.property("status");  pm.expect(body.status).to.be.a("string");  pm.expect(["PENDING","PAID","CANCELED"]).to.include(body.status);  pm.expect(body).to.have.property("createdAt");  pm.expect(body.createdAt).to.be.a("string").and.not.empty;  pm.expect(body).to.have.property("customer");  pm.expect(body.customer).to.be.an("object");  pm.expect(body.customer).to.have.property("id");  pm.expect(body.customer.id).to.be.a("string").and.not.empty;  pm.expect(body).to.have.property("items");  pm.expect(body.items).to.be.an("array");});pm.test("Contrato: itens do pedido", () => {  pm.expect(body.items.length).to.be.greaterThan(0);  body.items.forEach((it, idx) => {    pm.expect(it, `item[${idx}]`).to.have.property("sku");    pm.expect(it.sku).to.be.a("string").and.not.empty;    pm.expect(it).to.have.property("quantity");    pm.expect(it.quantity).to.be.a("number");    pm.expect(Number.isInteger(it.quantity)).to.eql(true);    pm.expect(it.quantity).to.be.at.least(1);    pm.expect(it).to.have.property("price");    pm.expect(it.price).to.be.a("number");    pm.expect(it.price).to.be.at.least(0);  });});

Esse modelo é útil para começar, mas tende a crescer e ficar repetitivo. Quando você perceber que está copiando as mesmas validações em vários endpoints, é um sinal de que vale migrar para JSON Schema e reutilização de schemas.

Passo a passo prático: validação com JSON Schema no Postman

Para validar com JSON Schema no Postman, você normalmente usa uma biblioteca de validação. O Postman não inclui um validador completo de JSON Schema por padrão, então a abordagem mais comum é adicionar uma biblioteca como Ajv (Another JSON Schema Validator) via pm.sendRequest para buscar o script, ou embutir uma versão minificada no projeto, ou ainda usar um pacote já disponível no ambiente de execução do Postman quando aplicável. O ponto central do passo a passo é: definir o schema, carregar o validador e executar a validação, falhando o teste quando houver erros.

Passo 1: escrever o schema e decidir o nível de rigidez

Comece com um schema que cubra os campos essenciais. Decida se vai usar additionalProperties: false. Em APIs que evoluem com frequência, uma boa prática é começar tolerante e endurecer gradualmente em partes críticas.

Passo 2: armazenar o schema de forma reutilizável

Em vez de colar o schema em cada request, armazene-o em um local reutilizável: por exemplo, como uma variável (em nível de coleção) contendo o JSON do schema, ou como um arquivo referenciado em um fluxo de execução (quando sua estratégia de automação permitir). O objetivo é manter um único ponto de manutenção.

Passo 3: validar a resposta contra o schema

O teste deve: obter o JSON da resposta, compilar o schema e validar. Se inválido, exibir erros legíveis, indicando o caminho do campo que falhou.

Exemplo de validação com Ajv (padrão de uso)

O exemplo abaixo mostra o padrão de código. Dependendo do seu setup, você precisará garantir que o objeto Ajv esteja disponível (por exemplo, carregado previamente em scripts compartilhados).

// Exemplo de padrão: requer Ajv disponível no runtimeconst Ajv = require("ajv");const ajv = new Ajv({ allErrors: true, strict: false });const schema = {  type: "object",  required: ["id","status","createdAt","customer","items","total"],  properties: {    id: { type: "string", minLength: 1 },    status: { type: "string", enum: ["PENDING","PAID","CANCELED"] },    createdAt: { type: "string", format: "date-time" },    customer: {      type: "object",      required: ["id","email"],      properties: {        id: { type: "string", minLength: 1 },        email: { type: "string", format: "email" }      }    },    items: {      type: "array",      items: {        type: "object",        required: ["sku","quantity","price"],        properties: {          sku: { type: "string", minLength: 1 },          quantity: { type: "integer", minimum: 1 },          price: { type: "number", minimum: 0 }        }      }    },    total: { type: "number", minimum: 0 }  }};pm.test("Contrato: resposta segue JSON Schema", () => {  const data = pm.response.json();  const validate = ajv.compile(schema);  const ok = validate(data);  if (!ok) {    const errors = (validate.errors || []).map(e => `${e.instancePath} ${e.message}`).join(" | ");    pm.expect.fail(`Schema inválido: ${errors}`);  }});

Repare em duas escolhas: allErrors: true para listar várias falhas de uma vez, e strict: false para reduzir quebras por detalhes de compatibilidade entre drafts e formatos. Em um ambiente mais controlado, você pode ativar modo estrito e padronizar o draft do schema.

Validações avançadas de contrato com JSON Schema

Enums, padrões e formatos

Use enum para estados e categorias. Use pattern para validar strings com regex (por exemplo, SKU). Use format para e-mail e date-time, lembrando que o suporte a format depende do validador e de plugins de formatos. Se o formato não estiver sendo verificado, você pode complementar com asserts específicos.

oneOf, anyOf e nullable

Às vezes um campo pode aceitar mais de uma forma. Exemplo: discount pode ser objeto quando aplicado e pode ser ausente quando não aplicado. Em schema, você pode modelar isso com oneOf ou anyOf. Para aceitar null, você pode definir type como array, por exemplo { "type": ["string","null"] }, ou usar convenções do seu draft/validador.

Regras condicionais

Contratos reais têm condicionais: se status é CANCELED, então canceledAt deve existir; se o pagamento é cartão, então card.last4 deve existir. JSON Schema suporta isso com if, then e else. Esse tipo de validação reduz a necessidade de muitos asserts manuais e documenta o contrato de forma executável.

Exemplo de condicional no schema

{  "type": "object",  "required": ["status"],  "properties": {    "status": { "type": "string", "enum": ["PENDING","PAID","CANCELED"] },    "canceledAt": { "type": "string", "format": "date-time" }  },  "if": {    "properties": { "status": { "const": "CANCELED" } },    "required": ["status"]  },  "then": {    "required": ["canceledAt"]  }}

Como lidar com listas: contrato de itens e paginação

Endpoints de listagem costumam retornar arrays de objetos e, frequentemente, metadados de paginação. O contrato deve validar: o tipo do container (array ou objeto com data), o tipo de cada item, e a consistência dos metadados (por exemplo, page inteiro, pageSize inteiro, total número não negativo). Um erro comum é a API retornar data como objeto quando há apenas um resultado, em vez de array com um item; isso quebra consumidores que sempre iteram.

Em JSON Schema, valide o array com items e, se necessário, minItems. Para paginação, valide que data é array e que meta contém os campos esperados. Se a API permite lista vazia, não use minItems ou defina como 0.

Erros também têm contrato: padronização de respostas de falha

Validação de contrato não é só para respostas 200. Respostas de erro devem ser previsíveis: um consumidor precisa saber onde encontrar mensagem, código interno, detalhes por campo e correlação. Um contrato de erro típico inclui: error.code, error.message, error.details (opcional), e traceId (opcional). Validar isso evita que um endpoint retorne um HTML de erro, uma string solta, ou um formato diferente do padrão.

Uma abordagem prática é ter um schema de erro reutilizável e aplicá-lo a todos os testes que esperam falha (por exemplo, 400, 401, 403, 404, 422, 500). Assim, você garante consistência e melhora a diagnóstica.

Exemplo de schema de erro

{  "type": "object",  "required": ["error"],  "properties": {    "error": {      "type": "object",      "required": ["code","message"],      "properties": {        "code": { "type": "string", "minLength": 1 },        "message": { "type": "string", "minLength": 1 },        "details": {          "type": "array",          "items": {            "type": "object",            "required": ["field","issue"],            "properties": {              "field": { "type": "string" },              "issue": { "type": "string" }            }          }        }      }    },    "traceId": { "type": "string" }  }}

Boas práticas para manter schemas úteis e fáceis de evoluir

Comece pelo essencial e aumente cobertura

Se você tentar modelar 100% do payload desde o início, a manutenção pode ficar pesada. Comece com campos críticos e tipos, depois adicione regras como enum, formatos e condicionais. A cada bug real encontrado, fortaleça o schema naquela área.

Reutilize partes com definições

Quando vários endpoints compartilham estruturas (por exemplo, um objeto customer), crie schemas reutilizáveis e referencie com $ref quando seu validador e sua estratégia de armazenamento suportarem. Isso reduz divergências e facilita atualização.

Decida conscientemente sobre campos extras

additionalProperties: false é poderoso, mas pode gerar ruído quando a API adiciona campos compatíveis. Uma alternativa é permitir extras no topo e bloquear extras apenas em objetos internos críticos, ou usar testes separados: um teste “compatibilidade” tolerante e outro “contrato estrito” para ambientes controlados.

Mensagens de erro legíveis

Quando um schema falha, o valor do teste está no diagnóstico. Sempre que possível, agregue e imprima os erros com caminho do campo. Isso acelera a correção e evita que o teste vire apenas um “vermelho” sem contexto.

Agora responda o exercício sobre o conteúdo:

Em validações de contrato, qual prática ajuda a evitar que clientes quebrem quando a API evolui sem intenção?

Você acertou! Parabéns, agora siga para a próxima página

Você errou! Tente novamente.

Validação de contrato verifica se a resposta mantém o formato acordado: presença de campos essenciais, tipos e estrutura. Isso detecta mudanças involuntárias (campo removido, tipo trocado, estrutura alterada) que podem quebrar integrações mesmo com status 200.

Próximo capitúlo

Testes de fluxo e encadeamento de requests: captura de tokens e reutilização de IDs

Arrow Right Icon
Baixe o app para ganhar Certificação grátis e ouvir os cursos em background, mesmo com a tela desligada.