Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Las pruebas de mutación son una manera de evaluar la calidad de nuestras pruebas unitarias. Para las pruebas de mutación, la herramienta Stryker.NET realiza automáticamente mutaciones en el código, ejecuta pruebas y genera un informe detallado con los resultados.
Escenario de prueba de ejemplo
Considere un ejemplo PriceCalculator.cs clase con un Calculate
método que calcule el precio, teniendo en cuenta el descuento.
public class PriceCalculator
{
public decimal CalculatePrice(decimal price, decimal discountPercent)
{
if (price <= 0)
{
throw new ArgumentException("Price must be greater than zero.");
}
if (discountPercent < 0 || discountPercent > 100)
{
throw new ArgumentException("Discount percent must be between 0 and 100.");
}
var discount = price * (discountPercent / 100);
var discountedPrice = price - discount;
return Math.Round(discountedPrice, 2);
}
}
El método anterior está cubierto por las siguientes pruebas unitarias:
[Fact]
public void ApplyDiscountCorrectly()
{
decimal price = 100;
decimal discountPercent = 10;
var calculator = new PriceCalculator();
var result = calculator.CalculatePrice(price, discountPercent);
Assert.Equal(90.00m, result);
}
[Fact]
public void InvalidDiscountPercent_ShouldThrowException()
{
var calculator = new PriceCalculator();
Assert.Throws<ArgumentException>(() => calculator.CalculatePrice(100, -1));
Assert.Throws<ArgumentException>(() => calculator.CalculatePrice(100, 101));
}
[Fact]
public void InvalidPrice_ShouldThrowException()
{
var calculator = new PriceCalculator();
Assert.Throws<ArgumentException>(() => calculator.CalculatePrice(-10, 10));
}
El código anterior resalta dos proyectos, uno para el servicio que actúa como PriceCalculator
y el otro es el proyecto de prueba.
Instalación de la herramienta global
En primer lugar, instale Stryker.NET. Para ello, debe ejecutar el comando :
dotnet tool install -g dotnet-stryker
Para ejecutar stryker
, invoque desde la línea de comandos del directorio donde se encuentra el proyecto de prueba unitaria:
dotnet stryker
Una vez ejecutadas las pruebas, se muestra un informe en la consola.
Stryker.NET guarda un informe HTML detallado en el directorio StrykerOutput.
Ahora, considere lo que son los mutantes y lo que significan "sobrevivió" y "mató". Un mutante es un pequeño cambio en el código que Stryker realiza a propósito. La idea es sencilla: si las pruebas son buenas, deben detectar el cambio y fallar. Si siguen superando, es posible que las pruebas no sean lo suficientemente fuertes.
En nuestro ejemplo, un mutante será el reemplazo de la expresión price <= 0
, por ejemplo, con price < 0
, después de la cual se ejecutan pruebas unitarias.
Stryker admite varios tipos de mutaciones:
Tipo | Descripción |
---|---|
Equivalente | El operador equivalente se usa para reemplazar un operador por su equivalente. Por ejemplo, x < y se convierte en x <= y . |
Aritméticos | El operador aritmético se usa para reemplazar un operador aritmético por su equivalente. Por ejemplo, x + y se convierte en x - y . |
Cuerda | El operador de cadena se usa para reemplazar una cadena por su equivalente. Por ejemplo, "text" se convierte en "" . |
Lógico | El operador lógico se usa para reemplazar un operador lógico por su equivalente. Por ejemplo, x && y se convierte en x \|\| y . |
Para obtener más tipos de mutación, consulte la documentación de Stryker.NET: Mutaciones .
Mejora incremental
Si, después de cambiar el código, las pruebas unitarias se pasan correctamente, no son suficientemente robustas y el mutante sobrevive. Después de las pruebas de mutación, cinco mutantes sobreviven.
Vamos a agregar datos de prueba para los valores de límite y volver a ejecutar pruebas de mutación.
[Fact]
public void InvalidPrice_ShouldThrowException()
{
var calculator = new PriceCalculator();
// changed price from -10 to 0
Assert.Throws<ArgumentException>(() => calculator.CalculatePrice(0, 10));
}
[Fact] // Added test for 0 and 100 discount
public void NoExceptionForZeroAnd100Discount()
{
var calculator = new PriceCalculator();
var exceptionWhen0 = Record.Exception(() => calculator.CalculatePrice(100, 0));
var exceptionWhen100 = Record.Exception(() => calculator.CalculatePrice(100, 100));
Assert.Null(exceptionWhen0);
Assert.Null(exceptionWhen100);
}
Como puede ver, después de corregir los mutantes equivalentes, solo tenemos mutaciones de cadena restantes, que podemos 'matar' fácilmente comprobando el texto del mensaje de excepción.
[Fact]
public void InvalidDiscountPercent_ShouldThrowExceptionWithCorrectMessage()
{
var calculator = new PriceCalculator();
var ex1 = Assert.Throws<ArgumentException>(() => calculator.CalculatePrice(100, -1));
Assert.Equal("Discount percent must be between 0 and 100.", ex1.Message);
var ex2 = Assert.Throws<ArgumentException>(() => calculator.CalculatePrice(100, 101));
Assert.Equal("Discount percent must be between 0 and 100.", ex2.Message);
}
[Fact]
public void InvalidPrice_ShouldThrowExceptionWithCorrectMessage()
{
var calculator = new PriceCalculator();
var ex = Assert.Throws<ArgumentException>(() => calculator.CalculatePrice(0, 10));
Assert.Equal("Price must be greater than zero.", ex.Message);
}
Las pruebas de mutación ayudan a encontrar oportunidades para mejorar las pruebas que las hacen más confiables. Te obliga a comprobar no solo la "ruta feliz", sino también casos límite complejos, lo que disminuye la probabilidad de errores en la producción.