Compartir a través de


Tutorial: Creación y ejecución de pruebas unitarias para código administrado

En este artículo se explica cómo crear, ejecutar y personalizar una serie de pruebas unitarias mediante el marco de pruebas unitarias de Microsoft para código administrado y Visual Studio Explorador de pruebas. Comienza con un proyecto de C# que está en desarrollo, crea pruebas que ejercen su código, ejecutan las pruebas y examinan los resultados. A continuación, cambie el código del proyecto y vuelva a ejecutar las pruebas. Si desea obtener información general conceptual de estas tareas antes de seguir estos pasos, consulte Conceptos básicos de pruebas unitarias. Si desea generar pruebas automáticamente a partir del código existente, consulte Crear métodos de prueba unitaria a partir del código.

Creación de un proyecto para probar

  1. Abra Visual Studio.

  2. En la ventana de inicio, elija Crear un nuevo proyecto.

  3. Busque y seleccione la plantilla de proyecto de C# Aplicación de consola para .NET y haga clic en Siguiente.

    Nota

    Si no ve la plantilla Console App, puede instalarla desde la ventana Crear un nuevo proyecto. En el mensaje ¿No encuentras lo que estás buscando?, selecciona el vínculo Instalar más herramientas y características. A continuación, en el Instalador de Visual Studio, elija la carga de trabajo Desarrollo de escritorio de .NET.

  4. Asigne al proyecto el nombre Banky haga clic en Siguiente.

    Elija la plataforma de destino recomendada o .NET 8 y, después, elija Crear.

    Se crea el proyecto Bank y se muestra en el Explorador de soluciones con el archivo Program.cs abierto en el editor de código.

    Nota

    Si Program.cs no está abierto en el editor, haga doble clic en el archivo Program.cs en Explorador de soluciones para abrirlo.

  5. Reemplace el contenido de Program.cs por el siguiente código de C# que define una clase, BankAccount:

    using System;
    
    namespace BankAccountNS
    {
        /// <summary>
        /// Bank account demo class.
        /// </summary>
        public class BankAccount
        {
            private readonly string m_customerName;
            private double m_balance;
    
            private BankAccount() { }
    
            public BankAccount(string customerName, double balance)
            {
                m_customerName = customerName;
                m_balance = balance;
            }
    
            public string CustomerName
            {
                get { return m_customerName; }
            }
    
            public double Balance
            {
                get { return m_balance; }
            }
    
            public void Debit(double amount)
            {
                if (amount > m_balance)
                {
                    throw new ArgumentOutOfRangeException("amount");
                }
    
                if (amount < 0)
                {
                    throw new ArgumentOutOfRangeException("amount");
                }
    
                m_balance += amount; // intentionally incorrect code
            }
    
            public void Credit(double amount)
            {
                if (amount < 0)
                {
                    throw new ArgumentOutOfRangeException("amount");
                }
    
                m_balance += amount;
            }
    
            public static void Main()
            {
                BankAccount ba = new BankAccount("Mr. Bryan Walton", 11.99);
    
                ba.Credit(5.77);
                ba.Debit(11.22);
                Console.WriteLine("Current balance is ${0}", ba.Balance);
            }
        }
    }
    
  6. Cambie el nombre del archivo a BankAccount.cs al hacer clic con el botón derecho y elegir Cambiar nombre en el Explorador de soluciones.

  7. En el menú Compilación, haga clic en Compilar solución (o presione CTRL + Mayús + B).

Ahora tiene un proyecto con métodos que puede probar. En este artículo, las pruebas se centran en el método Debit. Se llama al método Debit cuando se retira dinero de una cuenta.

Creación de un proyecto de prueba unitaria

  1. En el menú Archivo, seleccione Agregar>Nuevo proyecto.

    Sugerencia

    También puede hacer clic con el botón derecho en la solución en el Explorador de soluciones y elegir Agregar>Nuevo proyecto.

  2. Escriba prueba en el cuadro de búsqueda, seleccione C# como lenguaje y, a continuación, seleccione el proyecto de prueba de MSTest de C# para la plantilla de .NET y, a continuación, haga clic en Siguiente.

    Nota

    En la versión 16.9 de Visual Studio 2019, la plantilla de proyecto MSTest es proyecto de pruebas unitarias.

  3. Asigne al proyecto el nombre BankTests y haga clic en Siguiente.

  4. Elija la plataforma de destino recomendada o .NET 8 y, después, elija Crear.

    A partir de la versión 17.10 de Visual Studio 2022, también puede seleccionar un ejecutor de pruebas. Para el ejecutor de pruebas, puede elegir VSTest o MSTest. Para obtener más información sobre la diferencia que hay entre los ejecutores de pruebas, consulte comparación de Microsoft.Testing.Platform y VSTest.

    El proyecto BankTests se agrega a la solución Bank.

  5. En el proyecto BankTests, agregue una referencia al proyecto Bank.

    En el Explorador de soluciones, seleccione Dependencias en el proyecto BankTests y luego seleccione Agregar referencia (o Agregar referencia de proyecto) en el menú contextual.

  6. En el cuadro de diálogo Administrador de referencias, expanda Proyectos, seleccione Solución y active el elemento Bank.

  7. Elija Aceptar.

Creación de la clase de prueba

Cree una clase de prueba para comprobar la clase BankAccount. Puede usar el archivo UnitTest1.cs generado por la plantilla de proyecto, pero asignarle nombres más descriptivos al archivo y a la clase.

Cambiar el nombre de un archivo y una clase

  1. Para cambiar el nombre del archivo, en Explorador de soluciones, seleccione el archivo UnitTest1.cs en el proyecto BankTests. En el menú contextual, elija Cambiar nombre (o presione F2) y cambie el nombre del archivo a BankAccountTests.cs.

  2. Para cambiar el nombre de la clase, coloque el cursor en UnitTest1 en el editor de código, haga clic con el botón derecho y elija Cambiar nombre (o presione F2). Escriba BankAccountTests y presione Entrar.

El archivo BankAccountTests.cs ahora contiene el código siguiente:

// The 'using' statement for Test Tools is in GlobalUsings.cs
// using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace BankTests
{
    [TestClass]
    public class BankAccountTests
    {
        [TestMethod]
        public void TestMethod1()
        {
        }
    }
}

Agregar una instrucción using

Agregue una instrucción using a la clase de prueba para poder llamar al proyecto que se está probando sin usar nombres completos. En la parte superior del archivo de clase, agregue:

using BankAccountNS;

Requisitos de la clase de prueba

Los requisitos mínimos para una clase de prueba son:

  • El atributo [TestClass] es necesario en cualquier clase que contenga métodos de prueba unitaria que desee ejecutar en el Explorador de pruebas.

  • Cada método de prueba que quiera que reconozca el Explorador de pruebas debe tener el atributo [TestMethod].

Puede tener otras clases en un proyecto de prueba unitaria que no tengan el atributo [TestClass] y puede tener otros métodos en clases de prueba que no tengan el atributo [TestMethod]. Puede llamar a estos otros métodos y clases desde los métodos de prueba.

Creación del primer método de prueba

En este procedimiento, escribirá métodos de prueba unitaria para comprobar el comportamiento del método Debit de la clase BankAccount.

Hay al menos tres comportamientos que deben comprobarse:

  • El método produce un ArgumentOutOfRangeException si el importe del débito es mayor que el saldo.

  • El método produce un ArgumentOutOfRangeException si el importe de débito es menor que cero.

  • Si el importe del débito es válido, el método resta el importe de débito del saldo de la cuenta.

Sugerencia

Puede eliminar el método TestMethod1 predeterminado, ya que no lo usará en este tutorial.

Para crear un método de prueba

La primera prueba comprueba que una cantidad válida (es decir, una que sea menor que el saldo de la cuenta y mayor que cero) retire la cantidad correcta de la cuenta. Agregue el método siguiente a esa clase BankAccountTests:

[TestMethod]
public void Debit_WithValidAmount_UpdatesBalance()
{
    // Arrange
    double beginningBalance = 11.99;
    double debitAmount = 4.55;
    double expected = 7.44;
    BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);

    // Act
    account.Debit(debitAmount);

    // Assert
    double actual = account.Balance;
    Assert.AreEqual(expected, actual, 0.001, "Account not debited correctly");
}

El método es sencillo: configura un nuevo objeto BankAccount con un saldo inicial y, a continuación, retira una cantidad válida. Usa el método Assert.AreEqual para comprobar que el saldo final es el esperado. Los métodos como Assert.AreEqual, Assert.IsTruey otros se usan con frecuencia en las pruebas unitarias. Para obtener más información conceptual sobre cómo escribir una prueba unitaria, consulte Escribir las pruebas.

Requisitos del método de prueba

Un método de prueba debe cumplir los siguientes requisitos:

  • Está decorado con el atributo [TestMethod].

  • Devuelve void.

  • No puede tener parámetros.

Compilación y ejecución de la prueba

  1. En el menú Compilación, elija Compilar solución (o presione CTRL + Mayús + B).

  2. Si Explorador de Pruebas no está abierto, ábralo eligiendo Test>Explorador de Pruebas (o Test>Windows>Explorador de Pruebas) en la barra de menú superior (o presione Ctrl + E, T).

  3. Elija Ejecutar todas para ejecutar la prueba (o presione CTRL + R, V).

    Mientras se ejecuta la prueba, la barra de estado de la parte superior de la ventana Explorador de pruebas está animada. Al final de la ejecución de la prueba, la barra se vuelve verde si se superan todos los métodos de prueba o rojo si se produce un error en alguna de las pruebas.

    En este caso, se produce un error en la prueba.

  4. Seleccione el método en el Explorador de pruebas para ver los detalles en la parte inferior de la ventana.

Corregir el código y volver a ejecutar las pruebas

El resultado de la prueba contiene un mensaje que describe el error. Es posible que tenga que explorar en profundidad para ver este mensaje. Para el método AreEqual, el mensaje muestra lo que se esperaba y lo que se recibió realmente. Esperaba que se redujera el saldo pero, en su lugar, aumentó en la cantidad retirada.

La prueba unitaria puso al descubierto un error: la cantidad retirada se agrega al saldo de cuenta en lugar de ser restada.

Corrección del error

Para corregir el error, en el archivo BankAccount.cs, reemplace la línea:

m_balance += amount;

Por:

m_balance -= amount;

Volver a ejecutar la prueba

En Explorador de Pruebas, elija Ejecutar todo para volver a ejecutar la prueba, (o presione Ctrl + R, V). La barra roja o verde se vuelve verde para indicar que se ha superado la prueba.

Explorador de pruebas en Visual Studio 2019 mostrando la prueba superada

Explorador de pruebas en Visual Studio 2019 mostrando prueba superada

Uso de pruebas unitarias para mejorar el código

En esta sección se describe cómo un proceso iterativo de análisis, desarrollo de pruebas unitarias y refactorización puede ayudarle a hacer que el código de producción sea más sólido y eficaz.

Análisis de los problemas

Ha creado un método de prueba para confirmar que una cantidad válida se deduce correctamente en el método Debit. Ahora, compruebe que el método lanza un error ArgumentOutOfRangeException si el importe de débito es:

  • mayor que el saldo, o
  • menor que cero.

Creación y ejecución de nuevos métodos de prueba

Cree un método de prueba para comprobar el comportamiento correcto cuando la cantidad de débito sea inferior a cero:

[TestMethod]
public void Debit_WhenAmountIsLessThanZero_ShouldThrowArgumentOutOfRange()
{
    // Arrange
    double beginningBalance = 11.99;
    double debitAmount = -100.00;
    BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);

    // Act and assert
    Assert.ThrowsException<System.ArgumentOutOfRangeException>(() => account.Debit(debitAmount));
}

Use el método ThrowsException para afirmar que se ha producido la excepción correcta. Este método hace que la prueba falle a menos que se lance un ArgumentOutOfRangeException. Si modifica temporalmente el método sometido a prueba para lanzar una ApplicationException más genérica cuando la cantidad de débito es menor que cero, la prueba se comporta de manera correcta, es decir, falla.

Para probar el caso cuando el importe retirado es mayor que el saldo, siga estos pasos:

  1. Cree un nuevo método de prueba denominado Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange.

  2. Copie el cuerpo del método de Debit_WhenAmountIsLessThanZero_ShouldThrowArgumentOutOfRange al nuevo método.

  3. Establecer debitAmount en un número mayor que el del saldo.

Ejecute las dos pruebas y compruebe que superan.

Continuar el análisis

El método que se está probando se puede mejorar aún más. Con la implementación actual, no tenemos forma de saber qué condición (amount > m_balance o amount < 0) llevaron a la excepción que se produce durante la prueba. Sabemos solo que se ha producido un ArgumentOutOfRangeException en algún lugar del método. Sería mejor si pudiéramos saber qué condición en BankAccount.Debit provocó que se lanzara la excepción (amount > m_balance o amount < 0) de modo que podamos estar seguros de que nuestro método está verificando correctamente sus argumentos.

Examine de nuevo el método que se está probando (BankAccount.Debit) y observe que ambas instrucciones condicionales usan un constructor ArgumentOutOfRangeException que solo toma el nombre del argumento como parámetro:

throw new ArgumentOutOfRangeException("amount");

Hay un constructor que puede usar para enviar informes de información mucho más completa: ArgumentOutOfRangeException(String, Object, String) incluye el nombre del argumento, el valor del argumento y un mensaje definido por el usuario. Puede refactorizar el método en prueba para usar este constructor. Incluso mejor, puede utilizar miembros de tipo que se encuentran disponibles públicamente para especificar los errores.

Refactorización del código en prueba

En primer lugar, defina dos constantes para los mensajes de error en el ámbito de clase. Coloque las definiciones en la clase en prueba BankAccount:

public const string DebitAmountExceedsBalanceMessage = "Debit amount exceeds balance";
public const string DebitAmountLessThanZeroMessage = "Debit amount is less than zero";

A continuación, modifique las dos instrucciones condicionales en el método Debit:

if (amount > m_balance)
{
    throw new System.ArgumentOutOfRangeException("amount", amount, DebitAmountExceedsBalanceMessage);
}

if (amount < 0)
{
    throw new System.ArgumentOutOfRangeException("amount", amount, DebitAmountLessThanZeroMessage);
}

Refactorización de los métodos de prueba

Refactorice los métodos de prueba quitando la llamada a Assert.ThrowsException. Encapsule la llamada a Debit() en un bloque try/catch, detecte la excepción concreta que se espera y compruebe su mensaje asociado. El método Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.Contains proporciona la capacidad de comparar dos cadenas.

Ahora, el Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange podría tener este aspecto:

[TestMethod]
public void Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange()
{
    // Arrange
    double beginningBalance = 11.99;
    double debitAmount = 20.0;
    BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);

    // Act
    try
    {
        account.Debit(debitAmount);
    }
    catch (System.ArgumentOutOfRangeException e)
    {
        // Assert
        StringAssert.Contains(e.Message, BankAccount.DebitAmountExceedsBalanceMessage);
    }
}

Volver a probar, reescribir y volver a analizar

Actualmente, el método de prueba no maneja todos los casos que debería. Si el método sometido a prueba, el método Debit, no lanzaba un ArgumentOutOfRangeException cuando el debitAmount era mayor que el saldo (o menor que cero), el método de prueba pasaría. Este escenario no es bueno porque queréis que el método de prueba falle si no se lanza una excepción.

Este resultado es un error en el método de prueba. Para resolver el problema, agregue una validación Assert.Fail al final del método de prueba para controlar el caso donde no se produce ninguna excepción.

Al volver a ejecutar la prueba, se muestra que se produce un error en ella si se detecta la excepción correcta. El bloque catch detecta la excepción, pero el método continúa ejecutándose y se produce un error en la nueva aserción de Assert.Fail. Para resolver este problema, agregue una instrucción return después de StringAssert en el bloque catch. Al volver a ejecutar la prueba se confirma que ha corregido este problema. La versión final del Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange tiene este aspecto:

[TestMethod]
public void Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange()
{
    // Arrange
    double beginningBalance = 11.99;
    double debitAmount = 20.0;
    BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);

    // Act
    try
    {
        account.Debit(debitAmount);
    }
    catch (System.ArgumentOutOfRangeException e)
    {
        // Assert
        StringAssert.Contains(e.Message, BankAccount.DebitAmountExceedsBalanceMessage);
        return;
    }

    Assert.Fail("The expected exception was not thrown.");
}

Conclusión

Las mejoras en el código de prueba llevaron a métodos de prueba más sólidos e informativos. Pero lo más importante es que también mejoraron el código sometido a prueba.

Sugerencia

En este tutorial se usa el marco de pruebas unitarias de Microsoft para código administrado. El Explorador de pruebas también puede ejecutar pruebas desde marcos de pruebas unitarias de terceros que tengan adaptadores para el Explorador de pruebas. Para obtener más información, consulte Instalación de marcos de pruebas unitarias de terceros.

Para obtener información sobre cómo ejecutar pruebas desde una línea de comandos, consulte VSTest.Console.exe opciones de línea de comandos.