Tutorial: Crear y ejecutar pruebas unitarias en código administrado
Este tutorial le permite recorrer paso a paso la creación, ejecución y personalización de pruebas unitarias mediante el marco de pruebas unitarias de Microsoft para el código administrado 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.Por último, puede cambiar el código del proyecto y volver a ejecutar las pruebas.
Este tema contiene las siguientes secciones:
Crear un proyecto de pruebas unitarias.
Crear el primer método de prueba
Corrija el código y vuelva a ejecutar las pruebas
Utilice pruebas unitarias para mejorar el código
[!NOTA]
Este tutorial utiliza el marco de pruebas unitarias de Microsoft para código administrado.El Explorador de pruebas también puede ejecutar pruebas de marcos de pruebas unitarias de terceros, que tienen adaptadores para el Explorador de pruebas.Para obtener más información, consulte Cómo: Instalar frameworks de prueba unitaria de terceros
[!NOTA]
Para obtener información acerca de cómo ejecutar pruebas desde una línea de comandos, vea Tutorial: Utilizar la utilidad de prueba de la línea de comandos.
Requisitos previos
- El proyecto del banco.Vea Proyecto de ejemplo para crear pruebas unitarias.
Prepare el tutorial
Para preparar el tutorial
Abra Visual Studio 2012.
En el menú Archivo, elija Nuevo y haga clic en Proyecto.
Aparecerá el cuadro de diálogo Nuevo proyecto.
En Plantillas instaladas, haga clic en Visual C#.
En la lista de tipos de aplicación, haga clic en Biblioteca de clases.
En el cuadro Nombre, escriba Bank y, a continuación, haga clic en Aceptar.
[!NOTA]
Si el nombre "Bank" ya está en uso, elija otro nombre para el proyecto.
Se crea el nuevo proyecto Bank y se muestra en el Explorador de soluciones con el archivo Class1.cs abierto en el editor de código.
[!NOTA]
Si el archivo Class1.cs no se abre en el editor de código, en el Explorador de soluciones, haga doble clic en el archivo para abrirlo.
Copie el código fuente de Proyecto de ejemplo para crear pruebas unitarias.
Reemplace el contenido original de Class1.cs con el código de Proyecto de ejemplo para crear pruebas unitarias.
Guarde el archivo como BankAccount.cs
En el menú Compilar, haga clic en Compilar solución.
Ahora tiene un proyecto denominado Bankque contiene código fuente para realizar pruebas y las herramientas necesarias para ello.El espacio de nombres de Bank, BankAccountNS, contiene la clase pública BankAccount cuyos métodos probará en los procedimientos siguientes.
Este tutorial se centra en el método Debit. Se llama al método Debit cuando se retira dinero de una cuenta y contiene el siguiente código:
// method under test
public void Debit(double amount)
{
if(amount > m_balance)
{
throw new ArgumentOutOfRangeException("amount");
}
if (amount < 0)
{
throw new ArgumentOutOfRangeException("amount");
}
m_balance += amount;
}
Crear un proyecto de pruebas unitarias.
Requisito previo: siga los pasos del procedimiento Preparar el tutorial.
Para crear un proyecto de pruebas unitarias
En el menú Archivo, elija Agregar y, a continuación, elija Nuevo proyecto....
En el cuadro de diálogo Nuevo proyecto, expanda Instalado, expanda **Visual C#**y, luego, elija Prueba.
En la lista de plantillas, seleccione Proyecto de prueba unitaria.
En el cuadro Nombre, escriba BankTest y elija Aceptar.
El proyecto BankTests se agrega a la solución de Bank.
En el proyecto BankTests, agregue una referencia a la solución de Bank.
En el Explorador de soluciones, seleccione Referencias en el proyecto BankTests y, después, elija Agregue la referencia… desde el acceso directo.
En el cuadro de diálogo del Administrador de referencia, expanda Solución y active el elemento Bank.
Crear la clase de prueba
Se necesita una clase de prueba para comprobar la clase BankAccount.Podemos utilizar UnitTest1.cs, generado por la plantilla de proyecto, pero se debe asignar al archivo y ordenar nombres más descriptivos.Se puede hacer en un paso cambiando el nombre del archivo en el Explorador de soluciones.
Cambiar el nombre de un archivo de clase
En el Explorador de soluciones, seleccione el archivo UnitTest1.cs en el proyecto BankTests.Desde el acceso directo, elija Cambiar nombre y después cambie el nombre del archivo a BankAccountTests.cs.Elija Sí en el cuadro de diálogo que pregunta si desea cambiar todas las referencias del proyecto al elemento de código “UnitTest1”.En este paso se cambia el nombre de la clase a BankAccountTest.
El archivo de BankAccountTests.cs ahora contiene el siguiente código:
// unit test code
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace BankTests
{
[TestClass]
public class BankAccountTests
{
[TestMethod]
public void TestMethod1()
{
}
}
}
Agregue una sentencia using al proyecto en pruebas
Se puede agregar una sentencia using a la clase para poder llamar al proyecto en pruebas, sin utilizar 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:
El atributo [TestClass] se requiere en el marco de las pruebas unitarias de Microsoft para el código administrado, para cualquier clase que contenga los métodos de la prueba unitaria que desea ejecutar en el Explorador de pruebas.
Cada método de prueba que desea que el Explorador de pruebas para ejecutar debe tener el atributo de [TestMethod].
Puede tener otras clases de un proyecto de prueba unitaria que no tienen el atributo de [TestClass], y puede tener otros métodos de las clases de prueba que no tienen el atributo de [TestMethod].Puede utilizar estas otras clases y métodos de los métodos de prueba.
Crear el primer método de prueba
En este procedimiento, se escribirán métodos de prueba unitaria para comprobar el comportamiento del método Debit de la clase BankAccount.El método se ha mostrado anteriormente.
Analizando el método en pruebas, se determina que hay al menos tres comportamientos que deben comprobarse:
El método produce [ArgumentOutOfRangeException] si la cantidad de crédito es mayor que el saldo.
También produce ArgumentOutOfRangeException si la cantidad de crédito es menor que cero.
Si se cumplen las verificaciones 1.) y 2.), el método resta la cantidad del saldo de cuenta.
En la primera prueba, se comprueba que una cantidad válida (una menor que el saldo de cuenta y mayor que cero) retire la cantidad correcta de la cuenta.
Para crear un método de prueba
Agregue una sentencia using BankAccountNS; al archivo de BankAccountTests.cs.
Agregue el siguiente método BankAccountTests a la clase:
// unit test code [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 bastante sencillo.Se configura un nuevo objeto BankAccount con un saldo inicial y después se retira una cantidad válida.Se utiliza el método AreEqual del marco de pruebas unitarias de Microsoft para código administrado, para comprobar que el saldo de cierre era el esperado.
Requisitos del método de prueba
Un método de prueba debe cumplir los siguientes requisitos:
El método se debe señalar con el atributo [TestMethod].
El método debe devolver void.
El método no puede tener parámetros.
Compilar y ejecutar la prueba
Para compilar y ejecutar la prueba
En el menú Compilar, elija Compilar solución.
Si no hay ningún error, la ventana de UnitTestExplorer aparece con Debit_WithValidAmount_UpdatesBalance enumerado en el grupo Pruebas no ejecutadas.Si no aparece el Explorador de pruebas después de una compilación correcta, elija Prueba en el menú, luego elija Ventanas y elija Explorador de pruebas.
Elija Ejecutar todas para ejecutar la prueba.Mientras la prueba se ejecuta, la barra de estado en la parte superior de la ventana se anima.Al final de la ejecución de pruebas, la barra se vuelve verde si todos pasan los métodos de prueba, o rojo si no se pasa alguna prueba.
En este caso, la prueba no se pasa.El método de prueba se mueve a Pruebas no superadas.determinado.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
Analice los resultados de las pruebas
El resultado de la prueba contiene un mensaje que describe el error.Para el método AreEquals, el mensaje muestra qué se esperaba (el parámetro (Esperado<XXX>) y qué se recibió realmente (el parámetro Actual<YYY> ).Se esperaba una disminución en el saldo comparado con el inicial, pero, en cambio, ha aumentado por la cantidad retirada.
Un nuevo examen del código Debit muestra que la prueba unitaria ha tenido éxito encontrando un error.La cantidad retirada se agrega al saldo de cuenta en lugar de ser restado.
Corregir el error
Para corregir el error, reemplace simplemente la línea
m_balance += amount;
con
m_balance -= amount;
Vuelva a ejecutar la prueba
En el Explorador de pruebas, elija Ejecutar todas para volver a ejecutar la prueba.La barra de color rojo o verde se vuelve verde y la prueba se mueve al grupo de Pruebas superadas .
Utilice pruebas unitarias para mejorar el código
Esta sección describe cómo un proceso iterativo de análisis, desarrollo de pruebas unitarias y de refactorización puede ayudarle a hacer más compacto y eficaz el código de producción.
Analizar los problemas
Después de crear un método de prueba para confirmar que se reste correctamente una cantidad válida en el método Debit, podemos volver a los casos restantes en nuestro análisis original:
El método produce ArgumentOutOfRangeException si la cantidad de crédito es mayor que el saldo.
También produce ArgumentOutOfRangeException si la cantidad de crédito es menor que cero.
Crear los métodos de prueba
El primer intento de crear un método de prueba para resolver estos problemas es prometedor:
//unit test method
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void Debit_WhenAmountIsLessThanZero_ShouldThrowArgumentOutOfRange()
{
// arrange
double beginningBalance = 11.99;
double debitAmount = -100.00;
BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);
// act
account.Debit(debitAmount);
// assert is handled by ExpectedException
}
Se usa el atributo ExpectedExceptionAttribute para validar que se ha producido la excepción correcta.El atributo hace que la prueba dé error a menos que se produzca ArgumentOutOfRangeException.Ejecutar las pruebas con valores positivos y negativos de debitAmount y después modificar temporalmente el método probado para producir una excepción genérica ApplicationException cuando la cantidad es menor que cero, muestra que la prueba se comporta correctamente.Para probar el caso en el que la cantidad retirada es mayor que el saldo, lo que se debe hacer es:
Cree un nuevo método de prueba denominado Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange.
Copie el cuerpo del método de Debit_WhenAmountIsLessThanZero_ShouldThrowArgumentOutOfRange al nuevo método.
Establezca debitAmount en un número mayor que el del saldo.
Ejecutar las pruebas
Ejecutar los dos métodos con valores diferentes para debitAmount muestra que las pruebas controlan los casos restantes adecuadamente.Ejecutar las tres pruebas confirma que todos los casos en nuestro análisis original están cubiertos correctamente.
Continúe el análisis
Sin embargo, los dos últimos métodos de prueba también son algo problemáticos.No se puede estar seguro de qué condición en el código en pruebas lanza excepciones cuando se ejecutan las pruebas.Sería útil tener alguna manera de diferenciar las dos condiciones.Cuanto más se piensa en el problema, se hace más evidente que sabiendo qué condición se ha infringido aumentaría la confianza en las pruebas.Esta información también le sería útil al mecanismo de producción que controla la excepción cuando es lanzada por el método en pruebas.Generar más información cuando el método lanza una excepción ayudaría a todos los componentes relacionados, pero el atributo ExpectedException no puede proporcionar esta información.
Al examinar de nuevo el método probado, se puede ver que ambas instrucciones condicionales utilizan un constructor ArgumentOutOfRangeException, que toma el nombre del argumento como parámetro:
throw new ArgumentOutOfRangeException("amount");
En una búsqueda por MSDN Library detectamos que existe un constructor que reporta información mucho más completa.ArgumentOutOfRangeException(String, Object, String) incluye el nombre del argumento, su valor y un mensaje definido por el usuario.Podemos refactorizar el método probado para utilizar este constructor.Incluso mejor, podemos utilizar los miembros de tipo, que se encuentran disponibles públicamente, para especificar los errores.
Refactorizar el código sometido a prueba
Primero se definen dos constantes para los mensajes de error en el ámbito de la clase:
// class under test
public const string DebitAmountExceedsBalanceMessage = "Debit amount exceeds balance";
public const string DebitAmountLessThanZeroMessage = "Debit amount less than zero";
A continuación se modifican las dos instrucciones condicionales en el método Debit:
// method under test
// ...
if (amount > m_balance)
{
throw new ArgumentOutOfRangeException("amount", amount, DebitAmountExceedsBalanceMessage);
}
if (amount < 0)
{
throw new ArgumentOutOfRangeException("amount", amount, DebitAmountLessThanZeroMessage);
}
// ...
Refactorizar los métodos de prueba
En el método de prueba, primero quitamos el atributo ExpectedException.En su lugar, se captura la excepción y se verifica que haya sido lanzada en la instrucción condicional correcta.Sin embargo, se debe decidir ahora entre dos opciones para comprobar las condiciones restantes.Por ejemplo, en el método Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange, podemos realizar una de las siguientes acciones:
Asegurar que la propiedad ActualValue de la excepción (el segundo parámetro del constructor de ArgumentOutOfRangeException ) es mayor que el saldo inicial.Esta opción requiere que se pruebe la propiedad ActualValue de la excepción, en la variable beginningBalance del método de prueba y también requiere que se verifique que ActualValue es mayor que cero.
Asegurar que el mensaje (el tercer parámetro del constructor) incluye el DebitAmountExceedsBalanceMessage definido en la clase BankAccount.
El método StringAssert.Contains en el marco de pruebas unitarias de Microsoft permite comprobar la segunda opción sin los cálculos necesarios de la primera opción.
Un segundo intento de revisar Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange podría ser similar a:
[TestMethod]
public void Debit_WhenAmountIsGreaterThanBalance_ShouldThrowArgumentOutOfRange()
{
// arrange
double beginningBalance = 11.99;
double debitAmount = 20.0;
BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);\
// act
try
{
account.Debit(debitAmount);
}
catch (ArgumentOutOfRangeException e)
{
// assert
StringAssert.Contains(e.Message, BankAccount. DebitAmountExceedsBalanceMessage);
}
}
Vuelva a probar, reescriba y vuelva a analizar
Cuando se reexaminan los métodos de prueba con valores diferentes, encontramos los siguientes hechos:
Si se atrapa el error correcto usando un debitAmount mayor que el saldo, la verificación Contains pasa, se omite la excepción y pasa el método de prueba.Este es el comportamiento que deseamos.
Si se utiliza debitAmount, se la verificación no se puede realizar porque se devuelve el mensaje de error incorrecto.La verificación tampoco se puede realizar si se introduce una excepción temporal de ArgumentOutOfRange en otro punto del método en la ruta de acceso del código de prueba.Esto también es correcto.
Si el valor de debitAmount es válido (es decir, menor que el equilibrio pero mayor que cero, no se produce ninguna excepción, por lo que validar nunca se detecta.El método de prueba pasa.Esto no es bueno, porque se desea que el método de prueba no pase si no se produce ninguna excepción.
El tercer hecho es un error en el método de prueba.Para intentar resolver el problema, se agrega una validación Fail al final del método de prueba para controlar el caso donde no se produce ninguna excepción.
Pero al volver a examinar, se muestra que se produce un error en la prueba si se produce la excepción correcta.La instrucción de captura de excepciones restaura la excepción y el método continúa ejecutándose, produciendo errores en la nueva validación.Para resolver este nuevo problema, se agrega una sentencia return después de StringAssert.Al volver a examinar se confirma que hemos corregido los problemas.La versión final del Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange tiene el siguiente aspecto:
[TestMethod]
public void Debit_WhenAmountIsGreaterThanBalance_ShouldThrowArgumentOutOfRange()
{
// arrange
double beginningBalance = 11.99;
double debitAmount = 20.0;
BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);\
// act
try
{
account.Debit(debitAmount);
}
catch (ArgumentOutOfRangeException e)
{
// assert
StringAssert.Contains(e.Message, BankAccount. DebitAmountExceedsBalanceMessage);
return;
}
Assert.Fail("No exception was thrown.")
}
En esta sección final, el trabajo que se hizo al mejorar el código de prueba condujo a métodos de prueba más eficaces e informativos.Pero lo más importante es que el análisis adicional también condujo a mejoras en el código del proyecto en pruebas.