O que um teste de integração deve responder?
Um teste unitário pode confirmar que uma regra calcula o preço esperado. Mas ele não garante que o endpoint foi registrado, que o JSON foi convertido corretamente ou que a consulta funciona no banco usado pela aplicação. Um teste de integração cobre uma fronteira real entre componentes.
Isso não significa testar tudo por HTTP. Escolha um fluxo importante — por exemplo, criar um pedido — e verifique comportamento observável: resposta HTTP, dados persistidos e efeitos relevantes. Evite afirmar que uma implementação interna específica foi chamada; isso prende o teste ao desenho, não ao contrato.
WebApplicationFactory sobe a aplicação de teste
ASP.NET Core oferece WebApplicationFactory<TEntryPoint> para iniciar a aplicação em memória e permitir requisições com um cliente HTTP de teste. A configuração de testes pode trocar endpoints externos por alternativas controladas, configurar um banco temporário ou fornecer opções específicas.
public sealed class PedidosApiTests
: IClassFixture<WebApplicationFactory<Program>>
{
private readonly HttpClient _client;
public PedidosApiTests(
WebApplicationFactory<Program> factory)
{
_client = factory.CreateClient();
}
[Fact]
public async Task CriarPedido_retornaCreated()
{
var response = await _client.PostAsJsonAsync(
"/pedidos",
new { clienteId = "cliente-1", itens = new[] { "item-1" } });
Assert.Equal(HttpStatusCode.Created, response.StatusCode);
}
}O exemplo é esquemático: o modelo de request, autenticação e montagem de dados dependem da aplicação. Em projetos com top-level statements, pode ser necessário expor o tipo Program para o assembly de testes. O princípio é reutilizar a composição real, alterando somente as dependências que precisam de isolamento.
Use o banco certo para a pergunta
Um banco em memória pode ser rápido, mas nem sempre reproduz tipos, índices, transações e comportamento do banco de produção. Se o objetivo é validar consultas e migrations, executar o mesmo mecanismo em um container descartável costuma oferecer mais fidelidade. Ferramentas como Testcontainers podem iniciar dependências sob demanda, sem manter um servidor compartilhado entre desenvolvedores.
Use bancos em memória para testes onde a fidelidade relacional não é relevante e o contrato está bem coberto noutro nível. Para comportamento específico do provedor, use o provedor real. Não escolha a ferramenta por hábito; escolha pela falha que o teste pretende detectar.
Isolamento: cada teste deve começar em estado conhecido
Testes que compartilham um banco persistente podem passar isoladamente e falhar em conjunto, ou vice-versa. Prefira uma base temporária por conjunto de testes, transações com estratégia apropriada ou limpeza explícita de dados. Garanta que nomes, IDs e horários não colidam.
Quando há execução paralela, não deixe um teste apagar dados que outro ainda usa. Compartilhar um container é possível, desde que os dados sejam isolados. A velocidade é útil, mas não à custa de resultados não determinísticos.
O que substituir e o que manter real
É razoável substituir serviços caros ou não determinísticos, como um provedor de pagamento externo. O teste pode registrar a solicitação e devolver um resultado conhecido. Mantenha real aquilo cuja integração você quer validar: roteamento, pipeline HTTP, serialização, autenticação configurada ou banco.
Não substitua tudo por mocks: assim o teste vira uma demonstração de que os mocks funcionam. Também não deixe dependências externas reais causar cobranças, enviar e-mails ou exigir credenciais de produção. Um bom limite de teste é explícito e seguro.
Como evitar uma suíte lenta e frágil
- Tenha poucos testes de integração por fluxo crítico; cubra variações de regra com testes mais rápidos.
- Use builders de dados e nomes que revelem a intenção do cenário.
- Espere condições observáveis, não pausas arbitrárias com
Task.Delay. - Configure timeout e descarte dos recursos mesmo quando um teste falhar.
- Registre logs úteis no CI para diagnosticar falhas sem repetir às cegas.
Testes de integração são mais lentos que testes unitários, então vale executá-los em pipeline com dependências reproduzíveis. Um teste que falha intermitentemente reduz a confiança da equipe e deve ser investigado, não simplesmente repetido até passar.
Resumo: teste a fronteira que importa
Combine testes unitários para decisões rápidas e testes de integração para provar que componentes concretos se conectam. WebApplicationFactory valida a aplicação ASP.NET Core sem servidor externo; containers ajudam a aproximar dependências do ambiente real. Para aprofundar organização e limites, leia Clean Architecture e como organizar projetos .NET.