Exportar (0) Imprimir
Expandir todo
Este artículo se tradujo de forma manual. Mueva el puntero sobre las frases del artículo para ver el texto original.
Traducción
Original

Tutorial: Utilizar TDD con ASP.NET MVC

Este tutorial muestra cómo desarrollar una aplicación de ASP.NET MVC en Visual Studio utilizando el método de desarrollo controlado por pruebas (TDD). MVC se diseñó para habilitar la capacidad de realizar pruebas sin necesitar dependencias en un servidor web (IIS), una base de datos o clases externas. (Esto contrasta con las pruebas unitarias de las páginas de formularios Web Forms, que requieren un servidor web).

En este tutorial, creará pruebas para un controlador MVC antes de implementar la funcionalidad del controlador. Se pone énfasis en el modo de diseñar el propósito del controlador escribiendo pruebas unitarias antes de implementar el propio controlador, lo cual es un aspecto importante de la filosofía TDD. (Para obtener buena información general sobre la filosofía TDD de MVC, vea la entrada It’s Not TDD, It’s Design By Example en el blog de Brad Wilson).

En el tutorial, creará un proyecto y pruebas unitarias para una aplicación que puede utilizar para mostrar y editar contactos. Un contacto tiene un nombre, apellido, número de teléfono y un alias de correo electrónico.

Existe un proyecto de Visual Studio con código fuente para complementar este tema: Descargue. La descarga contiene los proyectos de MVC Csharp y VB completados (en las carpetas vb y cs) y el proyecto de Csharp y VB en la carpeta QuickStart. La primera parte de este tutorial muestra cómo crear un proyecto de MVC en Visual Studio, cómo agregar un modelo de datos y cómo agregar metadatos al modelo de datos. Si está familiarizado con los procedimientos para crear un proyecto de ASP.NET MVC y un modelo de datos de Entity Framework, puede utilizar los proyectos incluidos en el proyecto descargable bajo la carpeta QuickStart y pasar a la sección Adding a Repository más adelante en este tutorial.

Para poder completar este tutorial, necesitará:

  • Microsoft Visual Studio 2008 Service Pack 1 o posterior.

    NotaNota:

    Visual Studio Standard Edition y Visual Web Developer Express Edition no admiten los proyectos de prueba unitaria.

  • El marco de ASP.NET MVC 2. Para descargar la versión más actualizada del marco, vea la página de descarga de ASP.NET MVC.

  • El archivo de base de datos Contact.mdf. Este archivo de base de datos también forma parte del proyecto de ejemplo que puede descargar para este proyecto: Descargue

En esta sección, creará una nueva solución de Visual Studio que incluye el proyecto de aplicación y un proyecto de prueba.

NotaNota:

Este tutorial utiliza el marco de pruebas unitarias de Visual Studio. Para obtener información sobre cómo agregar otro marco de pruebas unitarias, vea Walkthrough: Creating a Basic MVC Project with Unit Tests in Visual Studio

Para crear una aplicación de MVC con pruebas unitarias

  1. En Visual Studio, en el menú Archivo, haga clic en Nuevo proyecto.

  2. En el cuadro de diálogo Nuevo proyecto, en Plantillas instaladas, abra el nodo Visual C# o el nodo Visual Basic y, a continuación, seleccione Web.

  3. Seleccione la plantilla Aplicación web de MVC ASP.NET.

  4. Denomine a la solución MvcContacts.

  5. Haga clic en Aceptar.

  6. Cuando se muestre el cuadro de diálogo Crear proyecto de prueba unitaria, compruebe que está seleccionada Sí, crear un proyecto de prueba unitaria y, a continuación, haga clic en Aceptar.

    Visual Studio crea una solución que contiene dos proyectos, uno denominado MvcContacts y otro denominado MvcContacts.Tests.

  7. En el menú Prueba, haga clic sucesivamente en Ejecutar, Todas las pruebas de la solución.

    Los resultados se muestran en la ventana Resultados de pruebas. Las pruebas se superan.

  8. En el proyecto MvcContacts.Tests, abra y examine la clase de prueba del controlador de cuenta (MvcContacts\MvcContacts.Tests\Controllers\AccountControllerTest) y la clase de modelo del controlador de cuenta (MvcContacts\Models\AccountModels).

    Estas clases proporcionan una buena introducción a cómo crear interfaces ficticias y al TDD. Este proceso consiste en crear objetos de sustitución simples (ficticios) para las dependencias de una clase de modo que pueda probar la clase sin las dependencias. Para probar las interfaces, normalmente crea una clase ficticia que implementa la interfaz que desea probar. Por ejemplo, la clase MockMembershipService de la clase de prueba del controlador de cuenta implementa la interfaz IMembershipService para simular los miembros que forman parte de clases de pertenencia, como los métodos ValidateUser, ChangePassword y CreateUser. La clase MockMembershipService permite probar los métodos de acción que crean cuentas de usuario, validar la información de registro del usuario y cambiar la contraseña de un usuario sin tener que crear instancias de una clase de pertenencia como Membership.

Este tutorial utiliza un Entity Data Model (EDM) que se crea a partir de la base de datos Contact que se incluye en el proyecto de ejemplo descargable. (Debe descargar el proyecto para obtener el archivo Contact.mdf. Para obtener más información, vea la sección Requisitos previos, anteriormente en este tutorial).

Para crear el modelo de base de datos

  1. En el Explorador de soluciones, haga clic con el botón secundario en la carpeta App_Data del proyecto MvcContacts, haga clic en Agregar y, a continuación, en Elemento existente.

    Se mostrará el cuadro de diálogo Agregar elemento existente.

  2. Navegue hasta la carpeta que contiene el archivo Contact.mdf, seleccione el archivo Contact.mdf y, a continuación, haga clic en Agregar.

  3. En el Explorador de soluciones, haga clic con el botón secundario en el proyecto MvcContacts, haga clic en Agregar y, a continuación, en Nuevo elemento.

    Se abrirá el cuadro de diálogo Agregar nuevo elemento.

  4. En Plantillas instaladas, abra el nodo Visual C#, seleccione Datos y, a continuación, seleccione la plantilla Entity Data Model de ADO.NET.

  5. En el cuadro Nombre, escriba ContactModel y, a continuación, haga clic en Agregar.

    Se muestra la ventana Asistente para Entity Data Model.

  6. En Qué debería contener el modelo, seleccione Generar desde la base de datos y, a continuación, haga clic en Siguiente.

  7. En ¿Qué conexión de datos debería utilizar la aplicación para conectarse a la base de datos?, seleccione Contact.mdf.

  8. Asegúrese de que está activada la casilla Guardar configuración de conexión de entidad en Web.Config como. Puede dejar el nombre predeterminado de la cadena de conexión.

  9. Haga clic en Siguiente.

    El asistente muestra una página en la que puede especificar qué objetos de base de datos desea incluir en el modelo.

  10. Seleccione el nodo Tablas para seleccionar la tabla Contacts. Puede dejar el espacio de nombres del modelo predeterminado.

  11. Haga clic en Finalizar.

    Se muestra ADO.NET Entity Data Model Designer. Cierre el diseñador.

En esta sección, agregará los metadatos del contacto. Los metadatos de la clase Contact no se utilizarán en las pruebas unitarias. Sin embargo, hace que el ejemplo sea más completo porque proporciona validación automatizada de datos del lado cliente y del lado servidor.

Para agregar los metadatos del modelo

  1. En la carpeta MvcContacts\Models, cree un nuevo archivo de clase denominado ContactMD.

    En este archivo, agregará una clase (ContactMD) que contendrá los metadatos del objeto entidad Contact que forma parte del modelo de datos que está utilizando para este tutorial.

  2. Reemplace el código del archivo por el código siguiente:

    
    using System.ComponentModel.DataAnnotations;
    
    namespace MvcContacts.Models {
        [MetadataType(typeof(ContactMD))]
        public partial class Contact {
            public class ContactMD {
                [ScaffoldColumn(false)]
                public object Id { get; set; }
                [Required()]
                public object FirstName { get; set; }
                [Required()]
                public object LastName { get; set; }
                [RegularExpression(@"^\d{3}-?\d{3}-?\d{4}$")]
                public object Phone { get; set; }
                [Required()]
                [DataType(DataType.EmailAddress)]
                [RegularExpression(@"^([0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*@([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,9})$")]
                public object Email { get; set; }
            }
        }
    } 
    
    
    

Un procedimiento recomendado en MVC es no incluir Entity Data Model (EDM) ni ningún otro código del marco de acceso a datos dentro del controlador. En su lugar, debería utilizar el modelo del repositorio. El repositorio se encuentra entre la aplicación y el almacén de datos. Un repositorio separa la lógica de negocios de las interacciones con la base de datos subyacente y centra el acceso a los datos en un área, lo cual facilita la creación y el mantenimiento.

El repositorio devuelve objetos del modelo de dominio. Para los modelos simples, como el modelo con el que trabaja en este tutorial, los objetos devueltos de EDM, LINQ to SQL y otros modelos de datos se consideran objetos de dominio.

Para aplicaciones más complejas, podría ser necesario un nivel de asignación. Un nivel de asignación no tiene porqué ser ineficaz. Los proveedores LINQ pueden crear consultas eficaces al almacén de datos back-end (es decir, pueden consultar utilizando un número mínimo de objetos intermedios).

El repositorio no debería requerir conocimientos de EDM, LINQ to SQL o cualquier otro modelo de datos con el que esté trabajando. (Aunque LINQ no se trata en este tutorial, si utiliza LINQ como la abstracción de consulta puede ocultar el mecanismo de almacenamiento de datos. Por ejemplo, esto permite utilizar SQL Server para la producción y LINQ to Objects sobre las colecciones en memoria para realizar pruebas).

Probar los métodos de acción en un controlador que tenga acceso directo al EDM requiere una conexión a una base de datos, porque los métodos de acción dependen del EDM (que a su vez depende de una base de datos). El siguiente código muestra un controlador MVC que utiliza la entidad Contact del EDM directamente, y proporciona un ejemplo simple de por qué mezclar llamadas a la base de datos en los métodos de acción dificulta la prueba del método de acción. Por ejemplo, las pruebas unitarias que editan y eliminan datos cambian el estado de la base de datos. Esto requiere que cada paso de las pruebas unitarias tenga una configuración limpia de la base de datos. Además, las llamadas a la base de datos son muy caras, mientras que las pruebas unitarias deberían ser ligeras de modo que se puedan ejecutar con frecuencia mientras se desarrolla la aplicación.


public class NotTDDController : Controller {

    ContactEntities _db = new ContactEntities();

    public ActionResult Index() {
        var dn = _db.Contacts;
        return View(dn);
    }

    public ActionResult Edit(int id) {
        Contact prd = _db.Contacts.FirstOrDefault(d => d.Id == id);
        return View(prd);
    }

    [HttpPost]
    public ActionResult Edit(int id, FormCollection collection) {
        Contact prd = _db.Contacts.FirstOrDefault(d => d.Id == id);
        UpdateModel(prd);
        _db.SaveChanges();
        return RedirectToAction("Index");
    }
} 



public class NotTDDController : Controller {

    ContactEntities _db = new ContactEntities();

    public ActionResult Index() {
        var dn = _db.Contacts;
        return View(dn);
    }

    public ActionResult Edit(int id) {
        Contact prd = _db.Contacts.FirstOrDefault(d => d.Id == id);
        return View(prd);
    }

    [HttpPost]
    public ActionResult Edit(int id, FormCollection collection) {
        Contact prd = _db.Contacts.FirstOrDefault(d => d.Id == id);
        UpdateModel(prd);
        _db.SaveChanges();
        return RedirectToAction("Index");
    }
} 


El modelo del repositorio tiene las siguientes ventajas:

  • Proporciona un punto de sustitución para las pruebas unitarias. Puede probar con facilidad la lógica de negocios sin una base de datos ni otras dependencias externas.

  • Las consultas duplicadas y los modelos de acceso a datos se pueden quitar y refactorizar en el repositorio.

  • Los métodos de controlador pueden utilizar parámetros fuertemente tipados, lo que significa que el compilador encuentra errores tipográficos en los datos cada vez que compila, en lugar de hacerlo en tiempo de ejecución durante las pruebas.

  • El acceso a datos está centralizado, lo que proporciona las siguientes ventajas:

    • Mayor separación de los problemas (SoC), otro principio de MVC, lo que aumenta el mantenimiento y la legibilidad.

    • Implementación simplificada del almacenamiento en caché de los datos centralizado.

    • Una arquitectura más flexible y menos acoplada que se puede adaptar a medida que evoluciona el diseño total de la aplicación.

  • El comportamiento se puede asociar a los datos relacionados. Por ejemplo, puede calcular los campos o exigir relaciones complejas o reglas de negocio entre los elementos de datos dentro de una entidad.

  • Se puede aplicar un modelo de dominio para simplificar la lógica de negocios compleja.

Utilizar el modelo del repositorio con MVC y TDD normalmente requiere crear una interfaz para la clase de acceso a datos. La interfaz de repositorio facilitará la inserción de un repositorio ficticio cuando realice pruebas unitarias en los métodos de controlador.

En esta sección, agregará un repositorio de contactos, que es una clase que se utiliza para guardar los contactos en una base de datos. También agregará una interfaz para el repositorio de contactos.

Para agregar un repositorio

  1. En la carpeta MvcContacts\Models, cree un archivo de clase y agregue una clase denominada IContactRepository.

    La clase IContactRepository contendrá la interfaz del objeto de repositorio.

  2. Reemplace el código del archivo de clase por el código siguiente:

    
    using System;
    using System.Collections.Generic;
    
    namespace MvcContacts.Models {
        public interface IContactRepository {
            void CreateNewContact(Contact contactToCreate);
            void DeleteContact(int id);
            Contact GetContactByID(int id);
            IEnumerable<Contact> GetAllContacts();
            int SaveChanges();
    
        }
    } 
    
    
    
  3. En la carpeta MvcContacts\Models, cree una nueva clase denominada EntityContactManagerRepository.

    La clase EntityContactManagerRepository implementará la interfaz IContactRepository del objeto de repositorio.

  4. Reemplace el código de la clase EntityContactManagerRepository por el código siguiente:

    
    using System.Collections.Generic;
    using System.Linq;
    
    namespace MvcContacts.Models {
        public class EF_ContactRepository : MvcContacts.Models.IContactRepository {
    
            private ContactEntities _db = new ContactEntities();
    
            public Contact GetContactByID(int id) {
                return _db.Contacts.FirstOrDefault(d => d.Id == id);
            }
    
            public IEnumerable<Contact> GetAllContacts() {
                return _db.Contacts.ToList();
            }
    
            public void CreateNewContact(Contact contactToCreate) {
                _db.AddToContacts(contactToCreate);
                _db.SaveChanges();
             //   return contactToCreate;
            }
    
            public int SaveChanges() {
                return _db.SaveChanges();
            }
    
            public void DeleteContact(int id) {
                var conToDel = GetContactByID(id);
                _db.Contacts.DeleteObject(conToDel);
                _db.SaveChanges();
            }
    
        }
    } 
    
    
    

En esta sección, agregará una implementación ficticia del repositorio, agregará pruebas unitarias e implementará la funcionalidad de la aplicación a partir de las pruebas unitarias.

Para implementar el repositorio ficticio

  1. En el proyecto MvcContacts.Tests, cree una carpeta Models.

  2. En la carpeta MvcContacts.Tests\Models, cree una nueva clase denominada MocContactRepository.

    La clase MocContactRepository implementará la interfaz IContactRepository que creó previamente y tendrá un repositorio simple para controlar el diseño de la aplicación.

  3. Reemplace el código de la clase MocContactRepository por el código siguiente:

    
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    using MvcContacts.Models;
    
    namespace MvcContacts.Tests.Models {
        class InMemoryContactRepository : MvcContacts.Models.IContactRepository {
            private List<Contact> _db = new List<Contact>();
    
            public Exception ExceptionToThrow { get; set; }
            //public List<Contact> Items { get; set; }
    
            public void SaveChanges(Contact contactToUpdate) {
    
                foreach (Contact contact in _db) {
                    if (contact.Id == contactToUpdate.Id) {
                        _db.Remove(contact);
                        _db.Add(contactToUpdate);
                        break;
                    }
                }
            }
    
            public void Add(Contact contactToAdd) {
                _db.Add(contactToAdd);
            }
    
            public Contact GetContactByID(int id) {
                return _db.FirstOrDefault(d => d.Id == id);
            }
    
            public void CreateNewContact(Contact contactToCreate) {
                if (ExceptionToThrow != null)
                    throw ExceptionToThrow;
    
                _db.Add(contactToCreate);
               // return contactToCreate;
            }
    
            public int SaveChanges() {
                return 1;
            }
    
            public IEnumerable<Contact> GetAllContacts() {
                return _db.ToList();
            }
    
    
            public void DeleteContact(int id) {
                _db.Remove(GetContactByID(id));
            }
    
        }
    } 
    
    
    
    
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    using MvcContacts.Models;
    
    namespace MvcContacts.Tests.Models {
        class InMemoryContactRepository : MvcContacts.Models.IContactRepository {
            private List<Contact> _db = new List<Contact>();
    
            public Exception ExceptionToThrow { get; set; }
            //public List<Contact> Items { get; set; }
    
            public void SaveChanges(Contact contactToUpdate) {
    
                foreach (Contact contact in _db) {
                    if (contact.Id == contactToUpdate.Id) {
                        _db.Remove(contact);
                        _db.Add(contactToUpdate);
                        break;
                    }
                }
            }
    
            public void Add(Contact contactToAdd) {
                _db.Add(contactToAdd);
            }
    
            public Contact GetContactByID(int id) {
                return _db.FirstOrDefault(d => d.Id == id);
            }
    
            public void CreateNewContact(Contact contactToCreate) {
                if (ExceptionToThrow != null)
                    throw ExceptionToThrow;
    
                _db.Add(contactToCreate);
               // return contactToCreate;
            }
    
            public int SaveChanges() {
                return 1;
            }
    
            public IEnumerable<Contact> GetAllContacts() {
                return _db.ToList();
            }
    
    
            public void DeleteContact(int id) {
                _db.Remove(GetContactByID(id));
            }
    
        }
    } 
    
    
    

Para agregar la compatibilidad con pruebas

  1. En el proyecto MvcContacts, abra el archivo Controllers\HomeController.cs. Reemplace el código del archivo Controllers\HomeController.cs por el siguiente código:

    using System;
    using System.Web.Mvc;
    using MvcContacts.Models;
    
    namespace MvcContacts.Controllers {
        [HandleError]
        public class HomeController : Controller {
            IContactRepository _repository;
            public HomeController() : this(new EF_ContactRepository()) { }
            public HomeController(IContactRepository repository) {
                _repository = repository;
            }
            public ViewResult Index() {
                throw new NotImplementedException();
            }
        }
    }
    

    Esta clase contiene dos constructores. Uno es un constructor sin parámetros. El otro toma un parámetro de tipo IContactRepository; las pruebas unitarias utilizarán este constructor para pasar el repositorio ficticio. El constructor sin parámetros crea una instancia de la clase EF_ContactRepository y es llamado por la canalización de MVC cuando se invoca un método de acción en el controlador.

  2. Cierre el archivo HomeController.

  3. En el proyecto MvcContacts.Test, abra el archivo Controllers\HomeControllerTest y reemplace el código del archivo por el siguiente código:

    using System.Web.Mvc;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using MvcContacts.Controllers;
    
    using MvcContacts.Models;
    using MvcContacts.Tests.Models;
    using System.Web;
    using System.Web.Routing;
    using System.Security.Principal;
    
    namespace MvcContacts.Tests.Controllers {
        [TestClass]
        public class HomeControllerTest {
            
            Contact GetContact() {
                return GetContact(1, "Janet", "Gates");
            }
    
            Contact GetContact(int id, string fName, string lName) {
                return new Contact
                {
                    Id = id,
                    FirstName = fName,
                    LastName = lName,
                    Phone = "710-555-0173",
                    Email = "janet1@adventure-works.com"
                };
            }
    
            private static HomeController GetHomeController(IContactRepository repository) {
                HomeController controller = new HomeController(repository);
    
                controller.ControllerContext = new ControllerContext()
                {
                    Controller = controller,
                    RequestContext = new RequestContext(new MockHttpContext(), new RouteData())
                };
                return controller;
            }
    
    
            private class MockHttpContext : HttpContextBase {
                private readonly IPrincipal _user = new GenericPrincipal(
                         new GenericIdentity("someUser"), null /* roles */);
    
                public override IPrincipal User {
                    get {
                        return _user;
                    }
                    set {
                        base.User = value;
                    }
                }
            }
        }
    }
    

    El código del ejemplo anterior contiene dos sobrecargas de método que obtienen un contacto (GetContact) y un método para obtener el objeto HomeController. Las pruebas unitarias llamarán a los métodos de HomeController. El código también contiene una clase para simular el objeto HttpContext. La clase MockHttpContext utilizada en este ejemplo es una versión simplificada de la que se encuentra en el archivo de clase AcountControllerTest creado al realizar un nuevo proyecto de MVC con pruebas unitarias.

Uno de los principios del TDD con MVC es que cada prueba debería controlar un requisito específico de un método de acción. La prueba no debería comprobar la base de datos ni otros componentes (aunque estos componentes se deberían probar en las pruebas unitarias de acceso a datos y en las pruebas de integración). Otro objetivo es que los nombres de las pruebas deberían ser muy descriptivos; los nombres cortos y generales como Creat_Post_Test1 harán más difícil entender la prueba si existen centenares de pruebas.

En esta parte del tutorial, se supondrá que las llamadas de diseño del método predeterminado del controlador Home devuelven una lista de contactos. El método predeterminado del controlador es Index, de modo que la primera prueba comprobará que el controlador devuelve la vista Index. Cuando realizó cambios en el método Index anterior de este tutorial, lo cambió para que devolviera un objeto ViewResult, no el objeto ActionResult más general. Si sabe que un método siempre devuelve un objeto ViewResult, puede simplificar las pruebas unitarias devolviendo un objeto ViewResult del método de controlador. Si devuelve un objeto ViewResult, la prueba unitaria no tiene que convertir el habitual objeto ActionResult en un objeto ViewResult como parte de la prueba, lo que produce pruebas más fáciles y legibles

Para agregar la primera prueba

  1. En la clase HomeControllerTest, agregue una prueba unitaria denominada Index_Get_AsksForIndexView que compruebe que el método Index devuelve una vista denominada Index.

    En el siguiente ejemplo se muestra la prueba unitaria completada.

    
    [TestMethod]
    public void Index_Get_AsksForIndexView() {
        // Arrange
        var controller = GetHomeController(new InMemoryContactRepository());
        // Act
        ViewResult result = controller.Index();
        // Assert
        Assert.AreEqual("Index", result.ViewName);
    } 
    
    
    
    
    [TestMethod]
    public void Index_Get_AsksForIndexView() {
        // Arrange
        var controller = GetHomeController(new InMemoryContactRepository());
        // Act
        ViewResult result = controller.Index();
        // Assert
        Assert.AreEqual("Index", result.ViewName);
    } 
    
    
    
  2. En el menú Prueba, haga clic sucesivamente en Ejecutar, Todas las pruebas de la solución.

    Los resultados se muestran en la ventana Resultados de pruebas. Tal y como se espera, la prueba unitaria Index_Get_AsksForIndexView produce un error.

  3. Implemente el siguiente método Index de la clase HomeController para devolver una lista de todos los contactos.

    public ViewResult Index() {
                return View("Index", _repository.ListContacts());
            }
    

    Siguiendo el espíritu del TDD, solo escribe el código necesario para satisfacer la prueba de diseño.

  4. Ejecute las pruebas. Esta vez la prueba unitaria Index_Get_AsksForIndexView se supera.

En esta sección, comprobará que puede recuperar todos los contactos. No desea crear pruebas unitarias que prueben el acceso a datos. Comprobar que la aplicación tiene acceso a la base de datos y puede recuperar contactos es importante, pero es una prueba de integración, no una prueba unitaria de TDD.

Para agregar una prueba para recuperar contactos

  • Cree una prueba que agregue dos contactos ficticios al repositorio ficticio en la clase HomeControllerTest y, a continuación, compruebe que se incluyen en el objeto Model de ViewData que se incluye en la vista Index.

    El ejemplo siguiente muestra la prueba completada:

    
    [TestMethod]
    public void Index_Get_RetrievesAllContactsFromRepository() {
        // Arrange
        Contact contact1 = GetContactNamed(1, "Orlando", "Gee");
        Contact contact2 = GetContactNamed(2, "Keith", "Harris");
        InMemoryContactRepository repository = new InMemoryContactRepository();
        repository.Add(contact1);
        repository.Add(contact2);
        var controller = GetHomeController(repository);
    
        // Act
        var result = controller.Index();
    
        // Assert
        var model = (IEnumerable<Contact>)result.ViewData.Model;
        CollectionAssert.Contains(model.ToList(), contact1);
        CollectionAssert.Contains(model.ToList(), contact1);
    } 
    
    
    
    
    [TestMethod]
    public void Index_Get_RetrievesAllContactsFromRepository() {
        // Arrange
        Contact contact1 = GetContactNamed(1, "Orlando", "Gee");
        Contact contact2 = GetContactNamed(2, "Keith", "Harris");
        InMemoryContactRepository repository = new InMemoryContactRepository();
        repository.Add(contact1);
        repository.Add(contact2);
        var controller = GetHomeController(repository);
    
        // Act
        var result = controller.Index();
    
        // Assert
        var model = (IEnumerable<Contact>)result.ViewData.Model;
        CollectionAssert.Contains(model.ToList(), contact1);
        CollectionAssert.Contains(model.ToList(), contact1);
    } 
    
    
    

Ahora probará el proceso de creación de un nuevo contacto. La primera prueba comprueba que una operación POST de HTTP se ha completado correctamente y ha invocado al método Create mediante los datos que deliberadamente contienen errores del modelo. El resultado no agregará un nuevo contacto, sino que en su lugar devolverá la vista HTTP GET Create que contiene los campos introducidos y los errores del modelo. Si se ejecuta una prueba unitaria en un controlador, no se ejecuta la canalización de MVC ni el proceso de enlace del modelo. Por consiguiente, el error del modelo no se detectaría durante el proceso de enlace. Para responder a esto, la prueba agrega un error ficticio.

Para agregar una prueba para crear un contacto

  1. Agregue la prueba siguiente al proyecto:

    
    [TestMethod]
    public void Create_Post_ReturnsViewIfModelStateIsNotValid() {
        // Arrange
        HomeController controller = GetHomeController(new InMemoryContactRepository());
        // Simply executing a method during a unit test does just that - executes a method, and no more. 
        // The MVC pipeline doesn't run, so binding and validation don't run.
        controller.ModelState.AddModelError("", "mock error message");
        Contact model = GetContactNamed(1, "", "");
    
        // Act
        var result = (ViewResult)controller.Create(model);
    
        // Assert
        Assert.AreEqual("Create", result.ViewName);
    } 
    
    
    
    
    [TestMethod]
    public void Create_Post_ReturnsViewIfModelStateIsNotValid() {
        // Arrange
        HomeController controller = GetHomeController(new InMemoryContactRepository());
        // Simply executing a method during a unit test does just that - executes a method, and no more. 
        // The MVC pipeline doesn't run, so binding and validation don't run.
        controller.ModelState.AddModelError("", "mock error message");
        Contact model = GetContactNamed(1, "", "");
    
        // Act
        var result = (ViewResult)controller.Create(model);
    
        // Assert
        Assert.AreEqual("Create", result.ViewName);
    } 
    
    
    

    El código muestra cómo un intento de agregar un contacto que contiene errores del modelo devuelve la vista HTTP GET Create.

  2. Agregue la siguiente prueba:

    
    [TestMethod]
    public void Create_Post_PutsValidContactIntoRepository() {
        // Arrange
        InMemoryContactRepository repository = new InMemoryContactRepository();
        HomeController controller = GetHomeController(repository);
        Contact contact = GetContactID_1();
    
        // Act
        controller.Create(contact);
    
        // Assert
        IEnumerable<Contact> contacts = repository.GetAllContacts();
        Assert.IsTrue(contacts.Contains(contact));
    } 
    
    
    
    
    [TestMethod]
    public void Create_Post_PutsValidContactIntoRepository() {
        // Arrange
        InMemoryContactRepository repository = new InMemoryContactRepository();
        HomeController controller = GetHomeController(repository);
        Contact contact = GetContactID_1();
    
        // Act
        controller.Create(contact);
    
        // Assert
        IEnumerable<Contact> contacts = repository.GetAllContacts();
        Assert.IsTrue(contacts.Contains(contact));
    } 
    
    
    

    El código muestra cómo comprobar que un POST de HTTP al método Create agrega un contacto válido al repositorio.

Una prueba que a menudo se pasa por alto es comprobar que los métodos controlan correctamente las excepciones. La clase MocContactRepository permite establecer una excepción ficticia, simulando una excepción que produciría una base de datos si ocurre una restricción u otra infracción. Muchas excepciones de base de datos no se pueden detectar con validación del modelo, de modo que es importante comprobar que el código de control de excepciones funciona correctamente. En el ejemplo siguiente se muestra cómo hacerlo.


[TestMethod]
public void Create_Post_ReturnsViewIfRepositoryThrowsException() {
    // Arrange
    InMemoryContactRepository repository = new InMemoryContactRepository();
    Exception exception = new Exception();
    repository.ExceptionToThrow = exception;
    HomeController controller = GetHomeController(repository);
    Contact model = GetContactID_1();

    // Act
    var result = (ViewResult)controller.Create(model);

    // Assert
    Assert.AreEqual("Create", result.ViewName);
    ModelState modelState = result.ViewData.ModelState[""];
    Assert.IsNotNull(modelState);
    Assert.IsTrue(modelState.Errors.Any());
    Assert.AreEqual(exception, modelState.Errors[0].Exception);
} 



[TestMethod]
public void Create_Post_ReturnsViewIfRepositoryThrowsException() {
    // Arrange
    InMemoryContactRepository repository = new InMemoryContactRepository();
    Exception exception = new Exception();
    repository.ExceptionToThrow = exception;
    HomeController controller = GetHomeController(repository);
    Contact model = GetContactID_1();

    // Act
    var result = (ViewResult)controller.Create(model);

    // Assert
    Assert.AreEqual("Create", result.ViewName);
    ModelState modelState = result.ViewData.ModelState[""];
    Assert.IsNotNull(modelState);
    Assert.IsTrue(modelState.Errors.Any());
    Assert.AreEqual(exception, modelState.Errors[0].Exception);
} 


En el ejemplo descargable se incluyen más pruebas que no se detallan aquí. Para obtener más información sobre cómo utilizar objetos ficticios y la metodología TDD con proyectos de MVC, revise las pruebas adicionales y escriba pruebas para los métodos Edit y Delete.

Adiciones de comunidad

AGREGAR
Mostrar:
© 2014 Microsoft