Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
por Tom FitzMacken
Descargar el proyecto completado
En esta guía y aplicación se muestra cómo crear pruebas unitarias para la aplicación de Api web 2 que usa Entity Framework. Muestra cómo modificar el controlador con scaffolding para permitir pasar un objeto de contexto para las pruebas y cómo crear objetos de prueba que funcionan con Entity Framework.
Para obtener una introducción a las pruebas unitarias con ASP.NET API web, consulte Pruebas unitarias con ASP.NET Web API 2.
En este tutorial se da por supuesto que está familiarizado con los conceptos básicos de ASP.NET API web. Para ver un tutorial introductorio, consulte Introducción a ASP.NET Web API 2.
Versiones de software usadas en el tutorial
- Visual Studio 2017
- Web API 2
En este tema
Este tema contiene las siguientes secciones:
- Requisitos previos
- Descargar código
- Creación de una aplicación con un proyecto de prueba unitaria
- Creación de la clase de modelo
- Agregar el controlador
- Agregar inyección de dependencias
- Instalación de paquetes NuGet en el proyecto de prueba
- Creación de contexto de prueba
- Creación de pruebas
- Ejecución de pruebas
Si ya ha completado los pasos descritos en Pruebas unitarias con ASP.NET Web API 2, puede ir directamente a la sección Agregar el controlador.
Prerrequisitos
Visual Studio 2017 Community, Professional o Enterprise Edition
Descargar código
Descargue el proyecto completado. El proyecto descargable incluye código de prueba unitaria para este tema y para el tema Unit Testing ASP.NET Web API 2 .
Creación de una aplicación con un proyecto de prueba unitaria
Puede crear un proyecto de prueba unitaria al crear la aplicación o agregar un proyecto de prueba unitaria a una aplicación existente. En este tutorial se muestra cómo crear un proyecto de prueba unitaria al crear la aplicación.
Cree una aplicación web ASP.NET denominada StoreApp.
En las ventanas New ASP.NET Project (Nuevo proyecto de ASP.NET), seleccione la plantilla Empty (Vacía ) y agregue carpetas y referencias principales para Web API. Seleccione la opción Agregar pruebas unitarias . El proyecto de prueba unitaria se denomina automáticamente StoreApp.Tests. Puede conservar este nombre.
Después de crear la aplicación, verá que contiene dos proyectos: StoreApp y StoreApp.Tests.
Creación de la clase de modelo
En el proyecto StoreApp, agregue un archivo de clase a la carpeta Models denominada Product.cs. Reemplace el contenido del archivo por el código siguiente.
using System;
namespace StoreApp.Models
{
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
}
Compile la solución.
Agregar el controlador
Haga clic con el botón derecho en la carpeta Controladores y seleccione Agregar y Nuevo Elemento con Scaffold. Seleccione Controlador de Web API 2 con acciones mediante Entity Framework.
Establezca los siguientes valores:
- Nombre del controlador: ProductController
- Clase de modelo: Product
- Clase de contexto de datos: [Seleccionar nuevo botón de contexto de datos que rellena los valores que se muestran a continuación]
Haga clic en Agregar para crear el controlador con código generado automáticamente. El código incluye métodos para crear, recuperar, actualizar y eliminar instancias de la clase Product. En el código siguiente se muestra el método para agregar un producto. Observe que el método devuelve una instancia de IHttpActionResult.
// POST api/Product
[ResponseType(typeof(Product))]
public IHttpActionResult PostProduct(Product product)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.Products.Add(product);
db.SaveChanges();
return CreatedAtRoute("DefaultApi", new { id = product.Id }, product);
}
IHttpActionResult es una de las nuevas características de Web API 2 y simplifica el desarrollo de pruebas unitarias.
En la sección siguiente, personalizará el código generado para facilitar el paso de objetos de prueba al controlador.
Añadir inyección de dependencias
Actualmente, la clase ProductController está codificada de forma rígida para usar una instancia de la clase StoreAppContext. Usará un patrón denominado inserción de dependencias para modificar la aplicación y quitar esa dependencia codificada de forma rígida. Al romper esta dependencia, puede pasar un objeto simulado durante las pruebas.
Haga clic con el botón derecho en la carpeta Models y agregue una nueva interfaz denominada IStoreAppContext.
Reemplace el código por el código siguiente.
using System;
using System.Data.Entity;
namespace StoreApp.Models
{
public interface IStoreAppContext : IDisposable
{
DbSet<Product> Products { get; }
int SaveChanges();
void MarkAsModified(Product item);
}
}
Abra el archivo StoreAppContext.cs y realice los siguientes cambios resaltados. Los cambios importantes que debe tener en cuenta son:
- La clase StoreAppContext implementa la interfaz IStoreAppContext
- Se implementa el método MarkAsModified
using System;
using System.Data.Entity;
namespace StoreApp.Models
{
public class StoreAppContext : DbContext, IStoreAppContext
{
public StoreAppContext() : base("name=StoreAppContext")
{
}
public DbSet<Product> Products { get; set; }
public void MarkAsModified(Product item)
{
Entry(item).State = EntityState.Modified;
}
}
}
Abra el archivo ProductController.cs. Cambie el código existente para que coincida con el código resaltado. Estos cambios interrumpen la dependencia en StoreAppContext y permiten que otras clases pasen un objeto diferente para la clase de contexto. Este cambio le permitirá pasar un contexto de test durante las pruebas unitarias.
public class ProductController : ApiController
{
// modify the type of the db field
private IStoreAppContext db = new StoreAppContext();
// add these constructors
public ProductController() { }
public ProductController(IStoreAppContext context)
{
db = context;
}
// rest of class not shown
}
Hay un cambio más que debe realizar en ProductController. En el método PutProduct , reemplace la línea que establece el estado de entidad que se va a modificar con una llamada al método MarkAsModified.
// PUT api/Product/5
public IHttpActionResult PutProduct(int id, Product product)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (id != product.Id)
{
return BadRequest();
}
//db.Entry(product).State = EntityState.Modified;
db.MarkAsModified(product);
// rest of method not shown
}
Compile la solución.
Ya está listo para configurar el proyecto de prueba.
Instalación de paquetes NuGet en el proyecto de prueba
Cuando se usa la plantilla Empty para crear una aplicación, el proyecto de prueba unitaria (StoreApp.Tests) no incluye ningún paquete NuGet instalado. Otras plantillas, como la plantilla de API web, incluyen algunos paquetes NuGet en el proyecto de prueba unitaria. Para este tutorial, debe incluir el paquete de Entity Framework y el paquete de Microsoft ASP.NET Web API 2 Core en el proyecto de prueba.
Haga clic con el botón derecho en el proyecto StoreApp.Tests y seleccione Administrar paquetes NuGet. Debes seleccionar el proyecto StoreApp.Tests para agregar los paquetes a ese proyecto.
En los paquetes en línea, busque e instale el paquete EntityFramework (versión 6.0 o posterior). Si parece que el paquete EntityFramework ya está instalado, es posible que haya seleccionado el proyecto StoreApp en lugar del proyecto StoreApp.Tests.
Busque e instale el paquete microsoft ASP.NET Web API 2 Core.
Cierre la ventana Administrar paquetes NuGet.
Creación de contexto de prueba
Agregue una clase denominada TestDbSet al proyecto de prueba. Esta clase actúa como clase base para el conjunto de datos de prueba. Reemplace el código por el código siguiente.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data.Entity;
using System.Linq;
namespace StoreApp.Tests
{
public class TestDbSet<T> : DbSet<T>, IQueryable, IEnumerable<T>
where T : class
{
ObservableCollection<T> _data;
IQueryable _query;
public TestDbSet()
{
_data = new ObservableCollection<T>();
_query = _data.AsQueryable();
}
public override T Add(T item)
{
_data.Add(item);
return item;
}
public override T Remove(T item)
{
_data.Remove(item);
return item;
}
public override T Attach(T item)
{
_data.Add(item);
return item;
}
public override T Create()
{
return Activator.CreateInstance<T>();
}
public override TDerivedEntity Create<TDerivedEntity>()
{
return Activator.CreateInstance<TDerivedEntity>();
}
public override ObservableCollection<T> Local
{
get { return new ObservableCollection<T>(_data); }
}
Type IQueryable.ElementType
{
get { return _query.ElementType; }
}
System.Linq.Expressions.Expression IQueryable.Expression
{
get { return _query.Expression; }
}
IQueryProvider IQueryable.Provider
{
get { return _query.Provider; }
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _data.GetEnumerator();
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return _data.GetEnumerator();
}
}
}
Agregue una clase denominada TestProductDbSet al proyecto de prueba que contiene el código siguiente.
using System;
using System.Linq;
using StoreApp.Models;
namespace StoreApp.Tests
{
class TestProductDbSet : TestDbSet<Product>
{
public override Product Find(params object[] keyValues)
{
return this.SingleOrDefault(product => product.Id == (int)keyValues.Single());
}
}
}
Agregue una clase denominada TestStoreAppContext y reemplace el código existente por el código siguiente.
using System;
using System.Data.Entity;
using StoreApp.Models;
namespace StoreApp.Tests
{
public class TestStoreAppContext : IStoreAppContext
{
public TestStoreAppContext()
{
this.Products = new TestProductDbSet();
}
public DbSet<Product> Products { get; set; }
public int SaveChanges()
{
return 0;
}
public void MarkAsModified(Product item) { }
public void Dispose() { }
}
}
Creación de pruebas
De forma predeterminada, el proyecto de prueba incluye un archivo de prueba vacío denominado UnitTest1.cs. Este archivo muestra los atributos que se usan para crear métodos de prueba. En este tutorial, puede eliminar este archivo porque agregará una nueva clase de prueba.
Agregue una clase denominada TestProductController al proyecto de prueba. Reemplace el código por el código siguiente.
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Web.Http.Results;
using System.Net;
using StoreApp.Models;
using StoreApp.Controllers;
namespace StoreApp.Tests
{
[TestClass]
public class TestProductController
{
[TestMethod]
public void PostProduct_ShouldReturnSameProduct()
{
var controller = new ProductController(new TestStoreAppContext());
var item = GetDemoProduct();
var result =
controller.PostProduct(item) as CreatedAtRouteNegotiatedContentResult<Product>;
Assert.IsNotNull(result);
Assert.AreEqual(result.RouteName, "DefaultApi");
Assert.AreEqual(result.RouteValues["id"], result.Content.Id);
Assert.AreEqual(result.Content.Name, item.Name);
}
[TestMethod]
public void PutProduct_ShouldReturnStatusCode()
{
var controller = new ProductController(new TestStoreAppContext());
var item = GetDemoProduct();
var result = controller.PutProduct(item.Id, item) as StatusCodeResult;
Assert.IsNotNull(result);
Assert.IsInstanceOfType(result, typeof(StatusCodeResult));
Assert.AreEqual(HttpStatusCode.NoContent, result.StatusCode);
}
[TestMethod]
public void PutProduct_ShouldFail_WhenDifferentID()
{
var controller = new ProductController(new TestStoreAppContext());
var badresult = controller.PutProduct(999, GetDemoProduct());
Assert.IsInstanceOfType(badresult, typeof(BadRequestResult));
}
[TestMethod]
public void GetProduct_ShouldReturnProductWithSameID()
{
var context = new TestStoreAppContext();
context.Products.Add(GetDemoProduct());
var controller = new ProductController(context);
var result = controller.GetProduct(3) as OkNegotiatedContentResult<Product>;
Assert.IsNotNull(result);
Assert.AreEqual(3, result.Content.Id);
}
[TestMethod]
public void GetProducts_ShouldReturnAllProducts()
{
var context = new TestStoreAppContext();
context.Products.Add(new Product { Id = 1, Name = "Demo1", Price = 20 });
context.Products.Add(new Product { Id = 2, Name = "Demo2", Price = 30 });
context.Products.Add(new Product { Id = 3, Name = "Demo3", Price = 40 });
var controller = new ProductController(context);
var result = controller.GetProducts() as TestProductDbSet;
Assert.IsNotNull(result);
Assert.AreEqual(3, result.Local.Count);
}
[TestMethod]
public void DeleteProduct_ShouldReturnOK()
{
var context = new TestStoreAppContext();
var item = GetDemoProduct();
context.Products.Add(item);
var controller = new ProductController(context);
var result = controller.DeleteProduct(3) as OkNegotiatedContentResult<Product>;
Assert.IsNotNull(result);
Assert.AreEqual(item.Id, result.Content.Id);
}
Product GetDemoProduct()
{
return new Product() { Id = 3, Name = "Demo name", Price = 5 };
}
}
}
Ejecución de pruebas
Ya está listo para ejecutar las pruebas. Se probará todo el método marcado con el atributo TestMethod . En el elemento de menú Probar , ejecute las pruebas.
Abra la ventana Explorador de pruebas y observe los resultados de las pruebas.