Las pruebas unitarias son una de las herramientas más importantes para garantizar la calidad del software. En aplicaciones desarrolladas con C# y ASP.NET Core, los servicios de negocio contienen la lógica principal del sistema, por lo que deben validarse de forma independiente. Una buena suite de pruebas permite detectar errores antes de llegar a producción, facilita el mantenimiento y brinda confianza al realizar cambios o refactorizaciones.
¿Qué es una prueba unitaria?
Una prueba unitaria verifica el comportamiento de una única unidad de código, normalmente un método o una clase, de manera aislada. Esto significa que no debe depender de bases de datos, servicios externos, APIs ni archivos. En lugar de utilizar estas dependencias reales, se emplean objetos simulados (mocks).
Por ejemplo, si un servicio registra un cliente utilizando un repositorio, la prueba debe validar únicamente la lógica del servicio y no si la base de datos funciona correctamente.
Herramientas recomendadas
En el ecosistema .NET existen varias herramientas ampliamente utilizadas:
- xUnit: Framework moderno para crear pruebas unitarias.
- Moq: Biblioteca para crear objetos simulados (mocks).
- FluentAssertions: Permite escribir validaciones más legibles y expresivas.
- coverlet: Herramienta para medir la cobertura de código.
Estas librerías forman una combinación muy utilizada en proyectos profesionales.
Ejemplo de servicio
Supongamos un servicio que registra usuarios:
public class UserService
{
private readonly IUserRepository _repository;
public UserService(IUserRepository repository)
{
_repository = repository;
}
public async Task Create(User user)
{
if(string.IsNullOrWhiteSpace(user.Name))
throw new ArgumentException("El nombre es obligatorio");
await _repository.Create(user);
}
}
Aquí existen dos reglas de negocio:
- El nombre no puede estar vacío.
- Si el nombre es válido, el usuario debe almacenarse.
Creando la prueba
Una prueba utilizando xUnit y Moq podría ser:
[Fact]
public async Task Create_Should_Save_User_When_Data_Is_Valid()
{
var repository = new Mock<IUserRepository>();
var service = new UserService(repository.Object);
var user = new User
{
Name = "Juan"
};
await service.Create(user);
repository.Verify(r => r.Create(user), Times.Once);
}
La prueba verifica que el repositorio fue llamado exactamente una vez cuando la información es válida.
Probando las reglas de negocio
También es importante comprobar que las validaciones funcionen correctamente.
[Fact]
public async Task Create_Should_Throw_Exception_When_Name_Is_Empty()
{
var repository = new Mock<IUserRepository>();
var service = new UserService(repository.Object);
var user = new User
{
Name = ""
};
await Assert.ThrowsAsync<ArgumentException>(() =>
service.Create(user));
}
Con esta prueba se garantiza que la regla de negocio continúa funcionando incluso después de futuras modificaciones.
Buenas prácticas
Para obtener pruebas realmente útiles, es recomendable seguir algunas prácticas:
- Probar un único comportamiento por prueba.
- Asignar nombres descriptivos a los métodos de prueba.
- Evitar lógica compleja dentro de las pruebas.
- Mantener las pruebas independientes entre sí.
- Simular únicamente las dependencias externas.
- Ejecutarlas automáticamente en cada compilación o pipeline de CI/CD.
Además, conviene utilizar el patrón Arrange – Act – Assert (AAA):
- Arrange: Preparar los datos.
- Act: Ejecutar la acción.
- Assert: Verificar el resultado.
Esta estructura hace que las pruebas sean mucho más fáciles de leer y mantener.
Cobertura de código
Una cobertura alta puede ser positiva, pero no debe convertirse en el único objetivo. Es preferible tener un 80 % de cobertura con pruebas bien diseñadas que un 100 % compuesto por pruebas que no validan comportamientos importantes.
Lo realmente valioso es cubrir los escenarios críticos:
- Validaciones.
- Reglas de negocio.
- Casos exitosos.
- Casos de error.
- Excepciones.
- Límites de entrada.
Conclusión
Las pruebas unitarias son una inversión que reduce errores, facilita el mantenimiento y aumenta la confianza al evolucionar una aplicación. En C#, herramientas como xUnit, Moq y FluentAssertions permiten construir pruebas claras, rápidas y fáciles de mantener. Al centrarse en validar la lógica de negocio de forma aislada, los equipos pueden desarrollar nuevas funcionalidades con mayor seguridad, detectar fallos desde etapas tempranas y mantener una base de código más robusta, escalable y preparada para crecer con el tiempo.

