TDD

3 Ejemplos de TDD en .NET C#

En este artículo, vamos a dar una breve introducción a la metodología de desarrollo guiado por pruebas (TDD, por sus siglas en inglés). Veremos cómo funciona TDD y cómo se aplica en el desarrollo de software, así como sus ventajas y desventajas. También mostraremos 3 ejemplos prácticos de TDD en C# .NET para que puedas ver cómo se aplica en la práctica. Si estás interesado en aprender más sobre TDD y cómo puede ayudarte a desarrollar software de alta calidad, no te pierdas este artículo.

Introducción a TDD (Test Driven Development)

¿Qué es TDD?

TDD es una metodología de desarrollo de software que consiste en escribir primero las pruebas automatizadas para una funcionalidad antes de escribir el código de la funcionalidad. El objetivo de TDD es asegurar que el código cumpla con los requisitos especificados en las pruebas y detecte tempranamente los errores y defectos.

Ventajas y desventajas TDD

Una de las ventajas principales de TDD es que ayuda a desarrollar código limpio y bien diseñado. Al escribir pruebas antes del código, se piensa en cómo la funcionalidad debe ser utilizada y se diseña el código de manera que sea fácil de probar. Además, al tener pruebas automatizadas, se puede detectar tempranamente los errores y defectos en el código, lo que facilita su corrección.

TDD también ayuda a mejorar la calidad del código al hacer que sea más fácil de refactorizar. Al tener pruebas automatizadas, se pueden realizar cambios en el código con confianza, sabiendo que las pruebas detectarán si se rompe alguna funcionalidad existente.

Sin embargo, TDD puede ser tedioso y requerir más tiempo para escribir pruebas automatizadas para cada pequeño cambio en el código. Además, puede ser difícil escribir pruebas para funcionalidades complejas. Por lo tanto, se recomienda usar TDD solo para funcionalidades críticas y utilizar otras técnicas de prueba para funcionalidades menos críticas.

En general, TDD es un enfoque de desarrollo de software útil para mejorar la calidad del código, detectar tempranamente los errores y facilitar el proceso de refactorización. Sin embargo, requiere un poco más de tiempo y esfuerzo para escribir pruebas automatizadas.

«Red, Green, Refactor»

TDD se divide en tres pasos:

  • escribir una prueba automatizada,
  • escribir el código necesario para hacer pasar la prueba
  • y refactorizar el código para mejorar su calidad.

Este ciclo se repite hasta que todas las funcionalidades requeridas estén implementadas y pasen las pruebas.

El ciclo de TDD se conoce como «Red, Green, Refactor» debido a los colores utilizados para indicar el estado de las pruebas. El ciclo se inicia escribiendo una prueba automatizada para una funcionalidad específica. En este paso, se espera que la prueba falle (red) ya que no se ha escrito el código de la funcionalidad aún.
Una vez escrita la prueba, se escribe el código necesario para hacer pasar la prueba. En este paso, se espera que el código haga pasar la prueba (green) y se verifique que cumple con los requisitos especificados en la prueba.
Finalmente, se refactoriza el código para mejorar su calidad, asegurando que las pruebas sigan pasando (Refactor). El ciclo se repite hasta que todas las funcionalidades requeridas estén implementadas y pasen las pruebas.
En resumen «Green, Red, Refactor» representa la secuencia de escribir primero una prueba automatizada, luego escribir el código necesario para hacer pasar la prueba y finalmente refactorizando el código para mejorar su calidad y asegurando que las pruebas sigan pasando.

¿Cuándo usar TDD?

Hay algunas situaciones en las que se recomienda especialmente usar TDD:

  1. Proyectos con requisitos cambiantes: TDD ayuda a detectar tempranamente los cambios en los requisitos y a asegurar que el código sigue cumpliendo con ellos.
  2. Proyectos con un alto riesgo de fallos: TDD ayuda a detectar y corregir errores y defectos tempranamente, lo que reduce el riesgo de fallos en el sistema.
  3. Proyectos con un alto grado de complejidad: TDD ayuda a asegurar que el código se está construyendo de manera incremental y controlable, lo que facilita el manejo de proyectos complejos.
  4. Proyectos con un equipo descentralizado: TDD ayuda a asegurar que todos los miembros del equipo entienden los requisitos y que el código cumple con ellos.

En general, TDD es una metodología que puede ser beneficiosa para cualquier proyecto de desarrollo de software, pero es especialmente útil en proyectos con un alto riesgo de fallos, un alto grado de complejidad o un equipo descentralizado.

Veamos el código👇

Ahora que conocemos un poco mejor TDD, mostraremos 3 ejemplos prácticos en C# .NET para que puedas ver cómo se aplica en la práctica.

Si tu café está listo comencemos.☕️

Primer ejemplo básico de TDD en C# .NET

Aquí te muestro un ejemplo básico de TDD en C# .NET:

1. Escribir una prueba automatizada para una funcionalidad específica. Por ejemplo, si queremos probar una clase de suma, podríamos escribir una prueba como esta:

[Fact]
public void Sum_TwoNumbers_ReturnsSum()
{
    // Arrange
    var calculator = new Calculator();
    var a = 2;
    var b = 3;
    var expected = 5;

    // Act
    var result = calculator.Sum(a, b);

    // Assert
    Assert.Equal(expected, result);
}

2. Escribir el código necesario para hacer pasar la prueba. En este ejemplo, escribiríamos la implementación de la clase Calculator:

public class Calculator
{
    public int Sum(int a, int b)
    {
        return a + b;
    }
}

3. Ejecutar las pruebas y asegurarse de que pasen. En este caso, la prueba debería pasar ya que la suma de 2 y 3 es 5.

4. Refactorizar el código para mejorar su calidad, asegurando que las pruebas sigan pasando.

Es importante mencionar que este es solo un ejemplo simple y en la práctica las pruebas automatizadas pueden ser más complejas, pero el proceso es el mismo, escribir primero las pruebas automatizadas, luego escribir el código y finalmente refactorizando el código para mejorar su calidad y asegurando que las pruebas sigan pasando.

Segundo ejemplo de TDD en C# .NET

Aquí te muestro un ejemplo de TDD en C# .NET de una funcionalidad de una clase de una aplicación web:

1. Escribir una prueba automatizada para una funcionalidad específica. Por ejemplo, si queremos probar una clase que valida si un usuario es administrador o no. Podríamos escribir una prueba como esta:

[Fact]
public void IsAdmin_AdminUser_ReturnsTrue()
{
    // Arrange
    var user = new User { Role = "admin" };
    var validator = new UserValidator();

    // Act
    var result = validator.IsAdmin(user);

    // Assert
    Assert.True(result);
}

2. Escribir el código necesario para hacer pasar la prueba. En este ejemplo, escribiríamos la implementación de la clase UserValidator:

public class UserValidator
{
    public bool IsAdmin(User user)
    {
        return user.Role == "admin";
    }
}

3. Ejecutar las pruebas y asegurarse de que pasen. En este caso, la prueba debería pasar ya que el usuario tiene un rol de administrador.

4. Refactorizar el código para mejorar su calidad, asegurando que las pruebas sigan pasando.

En este ejemplo se puede ver como se esta verificando si un usuario es administrador o no, mediante la clase UserValidator, se pueden agregar más pruebas para distintos casos y escenarios, como por ejemplo un usuario que no es administrador o un usuario que no existe.

De esta manera, se pueden ir agregando funcionalidades y pruebas automatizadas para cada una de ellas, garantizando que el código cumpla con los requisitos especificados y detectando tempranamente los errores y defectos.

Tercer ejemplo de TDD en C# .NET

Aquí te muestro un ejemplo más complejo de TDD en C# .NET de una funcionalidad de una aplicación web:

1. Escribir una prueba automatizada para una funcionalidad específica. Por ejemplo, si queremos probar una clase que realiza una búsqueda en una base de datos y devuelve un conjunto de resultados. Podríamos escribir una prueba como esta:

[Fact]
public void Search_ValidTerm_ReturnsResults()
{
    // Arrange
    var context = new SampleDbContext();
    var searcher = new DataSearcher(context);
    var term = "example";
    var expectedResults = new List<Customer> { new Customer { Name = "example 1" }, new Customer { Name = "example 2" } };

    // Act
    var results = searcher.Search(term);

    // Assert
    Assert.All(expectedResults, (expected, i) => Assert.Equal(expected.Name, results[i].Name));
}

2. Escribir el código necesario para hacer pasar la prueba. En este ejemplo, escribiríamos la implementación de la clase «DataSearcher» :

public class SampleDbContext : DbContext
{
    public DbSet<Customer> Customers { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=testDB;Trusted_Connection=True;");
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Customer>().HasData(
            new Customer { Name = "example 1" },
            new Customer { Name = "example 2" }
        );
    }
}

public class Customer
{
    [Key]
    public string? Name { get; set; }
}

3. Ejecutar las pruebas y asegurarse de que pasen. En este caso, la prueba debería pasar ya que la búsqueda devuelve los resultados esperados.

4. Refactorizar el código para mejorar su calidad, asegurando que las pruebas sigan pasando. En este ejemplo, se podrían refactorizar aspectos como la forma en que se realiza la búsqueda en la base de datos para mejorar el rendimiento, o se podría agregar validaciones adicionales en el código para garantizar que se están recibiendo los parámetros necesarios.

Este ejemplo ilustra como se puede aplicar TDD en una funcionalidad que involucra acceso a una base de datos y un procesamiento de datos. Es importante mencionar que en este caso se esta simulando una conexión a base de datos, pero en una aplicación real se usarian las librerias y conexiones necesarias para acceder a una base de datos.

De esta manera, se pueden ir agregando funcionalidades y pruebas automatizadas para cada una de ellas, garantizando que el código cumpla con los requisitos especificados y detectando tempranamente los errores y defectos.

Conclusión

En resumen, TDD nos ayudará a garantizar que el código cumpla con los requisitos específicos y detecte tempranamente errores y defectos mediante la escritura de pruebas automatizadas antes de escribir el código del sistema. Es especialmente útil en proyectos con alto riesgo de fallos, complejidad y equipos descentralizados.

Eso es todo, por el momento, seguiré actualizando y agregando contenido en este post. Espero que te haya resultado interesante😉

Código de ejemplo

Samples TDD .NET / C#

1