11. Pruebas unitarias
Secciones
Ajustes de lectura
Ninguna voz para este idioma en este navegador.
Más voces
Código y términos técnicos en inglés
Desactivado: la voz de la página lo lee todo, sin pausa entre dos voces.
Ninguna voz para este idioma en este navegador.
Más voces
Más voces: Microsoft Edge ofrece voces naturales en línea, y Windows (Configuración › Hora e idioma › Voz), macOS y Android instalan otras.
Hasta ahora, check.sh comprobaba los programas de este curso comparando toda su salida con un archivo. Una prueba unitaria comprueba un método directamente: lo llama con argumentos conocidos y compara el resultado con el valor que debería devolver. Una prueba también es un pequeño programa. Un framework de pruebas encuentra las pruebas, ejecuta cada una, cuenta los fallos y dice qué prueba falló y por qué. Esta lección usa xUnit, el framework de la plantilla dotnet new xunit, para probar métodos de las lecciones 4, 8 y 10.
El código de esta lección está en code/csharp-beginner: examples/l11_by_hand.cs, y la carpeta l11-tests con tres proyectos. Fretboard es una biblioteca, Fretboard.Tests contiene sus pruebas y las soluciones de los ejercicios, y las pruebas de Pitfalls.Tests fallan a propósito. check.sh ejecuta dotnet test sobre los dos proyectos de pruebas y compara un resumen con los archivos de expected/.
Una prueba a mano
Sección titulada «Una prueba a mano»Una prueba no necesita framework. Este programa llama cuatro veces al FretFrequency de la lección 4 y compara cada resultado con el valor que espera:
// Una prueba a mano: llamar a un método, comparar su resultado con el valor esperado, y decir qué comprobación fallóint failed = 0;Check("fret 12 of A2", FretFrequency(110.0, 12), 220.0);Check("fret 7 of A2", FretFrequency(110.0, 7), 164.81);Check("fret 5 of E2", FretFrequency(82.41, 5), 110.0);Check("fret 1 of B3", FretFrequency(246.94, 1), 261.63); // C4 en una tabla de frecuencias de notasConsole.WriteLine($"{failed} failed");
void Check(string name, double actual, double expected){ if (actual == expected) { Console.WriteLine($"ok {name}"); } else { Console.WriteLine($"FAIL {name}: expected {expected}, got {actual}"); failed++; }}
// El método de la lección 4double FretFrequency(double openString, int fret){ double frequency = openString * Math.Pow(2, fret / 12.0); return Math.Round(frequency, 2);}ok fret 12 of A2ok fret 7 of A2ok fret 5 of E2FAIL fret 1 of B3: expected 261.63, got 261.621 failedLa última comprobación falla, y el método no está mal. El valor esperado, 261.63 Hz, es el do central (do4) en una tabla de frecuencias. El método parte de 246.94, la cuerda Si al aire ya redondeada a dos decimales, y un traste por encima da 261.62. Una prueba que falla solo dice que dos valores difieren: puede estar mal el código, o el valor esperado, y tienes que averiguar cuál.
El programa también termina con el código de salida 0, como si todo hubiera pasado: un script que lo ejecuta no puede saber que una comprobación falló. check.sh también funciona así, con sus líneas ok y FAIL, pero fija su propio código de salida. Un framework de pruebas hace este trabajo por ti.
Una biblioteca y un proyecto de pruebas
Sección titulada «Una biblioteca y un proyecto de pruebas»Un proyecto de pruebas no puede llamar a los métodos de una aplicación basada en archivos. Los métodos van a una biblioteca de clases, un proyecto sin instrucciones de nivel superior que usan otros proyectos, y las pruebas van a un segundo proyecto que la referencia. Tres comandos crean los dos a partir de una carpeta vacía, con las plantillas del SDK:
dotnet new classlib -n Fretboarddotnet new xunit -n Fretboard.Testsdotnet add Fretboard.Tests reference FretboardLos dos primeros muestran The template "Class Library" was created successfully. y The template "xUnit Test Project" was created successfully., y después restauran los paquetes; el tercero muestra Reference `..\Fretboard\Fretboard.csproj` added to the project. en Windows. Cada plantilla añade también un archivo de relleno, Class1.cs y UnitTest1.cs, que esta lección borró.
La biblioteca contiene los métodos de lecciones anteriores, sin cambios, como métodos public static de una clase en el espacio de nombres Fretboard:
namespace Fretboard;
// Métodos de lecciones anteriores, movidos a una biblioteca para que un proyecto de pruebas pueda llamarlospublic static class Guitar{ static readonly string[] Chromatic = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
// Lección 4: la frecuencia de un traste, redondeada a dos decimales public static double FretFrequency(double openString, int fret) { double frequency = openString * Math.Pow(2, fret / 12.0); return Math.Round(frequency, 2); }
// Lección 4, ejercicio 2: una lista nueva con cada nota desplazada un número de semitonos public static List<string> Transpose(List<string> notes, int semitones) { List<string> result = []; foreach (string note in notes) { int index = Array.IndexOf(Chromatic, note); int moved = ((index + semitones) % 12 + 12) % 12; // + 12 mantiene los pasos negativos en 0..11 result.Add(Chromatic[moved]); } return result; }
// Lección 8, ejercicio 2: un número de traste leído de un texto, de 0 a 24 public static int ParseFret(string text) { int fret = int.Parse(text); ArgumentOutOfRangeException.ThrowIfNegative(fret); ArgumentOutOfRangeException.ThrowIfGreaterThan(fret, 24); return fret; }}Un segundo archivo, Project.cs, contiene el record Project de la lección 10 con un método Parse para una línea de data/ga-projects.csv. El archivo del proyecto de pruebas, Fretboard.Tests.csproj, lista lo que eligió la plantilla con el SDK 10.0.112:
<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>
<ItemGroup> <ProjectReference Include="..\Fretboard\Fretboard.csproj" /></ItemGroup>xunittrae los atributos yAssert, y<Using Include="Xunit" />los hace disponibles en todos los archivos del proyecto sin unusing.xunit.runner.visualstudioyMicrosoft.NET.Test.Sdkpermiten quedotnet testy los editores encuentren y ejecuten las pruebas;coverlet.collectormide qué líneas ejecutaron las pruebas, cuando se lo pides.ProjectReferencees la línea que escribiódotnet add reference: las pruebas ya pueden usar las clases públicas de la biblioteca.
La plantilla crea un proyecto xUnit v2. El sitio de xUnit dice que v2 está en modo de mantenimiento, y anima a usar v3 para el trabajo nuevo; esta lección se queda con lo que da la plantilla.
Una primera prueba
Sección titulada «Una primera prueba»Una prueba es un método public con el atributo [Fact], en una clase public:
// Un hecho (Fact): un solo caso, sin parámetro [Fact] public void FretFrequency_TwelfthFret_DoublesTheFrequency() { // Preparar: la cuerda La al aire double openA = 110.0;
// Actuar double result = Guitar.FretFrequency(openA, 12);
// Afirmar: el valor esperado va primero, el valor real después Assert.Equal(220.0, result); }La mayoría de las pruebas tienen tres pasos: preparar los datos (arrange), actuar llamando al método (act), afirmar que el resultado es el esperado (assert). Assert.Equal lanza una excepción cuando sus dos valores difieren, y xUnit cuenta la prueba como fallida. El nombre de la prueba dice qué comprueba, en qué situación y qué debería pasar: cuando falla, el nombre basta para saber qué se ha roto.
dotnet test compila la biblioteca y el proyecto de pruebas, y después ejecuta todas las pruebas:
dotnet test l11-tests/Fretboard.TestsMuestra las líneas de la restauración y de la compilación, y después una línea por proyecto de pruebas. check.sh conserva esa última línea, sin su duración, que cambia en cada ejecución:
Passed! - Failed: 0, Passed: 26, Skipped: 0, Total: 26 - Fretboard.Tests.dll (net10.0)Las 26 pruebas son las de esta lección y las soluciones de sus ejercicios. El comando termina con el código de salida 0, y con 1 en cuanto falla una prueba: un script o un trabajo de CI ve el fallo sin leer el texto.
Los records se comparan por valor (lección 6), así que un solo Assert.Equal comprueba los cuatro campos que lee Project.Parse, y Assert.Throws comprueba que un método lanza una excepción:
[Fact] public void Parse_ReadsTheFourFields() { Project project = Project.Parse("GA.Core,C#,67,0");
// Los records se comparan por valor: un solo Assert comprueba los cuatro campos Assert.Equal(new Project("GA.Core", "C#", 67, 0), project); }
[Fact] public void Parse_RefusesALineWithoutNumbers() { Assert.Throws<FormatException>(() => Project.Parse("GA.Core,C#,many,0")); }La lambda, () => Project.Parse(...), le da a Assert.Throws la llamada que debe hacer en lugar de hacerla: la excepción ocurre dentro de Assert.Throws, que la captura y comprueba su tipo.
Varios casos en una prueba
Sección titulada «Varios casos en una prueba»Una teoría, [Theory], es una prueba con parámetros, y cada atributo [InlineData] le da una fila de argumentos:
// Una teoría: la misma prueba con varias filas de datos, cada fila una prueba aparte [Theory] [InlineData(110.0, 0, 110.0)] [InlineData(110.0, 7, 164.81)] [InlineData(82.41, 5, 110.0)] [InlineData(110.0, 12, 220.0)] public void FretFrequency_RoundsToTwoDecimals(double openString, int fret, double expected) { Assert.Equal(expected, Guitar.FretFrequency(openString, fret)); }xUnit cuenta cada fila como una prueba propia: con esta teoría, la primera versión del proyecto tenía 15 pruebas, no 12. Una fila que falla se informa con sus argumentos, como muestra la sección siguiente, y las demás filas se ejecutan igualmente.
Cuando una prueba falla
Sección titulada «Cuando una prueba falla»Las cuatro pruebas de Pitfalls.Tests fallan a propósito. Cada una comete un error típico de principiante, y dotnet test lo explica:
// 0.1 + 0.2 no es exactamente 0.3 en un double [Fact] public void Doubles_ComparedBitForBit() { Assert.Equal(0.3, 0.1 + 0.2); }
// ParseFret lanza ArgumentOutOfRangeException, un tipo derivado de ArgumentException [Fact] public void Throws_WithTheBaseType() { Assert.Throws<ArgumentException>(() => Guitar.ParseFret("25")); }
// Los argumentos están intercambiados: el mensaje llama "Expected" al resultado del método [Fact] public void Equal_WithTheArgumentsSwapped() { Assert.Equal(Guitar.FretFrequency(110.0, 7), 164.8); }
// Una fila de la teoría está mal: E subido un semitono es F [Theory] [InlineData("C", 2, "D")] [InlineData("B", 1, "C")] [InlineData("E", 1, "F#")] public void Transpose_OneNote(string note, int semitones, string expected) { Assert.Equal([expected], Guitar.Transpose([note], semitones)); }check.sh conserva las advertencias de la compilación, el mensaje de cada prueba fallida y la última línea. Quita las rutas, las duraciones y la pila de llamadas (stack trace), la lista de llamadas a métodos que llevaron al fallo, y ordena las pruebas fallidas por nombre: xUnit no las informa en el orden del archivo.
PitfallTests.cs(26,9): warning xUnit2000: The literal or constant value 164.8 should be passed as the 'expected' argument in the call to 'Assert.Equal(expected, actual)' in method 'Equal_WithTheArgumentsSwapped' on type 'PitfallTests'. Swap the parameter values. (https://xunit.net/xunit.analyzers/rules/xUnit2000) Failed Pitfalls.Tests.PitfallTests.Doubles_ComparedBitForBit Error Message: Assert.Equal() Failure: Values differExpected: 0.29999999999999999Actual: 0.30000000000000004 Failed Pitfalls.Tests.PitfallTests.Equal_WithTheArgumentsSwapped Error Message: Assert.Equal() Failure: Values differExpected: 164.81Actual: 164.80000000000001 Failed Pitfalls.Tests.PitfallTests.Throws_WithTheBaseType Error Message: Assert.Throws() Failure: Exception type was not an exact matchExpected: typeof(System.ArgumentException)Actual: typeof(System.ArgumentOutOfRangeException)---- System.ArgumentOutOfRangeException : fret ('25') must be less than or equal to '24'. (Parameter 'fret')Actual value was 25. Failed Pitfalls.Tests.PitfallTests.Transpose_OneNote(note: "E", semitones: 1, expected: "F#") Error Message: Assert.Equal() Failure: Collections differ ↓ (pos 0)Expected: ["F#"]Actual: ["F"] ↑ (pos 0)Failed! - Failed: 4, Passed: 2, Skipped: 0, Total: 6 - Pitfalls.Tests.dll (net10.0)Las 2 pruebas que pasan son las dos primeras filas de la teoría. Lee cada mensaje desde arriba:
- Doubles. Un
doubleno puede guardar exactamente 0.1, 0.2 ni 0.3, así que0.1 + 0.2y0.3son dos números ligeramente distintos. xUnit muestra los dos con 17 cifras para enseñar la diferencia: incluso0.3se convierte en0.29999999999999999. Compara los doubles con un número de decimales,Assert.Equal(0.3, 0.1 + 0.2, 10), que pasa. Las pruebas deFretFrequencyno lo necesitan: el método redondea conMath.Round, y el resultado redondeado es el mismodoubleque el literal164.81de la prueba. - Tipos de excepción.
Assert.Throws<ArgumentException>quiere exactamente ese tipo.ParseFretlanzaArgumentOutOfRangeException, una clase derivada deArgumentException(lección 8): el mensaje muestra los dos tipos, y la excepción que se lanzó. Indica el tipo exacto, o usaAssert.ThrowsAny<ArgumentException>, que también acepta los tipos derivados. - Argumentos intercambiados. El valor esperado va primero. Aquí el literal va segundo, así que el mensaje llama «Expected» al resultado del método, 164.81, y «Actual» al valor incorrecto, 164.8: lo contrario de la verdad. El compilador avisó antes de que se ejecutaran las pruebas: xUnit2000 viene de un analizador, una comprobación que el paquete
xunitañade al compilador. - Una fila incorrecta. El nombre de la prueba muestra los argumentos de la fila fallida, y las flechas señalan el primer elemento que difiere. Esta vez el código está bien y la prueba está mal: mi (E) subido un semitono es fa (F).
Las correcciones de las dos primeras están en Fretboard.Tests; las dos últimas son el ejercicio 3:
// Assert.ThrowsAny también acepta un tipo derivado: ArgumentOutOfRangeException es una ArgumentException [Fact] public void ParseFret_RefusesANegativeFret() { Assert.ThrowsAny<ArgumentException>(() => Guitar.ParseFret("-1")); }
// Aquí se comparan dos doubles con 10 decimales, no bit a bit [Fact] public void Doubles_CompareWithAPrecision() { Assert.Equal(0.3, 0.1 + 0.2, 10); }Un objeto por prueba
Sección titulada «Un objeto por prueba»xUnit crea un objeto nuevo de la clase de pruebas para cada prueba. Un campo no lleva nada de una prueba a la siguiente:
// xUnit crea un objeto nuevo de la clase de pruebas para cada prueba, así que un campo empieza en 0 en cada pruebapublic class InstanceTests{ int _count;
[Fact] public void FirstTest() { _count++; Assert.Equal(1, _count); }
[Fact] public void SecondTest() { _count++; Assert.Equal(1, _count); }}Las dos pruebas pasan: cada una ve _count en 0. Una prueba no puede contar con que otra se haya ejecutado antes, y no lo necesita: cada prueba prepara sus propios datos.
Probar lo que la biblioteca hace público
Sección titulada «Probar lo que la biblioteca hace público»El proyecto de pruebas es otro programa: llama a la biblioteca desde fuera, y solo ve sus miembros public. Un miembro sin public es privado de su clase, como en este fragmento, donde las instrucciones de nivel superior hacen el papel de la prueba:
// Una prueba llama a la biblioteca desde fuera: solo ve lo que la biblioteca hace públicoConsole.WriteLine(Guitar.Chromatic.Length);
static class Guitar{ static readonly string[] Chromatic = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];}l11_private.cs(2,26): error CS0122: 'Guitar.Chromatic' is inaccessible due to its protection levelCS0122 dice que el miembro existe pero no se puede alcanzar desde aquí. Desde un proyecto de pruebas, un miembro marcado internal, visible solo dentro de su propio proyecto, da otro error: en un par de proyectos de borrador, el compilador respondió CS0117, 'Lab' does not contain a definition for 'Secret', como si el método no existiera. El atributo InternalsVisibleTo puede abrir los miembros internos de una biblioteca a sus pruebas. Probar a través de los métodos públicos suele ser mejor: las pruebas comprueban entonces lo que usan los demás proyectos, y siguen pasando cuando reescribes el interior de un método.
En Guitar Alchemist
Sección titulada «En Guitar Alchemist»En el commit 5c3a52a, Guitar Alchemist tiene 18 proyectos de pruebas. 16 archivos de proyecto referencian NUnit, otro framework de pruebas, y 3 referencian xUnit. Las ideas son las mismas, con otros nombres: [Test] por [Fact], [TestCase] por [InlineData], Assert.That(actual, Is.EqualTo(expected)) por Assert.Equal(expected, actual), y [Ignore("reason")] para omitir una prueba.
Las pruebas de los archivos YAML. La lección 9 encontró que tres de los cuatro archivos YAML de los servicios de conocimiento musical de GA no se cargan, lo que ahora es la issue #797 de GA. GA tiene pruebas para estos servicios, en MusicalKnowledgeServiceTests.cs. Tres de sus ocho pruebas se omiten con [Ignore("Configuration files not loaded in test environment")], y de las otras cinco, tres solo comprueban que un recuento es mayor que cero o que una lista no está vacía. Una sonda compiló este archivo sin cambios, con los servicios de GA y los mismos paquetes NUnit, y lo ejecutó: 5 pruebas pasaron, 3 se omitieron, ninguna falló. La búsqueda de «jazz» pasó con un resultado: la progresión de acordes por defecto que el cargador conserva tras su fallo. Una prueba que comprueba «más que cero» no puede ver un cargador que falla y devuelve un elemento por defecto; una prueba que comprobara el número real de elementos, o que la carga no informa de ningún error, sí podría.
Una preparación que nunca se ejecuta. El mismo proyecto de pruebas tiene una clase, TestEnvironment, marcada [SetUpFixture], que mueve el directorio actual a la raíz del repositorio. Su comentario dice que «ensures configuration-backed services can locate their YAML/JSON inputs during tests» (garantiza que los servicios basados en la configuración puedan encontrar sus entradas YAML/JSON durante las pruebas): el problema de la lección 10, resuelto para las pruebas. Pero NUnit ejecuta un setup fixture solo antes de las pruebas de su propio espacio de nombres, y este está solo en GA.Business.Core.Tests.TestBootstrap. Una prueba de la sonda, colocada junto a las pruebas YAML, vio el directorio actual todavía en su carpeta bin, y GA_TEST_MODE, que la preparación define, sin definir. Los archivos YAML se encuentran de todos modos, junto al programa, donde los copia la compilación.
Pruebas que no se compilan. El archivo de proyecto quita 35 archivos de la compilación, bajo el comentario «Exclude test files with missing service implementations» (excluir los archivos de prueba cuyos servicios no tienen implementación). Cinco de los nombres ya no corresponden a ningún archivo; los otros 30 archivos contienen 333 líneas que empiezan por [Test] o [TestCase]. Una prueba que no se compila nunca falla, y dotnet test no la menciona.
La tabla QA del diario registra estas mediciones.
Ejercicios
Sección titulada «Ejercicios»Las soluciones son pruebas de Fretboard.Tests, en ExerciseTests.cs: dotnet test las ejecuta con las demás.
Ejercicio 1 — transponer hacia abajo
Sección titulada «Ejercicio 1 — transponer hacia abajo»Escribe una prueba que compruebe que Guitar.Transpose(["A", "C", "E"], -3) devuelve F#, A y C#.
Solución
// Ejercicio 1: transponer hacia abajo [Fact] public void Transpose_MovesEachNoteDown() { List<string> result = Guitar.Transpose(["A", "C", "E"], -3);
Assert.Equal(["F#", "A", "C#"], result); }Assert.Equal compara dos listas elemento a elemento; la expresión de colección ["F#", "A", "C#"] toma el tipo del otro argumento.
Ejercicio 2 — los límites de ParseFret
Sección titulada «Ejercicio 2 — los límites de ParseFret»Los bugs se esconden en los límites. Prueba que ParseFret acepta "0" y "24", rechaza "-1" y "25" con ArgumentOutOfRangeException, y rechaza "seven" con FormatException. Usa teorías donde una prueba tenga varias filas.
Solución
// Ejercicio 2: los límites de ParseFret [Theory] [InlineData("0", 0)] [InlineData("24", 24)] public void ParseFret_AcceptsTheFirstAndLastFret(string text, int expected) { Assert.Equal(expected, Guitar.ParseFret(text)); }
[Theory] [InlineData("-1")] [InlineData("25")] public void ParseFret_RefusesTheFretsJustOutside(string text) { Assert.Throws<ArgumentOutOfRangeException>(() => Guitar.ParseFret(text)); }
[Fact] public void ParseFret_RefusesAWord() { Assert.Throws<FormatException>(() => Guitar.ParseFret("seven")); }Cada límite tiene un valor justo dentro, 0 y 24, y un valor justo fuera, -1 y 25. Si alguien escribe ThrowIfGreaterThan(fret, 23) por error, la fila "24" falla.
Ejercicio 3 — corregir las dos pruebas incorrectas
Sección titulada «Ejercicio 3 — corregir las dos pruebas incorrectas»Dos pruebas de Pitfalls.Tests esperan el valor equivocado: Equal_WithTheArgumentsSwapped y la fila "E", 1, "F#" de Transpose_OneNote. Escribe versiones correctas de las dos.
Solución
// Ejercicio 3: las dos trampas cuyo valor esperado estaba mal, corregidas [Fact] public void Equal_WithTheExpectedValueFirst() { Assert.Equal(164.81, Guitar.FretFrequency(110.0, 7)); }
[Theory] [InlineData("C", 2, "D")] [InlineData("B", 1, "C")] [InlineData("E", 1, "F")] public void Transpose_OneNote(string note, int semitones, string expected) { Assert.Equal([expected], Guitar.Transpose([note], semitones)); }La primera corrección pone el literal primero y usa el valor correcto, 164.81: la advertencia xUnit2000 también desaparece. En la segunda, el código estaba bien: la corrección está en la prueba.
Ejercicio 4 — una prueba que lee un archivo
Sección titulada «Ejercicio 4 — una prueba que lee un archivo»Escribe una prueba que lea data/ga-projects.csv con Project.Parse y compruebe que contiene 12 proyectos y 632 archivos fuente, .cs y .fs juntos. Aquí importan dos cosas de la lección 10: el archivo debe copiarse junto a las pruebas compiladas, y una prueba no debería depender del directorio actual.
Solución
Un elemento None en Fretboard.Tests.csproj copia el archivo en la carpeta de salida, bajo data:
<ItemGroup> <None Include="..\..\data\ga-projects.csv" Link="data\ga-projects.csv" CopyToOutputDirectory="PreserveNewest" /></ItemGroup>La prueba construye la ruta a partir de AppContext.BaseDirectory, la carpeta de las pruebas compiladas:
// Ejercicio 4: una prueba que lee data/ga-projects.csv, copiado junto a las pruebas por Fretboard.Tests.csproj [Fact] public void GaProjects_HoldSixHundredThirtyTwoSourceFiles() { string path = Path.Combine(AppContext.BaseDirectory, "data", "ga-projects.csv");
List<Project> projects = File.ReadLines(path).Skip(1).Select(Project.Parse).ToList();
Assert.Equal(12, projects.Count); Assert.Equal(632, projects.Sum(p => p.SourceFiles)); }Select(Project.Parse) le da a LINQ el propio método, como Count(IsFSharp) en la lección 9. 632 son los 557 archivos C# y los 75 archivos F# de la lección 10.
Lo que debes recordar
Sección titulada «Lo que debes recordar»- Una prueba unitaria llama a un método con argumentos conocidos y compara su resultado con el valor esperado. Un framework de pruebas ejecuta todas las pruebas, cuenta los fallos y fija el código de salida.
- Las pruebas van en un proyecto de pruebas que referencia una biblioteca de clases:
dotnet new classlib,dotnet new xunit,dotnet add reference.dotnet testcompila los dos y ejecuta las pruebas. [Fact]marca una prueba;[Theory]con[InlineData]ejecuta la misma prueba una vez por fila, y cada fila cuenta como una prueba.Assert.Equal(expected, actual): el valor esperado primero. Compara los doubles con un número de decimales.Assert.Throws<T>quiere el tipo exacto;Assert.ThrowsAny<T>acepta los tipos derivados.- Una prueba que falla dice que dos valores difieren: comprueba si está mal el código o la prueba antes de cambiar uno u otra.
- xUnit crea un objeto nuevo para cada prueba, y no promete ningún orden. Un proyecto de pruebas solo ve los miembros públicos de la biblioteca.
- Una prueba que se omite, que no se compila o que solo comprueba «más que cero» puede pasar mientras el código está roto.
La siguiente lección, un pequeño proyecto: una solución con esta biblioteca, una aplicación de consola y estas pruebas, un paquete NuGet, y un primer vistazo a async.
Fuentes
Sección titulada «Fuentes»- xUnit: primeros pasos con xUnit.net v2, regla del analizador xUnit2000
- .NET: pruebas unitarias de C# con xUnit, procedimientos recomendados para pruebas unitarias,
dotnet test, plantillas dedotnet new,dotnet add reference,InternalsVisibleTo,ArgumentOutOfRangeException,AppContext.BaseDirectory, elementos de MSBuild - NUnit: documentación,
SetUpFixture - Errores del compilador CS0122, CS0117