<p><strong>Meta Description:</strong> Domine testes de API: REST, GraphQL, microservices. Aprenda a usar Postman, RestAssured, pytest e automação de testes de API para garantir qualidade de integrações.</p>

<hr>
<h2>Introdução aos Testes de API</h2>

<p>APIs (Application Programming Interfaces) são a espinha dorsal da arquitetura moderna de software. Testar APIs de forma eficaz garante que os serviços se comuniquem corretamente, retornem dados esperados e tratem erros apropriadamente.</p>

<h3>Por Que Testar APIs?</h3>

<figure class="wp-block-table"><table>
<tr><th>Benefício</th><th>Impacto</th></tr>
<tr><td><strong>Detecção precoce de bugs</strong></td><td>APIs são gateway para frontend</td></tr>
<tr><td><strong>Integração confiável</strong></td><td>Microserviços se comunicam via API</td></tr>
<tr><td><strong>Performance</strong></td><td>API performance afeta toda a aplicação</td></tr>
<tr><td><strong>Documentação viva</strong></td><td>Contratos de API testados</td></tr>
<tr><td><strong>Regression testing</strong></td><td>Mudanças não quebram integrações</td></tr>
</table></figure>

<hr>
<h2>Tipos de Testes de API</h2>

<h3>1. Testes de Funcionalidade</h3>

<p>Verificam se a API funciona conforme especificado.</p>

<pre><code>import requests
import pytest

<p>BASE_URL = "https://api.exemplo.com/v1"</p>

<p>class TestUsuarioAPI:</p>

<p>def test_criar_usuario(self):<br>        """Deve criar usuário com sucesso"""<br>        payload = {<br>            "nome": "João Silva",<br>            "email": "joao@exemplo.com",<br>            "senha": "Senha@123"<br>        }</p>

<p>response = requests.post(<br>            f"{BASE_URL}/usuarios",<br>            json=payload,<br>            headers={"Content-Type": "application/json"}<br>        )</p>

<p>assert response.status_code == 201<br>        data = response.json()<br>        assert data["nome"] == payload["nome"]<br>        assert data["email"] == payload["email"]<br>        assert "id" in data<br>        assert "senha" not in data  # Não retorna senha!</p>

<p>def test_criar_usuario_email_duplicado(self):<br>        """Deve falhar quando email já existe"""<br>        payload = {<br>            "nome": "João Silva",<br>            "email": "joao@exemplo.com",  # Já existe<br>            "senha": "Senha@123"<br>        }</p>

<p>response = requests.post(<br>            f"{BASE_URL}/usuarios",<br>            json=payload<br>        )</p>

<p>assert response.status_code == 409  # Conflict<br>        assert "email" in response.json()["erro"].lower()</p>

<p>def test_buscar_usuario_por_id(self):<br>        """Deve retornar usuário existente"""<br>        response = requests.get(f"{BASE_URL}/usuarios/1")</p>

<p>assert response.status_code == 200<br>        data = response.json()<br>        assert data["id"] == 1<br>        assert "email" in data</p>

<p>def test_buscar_usuario_inexistente(self):<br>        """Deve retornar 404 para usuário inexistente"""<br>        response = requests.get(f"{BASE_URL}/usuarios/99999")</p>

<p>assert response.status_code == 404<br></code></pre></p>

<h3>2. Testes de Contrato</h3>

<p>Verificam se a API está em conformidade com seu contrato (OpenAPI/Swagger).</p>

<pre><code>import pytest
from jsonschema import validate

<p>schema = {<br>    "type": "object",<br>    "required": ["id", "nome", "email"],<br>    "properties": {<br>        "id": {"type": "integer"},<br>        "nome": {"type": "string", "minLength": 1},<br>        "email": {"type": "string", "format": "email"}<br>    }<br>}</p>

<p>def test_usuario_response_match_schema():<br>    response = requests.get(f"{BASE_URL}/usuarios/1")<br>    data = response.json()</p>

<p>validate(instance=data, schema=schema)<br></code></pre></p>

<h3>3. Testes de Performance</h3>

<pre><code>import time
import concurrent.futures

<p>def test_api_response_time():<br>    """Tempo de resposta deve ser &lt; 500ms&quot;&quot;&quot;<br>    start = time.time()<br>    response = requests.get(f"{BASE_URL}/usuarios/1")<br>    elapsed = (time.time() - start) * 1000</p>

<p>assert response.status_code == 200<br>    assert elapsed &lt; 500, f&quot;Response took {elapsed}ms&quot;</p>

<p>def test_concurrent_requests():<br>    """API deve suportar 100 requisições simultâneas"""<br>    def make_request():<br>        response = requests.get(f"{BASE_URL}/health")<br>        return response.status_code == 200</p>

<p>with concurrent.futures.ThreadPoolExecutor(max_workers=100) as executor:<br>        futures = [executor.submit(make_request) for _ in range(100)]<br>        results = [f.result() for f in concurrent.futures.as_completed(futures)]</p>

<p>success_rate = sum(results) / len(results)<br>    assert success_rate &gt; 0.95, f"Only {success_rate*100}{6727d158e4474b0847515bd23a8ee4bebd5f1aac7a18bc968e51d8985dccb442} succeeded"<br></code></pre></p>

<h3>4. Testes de Segurança</h3>

<pre><code>def test_sql_injection_prevention():
    """API deve prevenir SQL injection"""
    payload = {"email": "admin' OR '1'='1"}
    response = requests.post(f"{BASE_URL}/usuarios", json=payload)

<p># Não deve retornar dados sensíveis<br>    assert response.status_code in [400, 422]</p>

<p>def test_xss_in_input(self):<br>    """API deve sanitizar inputs"""<br>    payload = {"nome": "alert('xss')"}<br>    response = requests.post(f"{BASE_URL}/usuarios", json=payload)</p>

<p>if response.status_code == 201:<br>        data = response.json()<br>        assert "" not in data["nome"]</p>

<p>def test_rate_limiting(self):<br>    """API deve limitar requisições excessivas"""<br>    responses = []<br>    for _ in range(105):  # Limite é 100<br>        response = requests.get(f"{BASE_URL}/usuarios/1")<br>        responses.append(response.status_code)</p>

<p># Últimas 5 devem retornar 429 (Too Many Requests)<br>    assert 429 in responses[-5:]<br></code></pre></p>

<hr>
<h2>GraphQL: Testando APIs Modernas</h2>

<h3>Queries</h3>

<pre><code>import requests

<p>GRAPHQL_URL = "https://api.exemplo.com/graphql"</p>

<p>class TestGraphQL:</p>

<p>def test_query_basica(self):<br>        """Deve buscar dados com query GraphQL"""<br>        query = """<br>        query {<br>            usuario(id: 1) {<br>                id<br>                nome<br>                email<br>            }<br>        }<br>        """</p>

<p>response = requests.post(<br>            GRAPHQL_URL,<br>            json={"query": query}<br>        )</p>

<p>assert response.status_code == 200<br>        data = response.json()<br>        assert "data" in data<br>        assert "usuario" in data["data"]<br>        assert data["data"]["usuario"]["nome"]</p>

<p>def test_query_com_parametros(self):<br>        """Deve filtrar com argumentos"""<br>        query = """<br>        query($limite: Int) {<br>            produtos(limit: $limite) {<br>                id<br>                nome<br>                preco<br>            }<br>        }<br>        """</p>

<p>response = requests.post(<br>            GRAPHQL_URL,<br>            json={<br>                "query": query,<br>                "variables": {"limite": 5}<br>            }<br>        )</p>

<p>data = response.json()<br>        assert len(data["data"]["produtos"]) &lt;= 5</p>

<p>def test_mutation_inserir(self):<br>        """Deve criar dado com mutation"""<br>        mutation = """<br>        mutation {<br>            criarUsuario(input: {<br>                nome: "Maria Silva"<br>                email: "maria@exemplo.com"<br>                senha: "Senha@123"<br>            }) {<br>                id<br>                nome<br>                email<br>            }<br>        }<br>        """</p>

<p>response = requests.post(GRAPHQL_URL, json={"query": mutation})</p>

<p>assert response.status_code == 200<br>        data = response.json()<br>        assert "errors" not in data<br>        assert data["data"]["criarUsuario"]["nome"] == "Maria Silva"<br></code></pre></p>

<h3>Introspection para Validação</h3>

<pre><code>def test_graphql_introspection():
    """Deve permitir introspection do schema"""
    query = """
    {
        __schema {
            types {
                name
                kind
            }
        }
    }
    """

<p>response = requests.post(GRAPHQL_URL, json={"query": query})<br>    data = response.json()</p>

<p>assert "data" in data<br>    type_names = [t["name"] for t in data["data"]["__schema"]["types"]]<br>    assert "Query" in type_names<br></code></pre></p>

<hr>
<h2>Microserviços: Testando em Ambientes Distribuídos</h2>

<h3>Service Mesh Testing</h3>

<pre><code>┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│   API GW    │────▶│   Auth Svc  │────▶│  User Svc   │
│   :8080     │     │   :8081     │     │   :8082     │
└─────────────┘     └─────────────┘     └─────────────┘
       │                                      │
       │                                      │
       ▼                                      ▼
┌─────────────┐                        ┌─────────────┐
│   Product   │                        │  Database   │
│   Svc :8083 │                        │   :5432     │
└─────────────┘                        └─────────────┘
</code></pre>

<h3>Testes de Contrato com Pact</h3>

<pre><code># Provider (microserviço de usuários)
from pact import Consumer, Provider

<p>pact = Consumer('API-Gateway').has_pact_with(Provider('User-Service'))<br>pact.start_service()</p>

<p>try:<br>    # Definir expectativas<br>    (pact<br>     .given('user with id 1 exists')<br>     .upon_receiving('a request for user 1')<br>     .with_request('GET', '/users/1')<br>     .will_respond_with(200, body={<br>         'id': 1,<br>         'name': 'Test User',<br>         'email': 'test@example.com'<br>     })<br>     .verify())</p>

<p># Verificar provider<br>    from pact.matchers import like, Term</p>

<p>(pact<br>     .given('user exists')<br>     .upon_receiving('a request for users')<br>     .with_request('GET', '/users')<br>     .will_respond_with(200, body=[<br>         like({<br>             'id': 1,<br>             'name': Term('w+', 'Test User'),<br>             'email': Term(r'[w.]+@[w.]+', 'test@example.com')<br>         })<br>     ])<br>     .verify())<br>finally:<br>    pact.stop_service()<br></code></pre></p>

<h3>Contrato Consumer-Driven</h3>

<pre><code>// contract.json (gerado pelo consumer)
{
  "consumer": {
    "name": "Order-Service"
  },
  "provider": {
    "name": "User-Service"
  },
  "interactions": [
    {
      "description": "Get user details",
      "request": {
        "method": "GET",
        "path": "/users/1"
      },
      "response": {
        "status": 200,
        "body": {
          "id": 1,
          "name": "John",
          "email": "john@example.com"
        }
      }
    }
  ]
}
</code></pre>

<h3>TestContainers para Microserviços</h3>

<pre><code>import pytest
from testcontainers.postgres import PostgresContainer
from testcontainers.redis import RedisContainer

<p>@pytest.fixture(scope="module")<br>def postgres():<br>    with PostgresContainer("postgres:15") as pg:<br>        yield pg</p>

<p>@pytest.fixture(scope="module")<br>def redis():<br>    with RedisContainer("redis:7") as rd:<br>        yield rd</p>

<p>@pytest.fixture(scope="module")<br>def user_service(postgres, redis):<br>    with DockerCompose(".") as compose:<br>        # Start user-service<br>        compose.exec("user-service", "python", "manage.py", "migrate")<br>        port = compose.expose("user-service", 8080)<br>        yield f"http://localhost:{port}"<br></code></pre></p>

<hr>
<h2>Postman: Collection Runner</h2>

<h3>Collection Structure</h3>

<pre><code>{
  "info": {
    "name": "E-commerce API",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "item": [
    {
      "name": "Auth",
      "item": [
        {
          "name": "Login",
          "event": [
            {
              "listen": "test",
              "script": {
                "exec": [
                  "pm.test('Status 200', () =&gt; {",
                  "  pm.response.to.have.status(200);",
                  "});",
                  "pm.test('Token received', () =&gt; {",
                  "  const jsonData = pm.response.json();",
                  "  pm.collectionVariables.set('token', jsonData.token);",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "url": "{{baseUrl}}/auth/login",
            "body": {
              "mode": "raw",
              "raw": "{"email":"test@test.com","password":"test123"}"
            }
          }
        }
      ]
    },
    {
      "name": "Users",
      "item": [
        {
          "name": "Get All Users",
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [{"key": "token", "value": "{{token}}"}]
            },
            "method": "GET",
            "url": "{{baseUrl}}/users"
          }
        }
      ]
    }
  ]
}
</code></pre>

<h3>Newman CLI</h3>

<pre><code># Executar collection
newman run ecommerce-api.postman_collection.json 
  --environment dev.postman_environment.json 
  --reporters html,json 
  --reporter-json-export results.json 
  --reporter-html-export report.html

<p># Com CI/CD<br>newman run collection.json <br>  --environment ${{ secrets.POSTMAN_ENV }} <br>  --iteration-count 1 <br>  --bail<br></code></pre></p>

<hr>
<h2>Frameworks de Teste de API</h2>

<h3>RestAssured (Java)</h3>

<pre><code>import static io.restassured.RestAssured.*;
import static io.restassured.matcher.RestAssuredMatchers.*;
import static org.hamcrest.Matchers.*;

<p>class UserApiTest {</p>

<p>@BeforeAll<br>    static void setup() {<br>        RestAssured.baseURI = "https://api.exemplo.com/v1";<br>    }</p>

<p>@Test<br>    void criarUsuario() {<br>        given()<br>            .contentType("application/json")<br>            .body("""<br>                {<br>                    "nome": "João Silva",<br>                    "email": "joao@exemplo.com",<br>                    "senha": "Senha@123"<br>                }<br>                """)<br>        .when()<br>            .post("/usuarios")<br>        .then()<br>            .statusCode(201)<br>            .body("nome", equalTo("João Silva"))<br>            .body("email", equalTo("joao@exemplo.com"))<br>            .body("id", notNullValue())<br>            .body("senha", nullValue());<br>    }</p>

<p>@Test<br>    void validarSchema() {<br>        given()<br>            .get("/usuarios/1")<br>        .then()<br>            .statusCode(200)<br>            .body(matchesJsonSchemaInClasspath("schemas/usuario.json"));<br>    }<br>}<br></code></pre></p>

<h3>Pytest + requests</h3>

<pre><code># pytest.ini
[pytest]
addopts = -v --tb=short --strict-markers
markers =
    smoke: Smoke tests
    integration: Integration tests
    slow: Slow running tests

<p># conftest.py<br>import pytest<br>import requests</p>

<p>@pytest.fixture<br>def api_client():<br>    class APIClient:<br>        base_url = "https://api.exemplo.com/v1"</p>

<p>def __init__(self):<br>            self.session = requests.Session()</p>

<p>def create_user(self, data):<br>            return self.session.post(f"{self.base_url}/usuarios", json=data)</p>

<p>def get_user(self, user_id):<br>            return self.session.get(f"{self.base_url}/usuarios/{user_id}")</p>

<p>def delete_user(self, user_id):<br>            return self.session.delete(f"{self.base_url}/usuarios/{user_id}")</p>

<p>return APIClient()</p>

<p>@pytest.fixture<br>def clean_user(api_client):<br>    """Cria usuário para teste e limpa depois"""<br>    user_data = {<br>        "nome": "Test User",<br>        "email": f"test_{uuid4()}@exemplo.com",<br>        "senha": "Senha@123"<br>    }<br>    response = api_client.create_user(user_data)<br>    user_id = response.json()["id"]</p>

<p>yield user_id</p>

<p># Cleanup<br>    api_client.delete_user(user_id)</p>

<p># test_api.py<br>@pytest.mark.integration<br>def test_criar_e_buscar_usuario(api_client):<br>    # Create<br>    user_data = {"nome": "Test", "email": "test@ex.com", "senha": "123"}<br>    response = api_client.create_user(user_data)<br>    assert response.status_code == 201<br>    user_id = response.json()["id"]</p>

<p># Read<br>    response = api_client.get_user(user_id)<br>    assert response.status_code == 200<br>    assert response.json()["nome"] == "Test"<br></code></pre></p>

<hr>
<h2>Conclusão</h2>

<p>Testar APIs é fundamental para garantir qualidade em arquiteturas modernas. As chaves são:<br><ol><li><strong>Cobertura completa</strong> - Funcional, contrato, performance, segurança</li><br><li><strong>Automação</strong> - CI/CD com collections automatizadas</li><br><li><strong>Contratos</strong> - Consumer-driven contracts para microservices</li><br><li><strong>Isolamento</strong> - TestContainers para dependências</li><br><li><strong>Monitoramento</strong> - API health checks em produção</li><br></ol><br><hr><br><h3>FAQ</h3></p>

<p><strong>P: Postman substitui testes de código?</strong>  <br>R: Não. Postman é excelente para exploratory e collections, mas testes em código são mais versionáveis e reutilizáveis.</p>

<p><strong>P: Como testar APIs GraphQL?</strong>  <br>R: Use GraphQL client libraries (gql, strawberry) e valide queries, mutations e subscriptions.</p>

<p><strong>P: Contract testing é necessário?</strong>  <br>R: Essencial em microservices. Consumer-driven contracts (Pact) garantem que mudanças não quebram consumidores.</p>

<p><strong>P: Quanto coverage é suficiente?</strong>  <br>R: 100{6727d158e4474b0847515bd23a8ee4bebd5f1aac7a18bc968e51d8985dccb442} dos endpoints críticos, pelo menos 80{6727d158e4474b0847515bd23a8ee4bebd5f1aac7a18bc968e51d8985dccb442} dos endpoints públicos.</p>