{"id":7977,"date":"2025-10-14T22:07:18","date_gmt":"2025-10-15T01:07:18","guid":{"rendered":"https:\/\/www.afonsoft.com.br\/?p=7977"},"modified":"2025-10-14T22:07:21","modified_gmt":"2025-10-15T01:07:21","slug":"comprehensive-guide-to-c-testing-with-xunit-and-asp-net","status":"publish","type":"post","link":"https:\/\/www.afonsoft.com.br\/index.php\/2025\/10\/14\/comprehensive-guide-to-c-testing-with-xunit-and-asp-net\/","title":{"rendered":"Comprehensive Guide to C# Testing with xUnit and ASP.NET"},"content":{"rendered":"<h1>Comprehensive Guide to C# Testing with xUnit and ASP.NET<\/h1>\n<h2>Enhance Your ASP.NET Applications with Robust Unit Tests Using C# and xUnit<\/h2>\n<p><b>Guia Completo para Testes em C# com xUnit e ASP.NET<\/b><br \/>\n<i>Aprimore suas Aplica\u00e7\u00f5es ASP.NET com Testes de Unidade Robustos Usando C# e xUnit<\/i><\/p>\n<p>Para come\u00e7armos a jornada de testes em C# com xUnit, \u00e9 fundamental configurar o ambiente corretamente. Primeiramente, crie um projeto ASP.NET (Web API \u00e9 um bom exemplo) no Visual Studio ou no seu IDE preferido. Em seguida, adicione um novo projeto do tipo &#8220;Projeto de Teste xUnit (.NET)&#8221;. No projeto de teste, instale os seguintes pacotes NuGet: `xunit`, `xunit.runner.visualstudio` e `Microsoft.NET.Test.Sdk`. Certifique-se de que a vers\u00e3o do .NET SDK instalada no seu ambiente \u00e9 compat\u00edvel com o seu projeto ASP.NET. Para vincular o projeto de teste ao projeto principal, adicione uma refer\u00eancia do projeto de teste ao projeto ASP.NET. Ap\u00f3s a instala\u00e7\u00e3o dos pacotes, verifique se o test runner (Visual Studio Test Explorer ou dotnet test na linha de comando) reconhece os testes.<\/p>\n<hr>\n<p>Vamos agora escrever o nosso primeiro teste unit\u00e1rio com xUnit. Suponha que temos uma classe `Calculadora` com um m\u00e9todo `Adicionar`:<\/p>\n<p>&#8220;`csharp<br \/>\npublic class Calculadora<br \/>\n{<br \/>\n    public int Adicionar(int a, int b)<br \/>\n    {<br \/>\n        return a + b;<br \/>\n    }<br \/>\n}<br \/>\n&#8220;`<\/p>\n<p>Para testar este m\u00e9todo, criamos uma classe de teste `CalculadoraTests`:<\/p>\n<p>&#8220;`csharp<br \/>\nusing Xunit;<\/p>\n<p>public class CalculadoraTests<br \/>\n{<br \/>\n    [Fact]<br \/>\n    public void Adicionar_DeveRetornarASoma_QuandoValoresForemInformados()<br \/>\n    {<br \/>\n        \/\/ Arrange<br \/>\n        var calculadora = new Calculadora();<br \/>\n        int a = 5;<br \/>\n        int b = 3;<br \/>\n        int resultadoEsperado = 8;<\/p>\n<p>        \/\/ Act<br \/>\n        int resultadoAtual = calculadora.Adicionar(a, b);<\/p>\n<p>        \/\/ Assert<br \/>\n        Assert.Equal(resultadoEsperado, resultadoAtual);<br \/>\n    }<br \/>\n}<br \/>\n&#8220;`<\/p>\n<p>Observe o padr\u00e3o Arrange, Act, Assert (AAA). `[Fact]` indica que este \u00e9 um m\u00e9todo de teste. `Assert.Equal` verifica se o valor atual \u00e9 igual ao valor esperado.<\/p>\n<hr>\n<p>xUnit oferece uma gama rica de atributos e asser\u00e7\u00f5es para diferentes cen\u00e1rios de teste. O atributo `[Fact]` j\u00e1 foi apresentado, ideal para testes simples. `[Theory]` permite executar o mesmo teste com diferentes conjuntos de dados. `[InlineData]` fornece dados diretamente no atributo: `[Theory][InlineData(2, 3, 5)][InlineData(4, 6, 10)]`. Para dados mais complexos, `[MemberData]` e `[ClassData]` referenciam propriedades ou classes que retornam os dados.  `[Trait]` permite adicionar metadados aos testes, facilitando a categoriza\u00e7\u00e3o e filtragem. As asser\u00e7\u00f5es incluem `Assert.NotEqual`, `Assert.True`, `Assert.False`, `Assert.Null`, `Assert.NotNull`, `Assert.Empty`, `Assert.NotEmpty`. `Assert.Throws<T>` \u00e9 crucial para verificar se um m\u00e9todo lan\u00e7a a exce\u00e7\u00e3o esperada. Por exemplo, `Assert.Throws<ArgumentException>(() => calculadora.Dividir(10, 0));`.<\/p>\n<hr>\n<p>Testar c\u00f3digo ass\u00edncrono exige aten\u00e7\u00e3o especial. Utilize os modificadores `async` e `await` em seus m\u00e9todos de teste.  Para testar um m\u00e9todo ass\u00edncrono que retorna `Task<T>`, use `await` para obter o resultado: `var resultado = await metodoAssincrono();`. Para verificar exce\u00e7\u00f5es em c\u00f3digo ass\u00edncrono, use `Assert.ThrowsAsync<T>`. Por exemplo:<\/p>\n<p>&#8220;`csharp<br \/>\n[Fact]<br \/>\npublic async Task MetodoAssincrono_DeveLancarExcecao_QuandoCondicaoNaoSatisfazer()<br \/>\n{<br \/>\n    await Assert.ThrowsAsync<InvalidOperationException>(async () => await _servico.MetodoAssincrono(argumentoInvalido));<br \/>\n}<br \/>\n&#8220;`<\/p>\n<p>Lembre-se de configurar timeouts apropriados para evitar testes que se estendem indefinidamente. CancellationToken pode ser usado para cancelar opera\u00e7\u00f5es ass\u00edncronas demoradas.<\/p>\n<hr>\n<p>O mocking \u00e9 fundamental para isolar a unidade que est\u00e1 sendo testada, substituindo depend\u00eancias externas por objetos controlados. Frameworks como Moq e NSubstitute facilitam a cria\u00e7\u00e3o e configura\u00e7\u00e3o de mocks. Para criar um mock com Moq, use `var mockDependencia = new Mock<IDependencia>();`.  Voc\u00ea pode ent\u00e3o configurar o comportamento do mock: `mockDependencia.Setup(x => x.Metodo(It.IsAny<int>())).Returns(true);`. Injete o mock na classe sob teste atrav\u00e9s do construtor ou propriedade.  Para verificar se um m\u00e9todo do mock foi chamado, use `mockDependencia.Verify(x => x.Metodo(It.Is<int>(i => i > 0)), Times.Once);`.  O `Times.Once` garante que o m\u00e9todo foi chamado exatamente uma vez.<\/p>\n<hr>\n<p>Para testar controllers ASP.NET, \u00e9 essencial simular o ambiente HTTP. Utilize `Mock<IHttpContextAccessor>` para mockar o contexto HTTP.  Crie mocks para as depend\u00eancias do controller, como servi\u00e7os e reposit\u00f3rios. Por exemplo: `var mockServico = new Mock<IServico>();`. Configure o comportamento esperado dos mocks: `mockServico.Setup(x => x.ObterDados()).Returns(new List<string> { &#8220;dado1&#8221;, &#8220;dado2&#8221; });`. Ao testar m\u00e9todos GET, verifique o status code retornado (Assert.IsType<OkObjectResult>) e o conte\u00fado da resposta. Para m\u00e9todos POST, PUT e DELETE, verifique se as opera\u00e7\u00f5es foram realizadas corretamente (e.g., se um registro foi criado ou atualizado) e se os status codes apropriados foram retornados (e.g., 201 Created, 204 No Content). Teste tamb\u00e9m a valida\u00e7\u00e3o do modelo, verificando se erros de valida\u00e7\u00e3o retornam o status code 400 Bad Request.<\/p>\n<hr>\n<p>Testes orientados a dados, ou Theories, s\u00e3o ideais para validar o comportamento de um m\u00e9todo com m\u00faltiplos conjuntos de entrada. Utilize o atributo `[Theory]` em conjunto com `[InlineData]` para fornecer valores diretamente no c\u00f3digo:<\/p>\n<p>&#8220;`csharp<br \/>\n[Theory]<br \/>\n[InlineData(10, 2, 5)]<br \/>\n[InlineData(15, 3, 5)]<br \/>\n[InlineData(20, 4, 5)]<br \/>\npublic void Dividir_DeveRetornarResultadoCorreto(int dividendo, int divisor, int resultadoEsperado)<br \/>\n{<br \/>\n    var calculadora = new Calculadora();<br \/>\n    Assert.Equal(resultadoEsperado, calculadora.Dividir(dividendo, divisor));<br \/>\n}<br \/>\n&#8220;`<\/p>\n<p>Para conjuntos de dados mais complexos, utilize `[MemberData]` ou `[ClassData]` para obter os dados de um m\u00e9todo ou classe:<\/p>\n<p>&#8220;`csharp<br \/>\npublic static IEnumerable<object[]> DadosDivisao()<br \/>\n{<br \/>\n    yield return new object[] { 10, 2, 5 };<br \/>\n    yield return new object[] { 15, 3, 5 };<br \/>\n    yield return new object[] { 20, 4, 5 };<br \/>\n}<\/p>\n<p>[Theory]<br \/>\n[MemberData(nameof(DadosDivisao))]<br \/>\npublic void Dividir_DeveRetornarResultadoCorreto_ComMemberData(int dividendo, int divisor, int resultadoEsperado)<br \/>\n{<br \/>\n    var calculadora = new Calculadora();<br \/>\n    Assert.Equal(resultadoEsperado, calculadora.Dividir(dividendo, divisor));<br \/>\n}<br \/>\n&#8220;`<\/p>\n<hr>\n<p>Testes de integra\u00e7\u00e3o validam a intera\u00e7\u00e3o entre diferentes partes da aplica\u00e7\u00e3o, como controllers, servi\u00e7os, reposit\u00f3rios e bancos de dados. Ao contr\u00e1rio dos testes de unidade, os testes de integra\u00e7\u00e3o envolvem componentes reais e suas depend\u00eancias.  Configure um ambiente de teste isolado, utilizando um banco de dados de teste (in-memory ou uma c\u00f3pia do banco de dados de produ\u00e7\u00e3o). Utilize o `WebApplicationFactory<TStartup>` para criar um servidor de teste para sua aplica\u00e7\u00e3o ASP.NET. Inje\u00e7\u00e3o de depend\u00eancia (DI) \u00e9 crucial aqui. Use o `ConfigureServices` m\u00e9todo no `WebApplicationFactory` para substituir as depend\u00eancias reais por vers\u00f5es mockadas ou stubs. Escreva testes que simulem fluxos de usu\u00e1rios reais, verificando se as opera\u00e7\u00f5es no banco de dados, chamadas a APIs externas e filas de mensagens funcionam como esperado.<\/p>\n<hr>\n<p>A escrita de testes leg\u00edveis e mant\u00edveis \u00e9 crucial para o sucesso a longo prazo de um projeto. Nomes de testes devem ser descritivos e indicar claramente o que est\u00e1 sendo testado (ex: `Metodo_QuandoCondicao_EntaoResultado`). Utilize o padr\u00e3o AAA (Arrange, Act, Assert) de forma consistente para estruturar seus testes. Evite duplica\u00e7\u00e3o de c\u00f3digo criando m\u00e9todos auxiliares para tarefas repetitivas. Mantenha os testes independentes uns dos outros para evitar efeitos colaterais e garantir a confiabilidade dos resultados. Pratique Test-Driven Development (TDD), escrevendo os testes antes da implementa\u00e7\u00e3o do c\u00f3digo, para garantir que o c\u00f3digo atenda aos requisitos desde o in\u00edcio. Refatore seus testes regularmente para remover c\u00f3digo duplicado, melhorar a legibilidade e manter<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Comprehensive Guide to C# Testing with xUnit and ASP.NET Enhance Your ASP.NET Applications with Robust Unit Tests Using C# and xUnit Guia Completo para Testes em C# com xUnit e ASP.NET Aprimore suas Aplica\u00e7\u00f5es ASP.NET com Testes de Unidade Robustos Usando C# e xUnit Para come\u00e7armos a jornada de testes em C# com xUnit, \u00e9 [&hellip;]<\/p>\n","protected":false},"author":3,"featured_media":7978,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-7977","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-geral"],"_links":{"self":[{"href":"https:\/\/www.afonsoft.com.br\/index.php\/wp-json\/wp\/v2\/posts\/7977","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.afonsoft.com.br\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.afonsoft.com.br\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.afonsoft.com.br\/index.php\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/www.afonsoft.com.br\/index.php\/wp-json\/wp\/v2\/comments?post=7977"}],"version-history":[{"count":1,"href":"https:\/\/www.afonsoft.com.br\/index.php\/wp-json\/wp\/v2\/posts\/7977\/revisions"}],"predecessor-version":[{"id":7979,"href":"https:\/\/www.afonsoft.com.br\/index.php\/wp-json\/wp\/v2\/posts\/7977\/revisions\/7979"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.afonsoft.com.br\/index.php\/wp-json\/wp\/v2\/media\/7978"}],"wp:attachment":[{"href":"https:\/\/www.afonsoft.com.br\/index.php\/wp-json\/wp\/v2\/media?parent=7977"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.afonsoft.com.br\/index.php\/wp-json\/wp\/v2\/categories?post=7977"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.afonsoft.com.br\/index.php\/wp-json\/wp\/v2\/tags?post=7977"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}