Cuando escribes pruebas unitarias en xUnit, no siempre necesitas verificar que el resultado sea exactamente igual a un texto. A veces basta con confirmar que empiece con cierta palabra, contenga un fragmento o que una función dispare una excepción cuando recibe un parámetro inválido. Aquí aprendes a usar StartsWith, Contains y Throws para cubrir esos escenarios reales en C#.
¿Cómo pruebo que un texto empiece con una palabra usando StartsWith?
El método StartsWith te permite verificar que una cadena de texto comience con un string específico. Es ideal cuando el resultado completo puede variar, pero el inicio sí debe mantenerse constante.
Imagina la función QuantityInWords, que recibe una palabra y un número, y devuelve algo como "ten cats" cuando le pasas cat y 10. En lugar de comparar el resultado completo, validas solo el inicio.
csharp
[Fact]
public void QuantityInWords()
{
var stringOperation = new StringOperation();
var result = stringOperation.QuantityInWords("cat", 10);
Assert.StartsWith("ten", result);
Assert.Contains("cat", result);
}
Si tu Visual Studio está configurado en español, la librería puede devolver "diez" en lugar de "ten". Adapta la prueba a tu entorno sin perder la lógica de validación [03:30].
¿Qué hace StartsWith en xUnit? Verifica que una cadena de texto comience con un string específico. Útil cuando el resto del resultado puede variar pero el inicio debe ser constante.
¿Cuándo conviene usar Contains en lugar de Equal?
Usa Contains cuando solo necesitas confirmar que cierta palabra aparezca en el resultado, sin importar su posición ni si está pluralizada. Esto te da flexibilidad cuando el formato exacto puede cambiar.
En el ejemplo anterior, la función puede devolver "ten cats" pluralizado. Si validas con Equal te arriesgas a que la prueba falle por un detalle menor. Con Contains solo confirmas que la palabra cat esté presente [04:45].
La lección práctica: sé sabio al elegir qué comprobar. No siempre necesitas igualdad exacta; a veces verificar la presencia de un fragmento es suficiente y hace tus pruebas más robustes ante cambios pequeños.
¿Cómo pruebo que una función lance una excepción en xUnit?
Cuando una función dispara una excepción a propósito ante parámetros inválidos, esa lógica también debe cubrirse con pruebas. xUnit ofrece Assert.Throws<T> para validar que el código lance el tipo de excepción esperado.
Mira el método GetStringLen: si recibe null, lanza un ArgumentNullException. Para probarlo, usas una expresión lambda como delegado:
csharp
[Fact]
public void GetStringLen_Exception()
{
var stringOperation = new StringOperation();
Assert.Throws<ArgumentNullException>(() => stringOperation.GetStringLen(null));
}
La expresión lambda () => es clave: convierte la llamada en un delegado que Throws puede ejecutar y capturar la excepción [07:30].
¿Por qué necesito una lambda al usar Assert.Throws? Porque Throws requiere un delegado, no la ejecución directa. La lambda envuelve la llamada para que xUnit la ejecute y capture la excepción dentro del assert.
¿Qué pasa si la función no lanza la excepción esperada?
Si pasas un valor válido en lugar de null, la prueba falla con un mensaje claro: se esperaba una excepción y no ocurrió. Eso confirma que la prueba está cumpliendo su rol de detectar comportamientos inesperados.
¿Se puede usar la estructura triple A en pruebas de excepción?
En estos casos te saltas una de las A porque no hay un act que guarde resultado: la ejecución y la comprobación ocurren juntas dentro del Throws. El escenario lo pide así, y está perfectamente válido [07:00].
¿Qué tipo de excepciones debo cubrir con pruebas?
No todas las excepciones merecen prueba unitaria, solo las que forman parte de la lógica controlada del código. Estas son señales que la función emite a propósito para indicar uso indebido.
Ejemplos del proyecto que vale la pena cubrir:
GetStringLen lanza ArgumentNullException cuando recibe null.
TruncateString lanza una excepción cuando MaxLength es menor o igual a cero.
- Cualquier validación de rango o parámetro obligatorio que tu lógica defina.
Como práctica, crea dos pruebas para TruncateString: una que valide el caso exitoso y otra que confirme la excepción cuando el parámetro es inválido. Así cubres ambos caminos del código.
Resumen rápido de los nuevos asserts: StartsWith valida el inicio de un texto, Contains valida que un fragmento esté presente y Throws<T> valida que se lance una excepción específica. Con estos tres tienes herramientas para cubrir escenarios mucho más reales que la simple igualdad.
¿Qué otro escenario te gustaría cubrir con pruebas unitarias en tu propio proyecto? Comparte tu caso en los comentarios.