C# Testes (xUnit)
Toda vez que a Ana muda o boletim da escola, ela roda o programa, lança umas notas e confere a média de cabeça. Funciona, mas cansa, e um dia ela vai esquecer de conferir um caso. Um teste automatizado é um pedaço de código que faz essa conferência por ela: chama a função, compara o resultado com o esperado e diz "passou" ou "falhou". Com centenas deles, um único comando, dotnet test, confere o programa inteiro em segundos. Nesta aula você vai escrever testes com o xUnit, a biblioteca de testes mais usada no .NET.
- Por que testar e o que é um teste de unidade.
- A criar um projeto de testes com
dotnet new xunite ligá-lo à biblioteca que ele testa. - A escrever um teste com
[Fact]eAssert.Equal, e a rodar comdotnet test. - A ler um teste que falha (
Expected×Actual) e a consertar o código. - Um teste com vários valores (
[Theory]e[InlineData]) e o teste de exceções (Assert.Throws). --filter,--list-tests, os avisosxUnit2000exUnit1001e a ideia do TDD.
Todas as saídas desta aula são reais, feitas com o .NET 10 SDK (10.0.112) e o xUnit 2.9.3 na máquina fictícia da Ana, na solução ~/projetos/Escola da aula Bibliotecas de Classes. O dotnet new xunit baixa pacotes do NuGet: na primeira vez, precisa de internet e pode demorar.
Por que testar?
Na aula de bibliotecas, a média do Beto, (5,0 + 7,5) ÷ 2 = 6,25, saiu 6,2. A escola arredonda para 6,3. Ninguém percebeu na hora, porque ninguém conferiu esse caso. Um teste é a anotação permanente "com as notas 5,0 e 7,5, a média tem de ser 6,3", conferida pelo computador toda vez.
| Conferir à mão | Teste automatizado |
|---|---|
| Roda o programa, digita, olha a tela | dotnet test |
| Confere os casos de que você se lembra | Confere todos os casos escritos, sempre |
| Minutos a cada mudança | Segundos |
| Uma mudança aqui quebra algo ali sem ninguém ver | O teste daquele caso falha e avisa |
Um teste de unidade (em inglês, unit test) testa um pedaço pequeno do código, como um método, sem tela, sem teclado, sem internet. É por isso que as regras ficam numa biblioteca: o projeto de testes a usa como qualquer outro programa.
Todo teste tem três partes, os três A:
- Arrange (preparar): criar o que o teste precisa (um boletim, algumas notas).
- Act (agir): chamar o que está sendo testado (ler a
Media). - Assert (afirmar): comparar o resultado com o esperado.
1. Criar o projeto de testes
$ dotnet new xunit -o tests/Escola.Notas.Tests O modelo "Projeto de Teste xUnit" foi criado com êxito. Processando ações pós-criação... Restaurando /home/ana/projetos/Escola/tests/Escola.Notas.Tests/Escola.Notas.Tests.csproj: Determinando os projetos a serem restaurados... /home/ana/projetos/Escola/tests/Escola.Notas.Tests/Escola.Notas.Tests.csproj restaurado (em 2,57 seg). A restauração foi bem-sucedida. $ dotnet sln add tests/Escola.Notas.Tests O projeto ‘tests/Escola.Notas.Tests/Escola.Notas.Tests.csproj’ foi adicionado à solução. $ dotnet reference add src/Escola.Notas --project tests/Escola.Notas.Tests A referência ‘..\..\src\Escola.Notas\Escola.Notas.csproj’ foi adicionada ao projeto.
- dotnet new xunit -o tests/Escola.Notas.Tests
- O modelo "Projeto de Teste xUnit". Os testes ficam na pasta
tests, e o nome é o do projeto testado mais.Tests: assim, qualquer pessoa sabe o que ele testa. - dotnet sln add …
- Põe o projeto de testes na solução (veja Soluções), para o
dotnet testda pasta da solução encontrá-lo. - dotnet reference add src/Escola.Notas --project …Tests
- Os testes usam a biblioteca, como o programa usa. O caminho tem
..\..porque a pastatests/Escola.Notas.Testsestá dois níveis abaixo da solução.
O .csproj de testes
$ cat tests/Escola.Notas.Tests/Escola.Notas.Tests.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.4" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4" />
</ItemGroup>
<ItemGroup>
<Using Include="Xunit" />
</ItemGroup>
«… e o ProjectReference para a biblioteca, que o reference add acrescentou …»
</Project>
- <IsPackable>false</IsPackable>
- O
dotnet packnão deve empacotar os testes. - xunit
- A biblioteca de testes: os atributos
[Fact]e[Theory]e a classeAssert. - Microsoft.NET.Test.Sdk e xunit.runner.visualstudio
- As peças que fazem o
dotnet test(e os editores) encontrarem e rodarem os testes do xUnit. - coverlet.collector
- Mede a cobertura: quais linhas do código os testes executaram. É opcional.
- <Using Include="Xunit" />
- Um
global using Xunit;para o projeto todo (veja O Arquivo .csproj). Por isso os arquivos de teste não precisam deusing Xunit;. - sem <OutputType>
- Um projeto de testes não é um programa: quem o roda é o
dotnet test.
O modelo também criou um arquivo de exemplo, com um teste vazio:
namespace Escola.Notas.Tests;
public class UnitTest1
{
[Fact]
public void Test1()
{
}
}
$ dotnet test Determinando os projetos a serem restaurados... /home/ana/projetos/Escola/tests/Escola.Notas.Tests/Escola.Notas.Tests.csproj restaurado (em 570 ms). 3 de 4 projetos estão atualizados para restauração. Escola.Notas -> /home/ana/projetos/Escola/src/Escola.Notas/bin/Debug/net10.0/Escola.Notas.dll Escola.Notas.Tests -> /home/ana/projetos/Escola/tests/Escola.Notas.Tests/bin/Debug/net10.0/Escola.Notas.Tests.dll Execução de teste para /home/ana/projetos/Escola/tests/Escola.Notas.Tests/bin/Debug/net10.0/Escola.Notas.Tests.dll (.NETCoreApp,Version=v10.0) Versão do VSTest 18.0.2 (x64) Iniciando execução de teste, espere... 1 arquivos de teste no total corresponderam ao padrão especificado. Aprovado! – Com falha: 0, Aprovado: 1, Ignorado: 0, Total: 1, Duração: 20 ms - Escola.Notas.Tests.dll (net10.0)
- Escola.Notas -> … / Escola.Notas.Tests -> …
- O
dotnet testcompilou primeiro a biblioteca e os testes (os programas da solução não foram compilados porque os testes não dependem deles). - Versão do VSTest 18.0.2
- O VSTest é o "executor" de testes do .NET: ele carrega o
.dllde testes e pede ao xUnit para rodar cada um. - Aprovado! – Com falha: 0, Aprovado: 1 …
- O resumo: 1 teste, 1 aprovado. Um teste vazio passa, porque nada nele falhou.
2. O primeiro teste de verdade: [Fact] e Assert.Equal
A Ana apagou o UnitTest1.cs e escreveu os testes do boletim. O terceiro é o caso do Beto:
namespace Escola.Notas.Tests;
public class BoletimTests
{
[Fact]
public void Media_de_tres_notas_tem_uma_casa_decimal()
{
var boletim = new Boletim("Ana");
boletim.Lancar(8.5m);
boletim.Lancar(6.0m);
boletim.Lancar(9.25m);
Assert.Equal(7.9m, boletim.Media);
}
[Fact]
public void Sem_notas_a_media_e_zero()
{
var boletim = new Boletim("Ana");
Assert.Equal(0m, boletim.Media);
}
[Fact]
public void Media_6_25_arredonda_para_6_3()
{
var boletim = new Boletim("Beto");
boletim.Lancar(5.0m);
boletim.Lancar(7.5m);
Assert.Equal(6.3m, boletim.Media);
}
}
- public class BoletimTests
- Uma classe de testes: costuma ter o nome da classe testada mais
Tests. Ela precisa serpublic. - [Fact]
- "Fato": um atributo (veja Atributos) que marca o método como um teste. Sem ele, o xUnit ignora o método.
- public void Media_de_tres_notas_tem_uma_casa_decimal()
- O nome do teste é uma frase que diz o que deve acontecer. Quando ele falhar, só o nome já explica o problema. Os
_são só para ler melhor. - var boletim = …; boletim.Lancar(…);
- Arrange: preparar.
- Assert.Equal(7.9m, boletim.Media);
- Act e Assert: "afirme que são iguais". O primeiro argumento é o esperado; o segundo, o que o código deu. Se forem diferentes, o teste falha ali.
Um teste que falha
$ dotnet test
Determinando os projetos a serem restaurados...
Todos os projetos estão atualizados para restauração.
Escola.Notas -> /home/ana/projetos/Escola/src/Escola.Notas/bin/Debug/net10.0/Escola.Notas.dll
Escola.Notas.Tests -> /home/ana/projetos/Escola/tests/Escola.Notas.Tests/bin/Debug/net10.0/Escola.Notas.Tests.dll
Execução de teste para /home/ana/projetos/Escola/tests/Escola.Notas.Tests/bin/Debug/net10.0/Escola.Notas.Tests.dll (.NETCoreApp,Version=v10.0)
Versão do VSTest 18.0.2 (x64)
Iniciando execução de teste, espere...
1 arquivos de teste no total corresponderam ao padrão especificado.
[xUnit.net 00:00:00.41] Escola.Notas.Tests.BoletimTests.Media_6_25_arredonda_para_6_3 [FAIL]
Com falha Escola.Notas.Tests.BoletimTests.Media_6_25_arredonda_para_6_3 [26 ms]
Mensagem de erro:
Assert.Equal() Failure: Values differ
Expected: 6,3
Actual: 6,2
Rastreamento de pilha:
at Escola.Notas.Tests.BoletimTests.Media_6_25_arredonda_para_6_3() in /home/ana/projetos/Escola/tests/Escola.Notas.Tests/BoletimTests.cs:line 31
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
Com falha! – Com falha: 1, Aprovado: 2, Ignorado: 0, Total: 3, Duração: 113 ms - Escola.Notas.Tests.dll (net10.0)
Leia de cima para baixo:
- [FAIL]
- Qual teste falhou:
Media_6_25_arredonda_para_6_3. O nome já diz o que se esperava. - Assert.Equal() Failure: Values differ
- "Falha no
Assert.Equal: os valores são diferentes." - Expected: 6,3 / Actual: 6,2
- Esperado (o que você escreveu no teste) e obtido (o que o código deu). É a linha mais importante.
- at … BoletimTests.cs:line 31
- A linha do
Assertque falhou. As duas linhasSystem.Reflection…são o xUnit chamando o teste: pode ignorar. - Com falha: 1, Aprovado: 2
- Os outros dois testes continuaram passando. Um teste que falha não impede os outros de rodar.
O teste fez o trabalho dele: achou o defeito. O Math.Round arredonda os números "do meio" para o vizinho par (6,25 → 6,2). A Ana consertou a biblioteca (não o teste!), na linha de Regras.cs:
public static decimal Arredondar(decimal valor) => Math.Round(valor, 1, MidpointRounding.AwayFromZero);
$ dotnet test 2>&1 | tail -2 Aprovado! – Com falha: 0, Aprovado: 3, Ignorado: 0, Total: 3, Duração: 44 ms - Escola.Notas.Tests.dll (net10.0)
MidpointRounding.AwayFromZero ("o meio vai para longe do zero") é o arredondamento da escola: 6,25 → 6,3. Os três passam. E, a partir de agora, se alguém mexer no arredondamento, esse teste avisa.
O editor do guia não tem o xUnit, mas a ideia de um teste cabe em qualquer programa: comparar o esperado com o obtido. Experimente:
Missão: abaixo, um "testador" feito à mão confere a função Media com três casos, como os testes da Ana. Rode: um caso FALHOU. Conserte a função (não os testes) acrescentando MidpointRounding.AwayFromZero ao Math.Round, até aparecer 3 de 3 passaram.
int passaram = 0, total = 0;
Verificar(7.9m, Media([8.5m, 6.0m, 9.25m]), "média de três notas");
Verificar(0m, Media([]), "sem notas, média zero");
Verificar(6.3m, Media([5.0m, 7.5m]), "6,25 arredonda para 6,3");
Console.WriteLine($"{passaram} de {total} passaram");
void Verificar(decimal esperado, decimal obtido, string nome)
{
total++;
if (esperado == obtido)
{
passaram++;
Console.WriteLine($"ok {nome}");
}
else
{
Console.WriteLine($"FALHOU {nome}: esperado {esperado}, obtido {obtido}");
}
}
static decimal Media(decimal[] notas) =>
notas.Length == 0 ? 0 : Math.Round(notas.Average(), 1);
int passaram = 0, total = 0;
Verificar(7.9m, Media([8.5m, 6.0m, 9.25m]), "média de três notas");
Verificar(0m, Media([]), "sem notas, média zero");
Verificar(6.3m, Media([5.0m, 7.5m]), "6,25 arredonda para 6,3");
Console.WriteLine($"{passaram} de {total} passaram");
void Verificar(decimal esperado, decimal obtido, string nome)
{
total++;
if (esperado == obtido)
{
passaram++;
Console.WriteLine($"ok {nome}");
}
else
{
Console.WriteLine($"FALHOU {nome}: esperado {esperado}, obtido {obtido}");
}
}
static decimal Media(decimal[] notas) =>
notas.Length == 0 ? 0 : Math.Round(notas.Average(), 1, MidpointRounding.AwayFromZero);
3. Vários valores num teste: [Theory] e [InlineData]
Para testar a Situacao, a Ana quer conferir vários casos: 7,0 (bem no limite) aprova, 6,9 não. Em vez de um [Fact] para cada, um [Theory] ("teoria") recebe os valores como parâmetros:
public class SituacaoTests
{
[Theory]
[InlineData(7.0, "Aprovado")]
[InlineData(9.5, "Aprovado")]
[InlineData(6.9, "Recuperação")]
[InlineData(0.0, "Recuperação")]
public void Situacao_depende_da_media(double nota, string esperado)
{
var boletim = new Boletim("Ana");
boletim.Lancar((decimal)nota);
Assert.Equal(esperado, boletim.Situacao);
}
[Theory]
[InlineData(-1)]
[InlineData(11)]
public void Nota_fora_de_0_a_10_lanca_excecao(int nota)
{
var boletim = new Boletim("Ana");
var erro = Assert.Throws<ArgumentOutOfRangeException>(() => boletim.Lancar(nota));
Assert.Equal("nota", erro.ParamName);
}
}
- [Theory]
- Um teste com parâmetros: ele roda uma vez para cada
[InlineData]. - [InlineData(7.0, "Aprovado")]
- "Dados na linha": os valores de uma rodada, na ordem dos parâmetros (
nota= 7.0,esperado= "Aprovado"). - double nota … (decimal)nota
- Um atributo não aceita valores
decimal(o C# não deixa:7.0mnão pode ser argumento de atributo). Por isso o parâmetro édouble, convertido paradecimaldentro do teste (veja Conversão de Tipos). - Assert.Throws<ArgumentOutOfRangeException>(() => …)
- "Afirme que lança": roda a lambda e passa se ela lançar essa exceção; falha se não lançar nada (ou se lançar outra). Ele devolve a exceção, para você conferir mais coisas nela.
- Assert.Equal("nota", erro.ParamName)
- Confere que a exceção fala do parâmetro certo.
$ dotnet test 2>&1 | tail -2
Aprovado! – Com falha: 0, Aprovado: 9, Ignorado: 0, Total: 9, Duração: 67 ms - Escola.Notas.Tests.dll (net10.0)
$ dotnet test --list-tests 2>&1 | tail -12
Versão do VSTest 18.0.2 (x64)
Os Testes a seguir estão disponíveis:
Escola.Notas.Tests.BoletimTests.Media_de_tres_notas_tem_uma_casa_decimal
Escola.Notas.Tests.BoletimTests.Sem_notas_a_media_e_zero
Escola.Notas.Tests.BoletimTests.Media_6_25_arredonda_para_6_3
Escola.Notas.Tests.SituacaoTests.Situacao_depende_da_media(nota: 7, esperado: "Aprovado")
Escola.Notas.Tests.SituacaoTests.Situacao_depende_da_media(nota: 9,5, esperado: "Aprovado")
Escola.Notas.Tests.SituacaoTests.Situacao_depende_da_media(nota: 6,9000000000000004, esperado: "Recuperação")
Escola.Notas.Tests.SituacaoTests.Situacao_depende_da_media(nota: 0, esperado: "Recuperação")
Escola.Notas.Tests.SituacaoTests.Nota_fora_de_0_a_10_lanca_excecao(nota: -1)
Escola.Notas.Tests.SituacaoTests.Nota_fora_de_0_a_10_lanca_excecao(nota: 11)
Nove testes: 3 [Fact] + 4 rodadas de um [Theory] + 2 do outro. O --list-tests ("liste os testes") mostra cada rodada com os seus valores. Repare no 6,9000000000000004: é o double 6.9, que não tem representação exata no computador (veja float, double e decimal). A conversão (decimal)nota o arredonda para 6,9, e o teste passa.
4. Rodar só alguns testes: --filter
$ dotnet test --filter "FullyQualifiedName~SituacaoTests" 2>&1 | tail -1 Aprovado! – Com falha: 0, Aprovado: 6, Ignorado: 0, Total: 6, Duração: 50 ms - Escola.Notas.Tests.dll (net10.0)
- --filter "FullyQualifiedName~SituacaoTests"
- "Filtre": só os testes cujo nome completo (namespace + classe + método) contém (
~)SituacaoTests. Com=em vez de~, o nome tem de ser exatamente igual. Útil quando há centenas de testes e você está mexendo numa parte só.
Para ver o nome de cada teste aprovado, aumente a conversa do executor:
$ dotnet test --logger "console;verbosity=normal" 2>&1 | grep -E "Aprovado|Com falha" | head -12
Aprovado Escola.Notas.Tests.SituacaoTests.Nota_fora_de_0_a_10_lanca_excecao(nota: 11) [9 ms]
Aprovado Escola.Notas.Tests.SituacaoTests.Nota_fora_de_0_a_10_lanca_excecao(nota: -1) [< 1 ms]
Aprovado Escola.Notas.Tests.SituacaoTests.Situacao_depende_da_media(nota: 7, esperado: "Aprovado") [< 1 ms]
Aprovado Escola.Notas.Tests.SituacaoTests.Situacao_depende_da_media(nota: 6,9000000000000004, esperado: "Recuperação") [< 1 ms]
Aprovado Escola.Notas.Tests.SituacaoTests.Situacao_depende_da_media(nota: 9,5, esperado: "Aprovado") [< 1 ms]
Aprovado Escola.Notas.Tests.SituacaoTests.Situacao_depende_da_media(nota: 0, esperado: "Recuperação") [< 1 ms]
Aprovado Escola.Notas.Tests.BoletimTests.Media_6_25_arredonda_para_6_3 [22 ms]
Aprovado Escola.Notas.Tests.BoletimTests.Media_de_tres_notas_tem_uma_casa_decimal [< 1 ms]
Aprovado Escola.Notas.Tests.BoletimTests.Sem_notas_a_media_e_zero [< 1 ms]
Aprovados: 9
A ordem não é a do arquivo: o xUnit não promete nenhuma ordem. Por isso, um teste nunca pode depender de outro: cada um cria o seu próprio boletim (o xUnit cria até um objeto novo da classe de testes para cada teste).
5. As verificações mais usadas
| Verificação | Passa quando… |
|---|---|
Assert.Equal(esperado, obtido) | Os dois são iguais. |
Assert.NotEqual(a, b) | São diferentes. |
Assert.True(condição) / Assert.False(condição) | A condição é true / false. |
Assert.Null(x) / Assert.NotNull(x) | x é / não é null. |
Assert.Contains("Ana", texto) | O texto (ou a lista) contém o item. |
Assert.Empty(lista) / Assert.Single(lista) | A lista está vazia / tem um item só. |
Assert.Throws<TipoDaExceção>(() => …) | O código lança exatamente essa exceção. |
await Assert.ThrowsAsync<…>(() => …) | O mesmo, para métodos async (o teste vira public async Task). |
Assert.InRange(valor, mínimo, máximo) | O valor está no intervalo. |
6. A ideia do TDD
A Ana escreveu o teste do 6,25 antes de consertar o arredondamento: primeiro ele falhou, depois ela mudou o código, depois ele passou. Fazer isso de propósito, sempre, tem nome: TDD (Test-Driven Development, "desenvolvimento guiado por testes"). O ciclo tem três passos:
- Vermelho: escreva um teste para o que ainda não funciona. Rode: ele falha.
- Verde: escreva o mínimo de código para ele passar. Rode: todos passam.
- Refatorar: melhore o código (nomes, repetições) com os testes passando. Se algum falhar, você quebrou algo.
Ver o teste falhar primeiro é importante: um teste que nunca falhou pode estar testando a coisa errada (ou nada).
Quando dá errado
Assert.Throws() Failure: No exception was thrown
$ dotnet test --filter "FullyQualifiedName~ErrosTests" 2>&1 | sed -n '/Iniciando/,$p'
Iniciando execução de teste, espere...
1 arquivos de teste no total corresponderam ao padrão especificado.
[xUnit.net 00:00:00.32] Escola.Notas.Tests.ErrosTests.Nota_10_lanca_excecao [FAIL]
Com falha Escola.Notas.Tests.ErrosTests.Nota_10_lanca_excecao [7 ms]
Mensagem de erro:
Assert.Throws() Failure: No exception was thrown
Expected: typeof(System.ArgumentOutOfRangeException)
Rastreamento de pilha:
at Escola.Notas.Tests.ErrosTests.Nota_10_lanca_excecao() in /home/ana/projetos/Escola/tests/Escola.Notas.Tests/ErrosTests.cs:line 10
«…»
Com falha! – Com falha: 1, Aprovado: 0, Ignorado: 0, Total: 1, Duração: 36 ms - Escola.Notas.Tests.dll (net10.0)
O que aconteceu: o teste esperava uma exceção em boletim.Lancar(10m), e nenhuma veio ("nenhuma exceção foi lançada"). Aqui quem está errado é o teste: 10 é uma nota válida.
Como resolver: quando um teste falha, pergunte primeiro "o que está errado: o código ou o teste?". Aqui, o teste certo usa um valor inválido, como 10.5m ou 11m.
warning xUnit2000: esperado e obtido trocados
$ dotnet test 2>&1 | grep warning | sort -u /home/ana/projetos/Escola/tests/Escola.Notas.Tests/ErrosTests.cs(19,9): warning xUnit2000: The literal or constant value 8m should be passed as the 'expected' argument in the call to 'Assert.Equal(expected, actual)' in method 'Ordem_trocada' on type 'ErrosTests'. Swap the parameter values. (https://xunit.net/xunit.analyzers/rules/xUnit2000) [/home/ana/projetos/Escola/tests/Escola.Notas.Tests/Escola.Notas.Tests.csproj]
O que aconteceu: a Ana escreveu Assert.Equal(boletim.Media, 8m). O teste até passa, mas, se um dia falhar, a mensagem vai dizer Expected e Actual trocados, e confundir quem ler. O xUnit vem com analisadores que conferem os testes enquanto compilam; os avisos dele começam com xUnit e vêm em inglês: "o valor literal 8m deveria ser o argumento expected… troque os valores".
Como resolver: o esperado vem primeiro: Assert.Equal(8m, boletim.Media).
error xUnit1001: Fact methods cannot have parameters.
$ dotnet test 2>&1 | grep -E "error|warning" | sort -u /home/ana/projetos/Escola/tests/Escola.Notas.Tests/ErrosTests.cs(6,17): error xUnit1001: Fact methods cannot have parameters. Remove the parameters from the method or convert it into a Theory. (https://xunit.net/xunit.analyzers/rules/xUnit1001) [/home/ana/projetos/Escola/tests/Escola.Notas.Tests/Escola.Notas.Tests.csproj]
O que aconteceu: um método com [Fact] tinha um parâmetro (int nota). Um [Fact] não recebe valores: de onde viria a nota?
Como resolver: como a própria mensagem diz, tire o parâmetro ou troque para [Theory] com [InlineData(…)].
O que aconteceu: não há mensagem de erro: o teste novo simplesmente não roda. As causas comuns: faltou o [Fact] (ou [Theory]) em cima do método; o método ou a classe não é public; o projeto de testes não está na solução e você rodou dotnet test na pasta da solução.
Como resolver: dotnet test --list-tests mostra o que o xUnit encontrou. Se o teste não estiver na lista, confira o atributo e o public; se o projeto todo sumiu, dotnet sln list.
Referência rápida
| Comando ou código | O que faz |
|---|---|
dotnet new xunit -o tests/Nome.Tests | Cria o projeto de testes. |
dotnet reference add src/Nome --project tests/Nome.Tests | Liga os testes ao que eles testam. |
dotnet test | Compila e roda todos os testes. |
dotnet test --list-tests | Lista os testes encontrados. |
dotnet test --filter "FullyQualifiedName~Texto" | Roda só os testes com "Texto" no nome. |
dotnet test --logger "console;verbosity=normal" | Mostra cada teste aprovado. |
[Fact] | Um teste sem parâmetros. |
[Theory] + [InlineData(…)] | Um teste que roda com cada linha de dados. |
Assert.Equal(esperado, obtido) | Afirma que são iguais (o esperado primeiro). |
Assert.Throws<T>(() => …) | Afirma que o código lança a exceção T. |
Exercícios
1. Marcar o teste
Complete o atributo de um teste sem parâmetros:
public void Nota_7_aprova() { … }
2. A ordem do Assert
A média esperada é 8m. Complete na ordem certa:
3. Rodar os testes
Complete o comando que compila e roda todos os testes da solução:
4. O dotnet test mostrou Expected: 6,3 e Actual: 6,2. O que isso quer dizer?
5. Você quer testar a Situacao com as médias 7,0, 6,9 e 10,0. Qual é o jeito mais simples?
6. Um teste falhou. Qual é a primeira pergunta?
Resumo
- Um teste automatizado confere um caso do seu código sempre que você roda
dotnet test: preparar, agir, afirmar. - Projeto de testes:
dotnet new xunit -o tests/Nome.Tests,dotnet sln addedotnet reference addpara a biblioteca testada. [Fact]marca um teste;Assert.Equal(esperado, obtido)compara, com o esperado primeiro.- No teste que falha, leia o nome, o
Expected, oActuale a linha. Conserte o código (ou o teste, se ele estiver errado). [Theory]+[InlineData]roda o mesmo teste com vários valores;Assert.Throwstesta exceções.--filterescolhe os testes;--list-testsmostra o que foi encontrado; os analisadoresxUnit…avisam dos erros comuns.- TDD: vermelho (teste falha), verde (código passa), refatorar.