Tutorial: Crear y ejecutar pruebas unitarias en código administrado
En este artículo se recorre paso a paso la creación, ejecución y personalización de una serie de pruebas unitarias mediante el marco de pruebas unitarias para código administrado de Microsoft y el Explorador de pruebas de Visual Studio. Se empieza con un proyecto C# que está en desarrollo, se crean pruebas que utilizan el código, se ejecutan las pruebas y se examinan los resultados. Luego se cambia el código del proyecto y se vuelven a ejecutar las pruebas. Si quiere obtener información general conceptual de estas tareas antes de seguir estos pasos, consulte Conceptos básicos de prueba unitaria. Si desea generar pruebas automáticamente a partir del código existente, consulte Creación de códigos auxiliares del método de prueba unitaria a partir del código.
Crear un proyecto para pruebas
Abra Visual Studio.
En la ventana de inicio, elija Crear un proyecto nuevo.
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 Aplicación de consola, puede instalarla desde la ventana Crear un proyecto. En el mensaje ¿No encuentra lo que busca? , elija 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.
Asigne al proyecto el nombre Bank y haga clic en Siguiente.
Seleccione 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 se abre en el editor, haga doble clic en el archivo Program.cs en el Explorador de soluciones para abrirlo.
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); } } }
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.
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.
Crear un proyecto de prueba unitaria
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.
Escriba prueba en el cuadro de búsqueda, seleccione C# como lenguaje y, a continuación, seleccione el proyecto de prueba unitaria de MSTest de C# para la plantilla de .NET y, a continuación, haga clic en Siguiente.
Nota:
En Visual Studio 2019 versión 16.9, la plantilla de proyecto MSTest es Proyecto de prueba unitaria.
Asigne al proyecto el nombre BankTests y haga clic en Siguiente.
Seleccione la plataforma de destino recomendada o .NET 8 y, después, elija Crear.
El proyecto BankTests se agrega a la solución Bank.
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.
En el cuadro de diálogo Administrador de referencias, expanda Proyectos, seleccione Solución y active el elemento Bank.
Elija Aceptar.
Crear la clase de prueba
Cree una clase de prueba para comprobar la clase BankAccount
. Puede utilizar el archivo UnitTest1.cs, generado por la plantilla de proyecto, pero asigne al archivo y a la clase nombres más descriptivos.
Cambio de nombre de un archivo y una clase
Para cambiar el nombre del archivo, en el Explorador de soluciones, seleccione el archivo UnitTest1.cs del proyecto BankTests. En el menú contextual, seleccione Cambiar nombre (o presione F2) y cambie el nombre del archivo a BankAccountTests.cs.
Para cambiar el nombre de la clase, sitúe el cursor sobre
UnitTest1
en el editor de código, haga clic con el botón derecho y seleccione Cambiar nombre (o presione F2). Escriba BankAccountTests y presione Entrar.
El archivo BankAccountTests.cs contiene ahora el siguiente código:
// 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 los siguientes:
Se requiere el atributo
[TestClass]
en cualquier clase que contenga métodos de prueba unitaria que quiera ejecutar en el Explorador de pruebas.Cada método de prueba que quiera que el Explorador de pruebas reconozca debe tener el atributo
[TestMethod]
.
Puede tener otras clases de un proyecto de prueba unitaria que no tengan el atributo [TestClass]
y puede tener otros métodos de clases de prueba que no tengan el atributo [TestMethod]
. Puede llamar a estos otros métodos y clases desde los métodos de prueba.
Crear el primer método de prueba
En este procedimiento, escribe 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 ArgumentOutOfRangeException si la cantidad de débito es mayor que el saldo.
El método produce ArgumentOutOfRangeException si la cantidad de débito es menor que cero.
Si la cantidad de débito es válida, el método resta la cantidad de débito del saldo de cuenta.
Sugerencia
Puede eliminar el método TestMethod1
predeterminado, ya que no lo usará en este tutorial.
Para crear un método de prueba
En la primera prueba, se comprueba que una cantidad válida (es decir, una menor que el saldo de cuenta y mayor que cero) retire la cantidad correcta de la cuenta. Agregue el siguiente método 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 luego retira una cantidad válida. Usa el método Assert.AreEqual para comprobar que el saldo de cierre es el esperado. Los métodos como Assert.AreEqual
, Assert.IsTrue y otros se usan con frecuencia en las pruebas unitarias. Para obtener más información conceptual sobre cómo escribir una prueba unitaria, vea Escribir las pruebas.
Requisitos del método de prueba
Un método de prueba debe cumplir los siguientes requisitos:
Es representativo del atributo
[TestMethod]
.Devuelve
void
.No puede tener parámetros.
Compilar y ejecutar la prueba
En el menú Compilación, elija Compilar solución (o presione CTRL + Mayús + B).
Si el explorador de pruebas no está abierto, ábralo eligiendo Prueba>Explorador de pruebas (o Prueba>Windows>Explorador de pruebas) en la barra de menús superior (o presione Ctrl + E, T).
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 serie de pruebas, la barra se vuelve verde si todos los métodos de prueba se completan correctamente o roja si no alguna de las prueba no lo hace.
En este caso, la prueba no se completa correctamente.
Seleccione el método en el Explorador de pruebas para ver los detalles en la parte inferior de la ventana.
Corrija el código y vuelva 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. En el caso del método AreEqual
, el mensaje muestra lo que se esperaba y lo que se ha recibido 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.
Corregir el error
Para corregir el error, en el archivo BankAccount.cs, reemplace la línea:
m_balance += amount;
Por:
m_balance -= amount;
Vuelva a ejecutar la prueba
En el Explorador de pruebas, elija Ejecutar todas para volver a ejecutar la prueba (o presione CTRL + R, V). La barra de color rojo o verde se vuelve verde para indicar que se ha superado la prueba.
Utilice pruebas unitarias para mejorar el código
En esta sección se describe cómo un proceso iterativo de análisis, el desarrollo de pruebas unitarias y la refactorización pueden servirle de ayuda para que el código de producción sea más compacto y eficaz.
Analizar los problemas
Ha creado un método de prueba para confirmar que se resta correctamente una cantidad válida en el método Debit
. Ahora, compruebe que el método produce ArgumentOutOfRangeException si la cantidad 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 que el comportamiento es el correcto cuando la cantidad de débito es menor que 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 declarar que se ha producido la excepción correcta. Este método provoca un error en la prueba, a menos que se produzca ArgumentOutOfRangeException. Si modifica temporalmente el método en pruebas para que produzca una excepción ApplicationException más genérica cuando la cantidad de débito es menor que cero, la prueba se comporta correctamente; es decir, se produce un error.
Para probar el caso en el que la cantidad retirada es mayor que el saldo, siga los siguientes pasos:
Crear un nuevo método de prueba denominado
Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange
.Copiar el cuerpo del método de
Debit_WhenAmountIsLessThanZero_ShouldThrowArgumentOutOfRange
en el nuevo método.Establecer
debitAmount
en un número mayor que el del saldo.
Ejecute las dos pruebas y compruebe que se superan.
Continuar el análisis
Puede mejorar aún más el método que se está probando. Con la implementación actual, no hay ninguna manera de saber qué condición (amount > m_balance
o amount < 0
) ha provocado la excepción producida durante la prueba. Sabemos solo que se ha producido un ArgumentOutOfRangeException
en algún lugar del método. Sería mejor si pudiéramos decir qué condición en BankAccount.Debit
produjo la excepción (amount > m_balance
o amount < 0
) para que pudiéramos estar seguros de que el método está comprobando correctamente el estado de sus argumentos.
Examine de nuevo el método en pruebas (ArgumentOutOfRangeException
) y compruebe que ambas instrucciones condicionales utilizan un constructor BankAccount.Debit
que tan solo toma el nombre del argumento como parámetro:
throw new ArgumentOutOfRangeException("amount");
Puede usar un constructor que proporcione información mucho más completa: ArgumentOutOfRangeException(String, Object, String) incluye el nombre y el valor del argumento, y un mensaje definido por el usuario. Puede refactorizar el método en pruebas para utilizar este constructor. Incluso mejor, puede utilizar miembros de tipo que se encuentran disponibles públicamente para especificar los errores.
Refactorizar el código en pruebas
Primero, defina dos constantes para los mensajes de error en el ámbito de la 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);
}
Refactorizar los métodos de prueba
Refactorice los métodos de prueba mediante la eliminación de 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 ofrece la posibilidad de comparar dos cadenas.
Por tanto, Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange
quedaría de la siguiente manera:
[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);
}
}
Vuelva a probar, reescriba y vuelva a analizar
Actualmente, el método de prueba no controla todos los casos que debería. Si el método sometido a prueba (Debit
) no ha podido iniciar una excepción ArgumentOutOfRangeException cuando debitAmount
era mayor que el saldo (o menor que cero), el método de prueba pasaría. Este escenario no es bueno, porque quiere que el método de prueba no se supere si no se produce ninguna excepción.
Se trata de 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 sigue ejecutándose y se produce un error en el nueva comprobació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 resuelto este problema. La versión final de Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange
tiene el siguiente 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 condujeron a métodos de prueba más eficaces e informativos. Pero lo que es más importante, también mejoraron el código sometido a prueba.
Sugerencia
En este tutorial se utiliza 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 más información, vea Instalar marcos de prueba unitaria de terceros.
Contenido relacionado
Para obtener información sobre cómo ejecutar pruebas desde una línea de comandos, vea Opciones de la línea de comandos para VSTest.Console.exe.