Escolha o nível de teste pela pergunta

Um teste unitário pergunta se uma regra produz o resultado esperado para entradas e dependências controladas. Testes de integração verificam a composição real de HTTP, banco ou broker. Testes de desempenho medem latência, taxa de erros e capacidade sob carga. Não use um único tipo como substituto dos demais.

Priorize comportamentos importantes e limites de falha, não a quantidade de testes nem a cobertura de linhas isolada. Veja o guia completo de testes de software e os aprofundamentos em testes unitários em .NET e testes de desempenho com k6. Para integração HTTP, consulte ASP.NET Core e containers; para limites entre módulos, testes de arquitetura.

Unidades pequenas, dependências explícitas

Teste funções de domínio e casos de uso com valores legíveis e asserções sobre comportamento. Se um caso de uso depende de um repositório, use uma implementação falsa simples quando isso tornar o cenário mais claro; não substitua toda dependência por mocks se o teste acabar verificando apenas chamadas internas. Reserve o banco real para testes de integração.

[Fact]
public async Task Rejeita_pedido_sem_itens()
{
    var servico = CriarServicoComDependenciasControladas();
    var resultado = await servico.CriarAsync(
        new CriarPedidoRequest([]), CancellationToken.None);

    Assert.False(resultado.Sucesso);
    Assert.Equal("Pedido sem itens.", resultado.Erro);
}

O exemplo é ilustrativo; adapte nomes e retorno ao domínio real. Testes devem ser determinísticos, independentes e não depender de horário, rede, ordem de execução ou estado compartilhado. Use nomes que expressem cenário e resultado.

Use k6 para exercitar um contrato HTTP

Um teste de carga precisa de um ambiente e dados autorizados para teste. Comece com volume baixo, aumente gradualmente e coordene a execução com quem opera a infraestrutura. Nunca direcione um teste intensivo a produção ou a serviços de terceiros sem autorização explícita.

import http from 'k6/http';
import { check } from 'k6';

const baseUrl = __ENV.BASE_URL || 'http://localhost:5000';

export const options = {
  vus: 10,
  duration: '30s',
  thresholds: {
    http_req_failed: ['rate<0.01'],
    http_req_duration: ['p(95)<500'],
  },
};

export default function () {
  const resposta = http.get(`${baseUrl}/api/catalogo`);
  check(resposta, { 'status é 200': (r) => r.status === 200 });
}

Os thresholds são apenas exemplo, não metas universais. Defina-os a partir dos SLOs, do perfil de tráfego e da capacidade que precisa validar. A falha de um threshold torna o resultado do teste malsucedido, mas a interpretação depende também dos dados e do ambiente.

Progrida de smoke test para carga

Confirme primeiro conectividade, autenticação de teste e formato das respostas com uma execução curta. Depois modele rampa de usuários ou taxa de chegada, duração, pausas, dados e cenários representativos. Inclua operações de escrita somente com dados descartáveis e estratégia de limpeza; semear dados é parte do desenho do teste.

Uma taxa de usuários virtuais não equivale automaticamente a uma taxa de requisições específica: cada iteração leva tempo. Observe throughput, percentis de latência, erros e saturação do servidor, banco e dependências. Evite que o próprio gerador de carga seja o gargalo.

Interprete antes de otimizar

Compare execuções sob condições semelhantes, registre versão, configuração, volume de dados e perfil de carga e repita para entender variabilidade. Correlacione os resultados com traces, consultas e métricas do serviço para encontrar o gargalo. Não aumente threads ou timeouts só para melhorar um número se isso transfere saturação para outra dependência.

Veja como usar logs, métricas e traces na investigação e como acompanhar o processo com pipelines Jenkins e Kubernetes.

Documentação e leituras relacionadas