.net - simple - pruebas unitarias con mockito
Las pruebas unitarias de las rutas de ASP.NET MVC 2 con áreas se rescatan en AreaRegistration.RegisterAllAreas() (7)
Aquí una bonita versión con enfoques combinados.
Código utilizado desde:
- Respuesta anterior
- Enfoque de phils
- Tipos anónimos como diccionarios
[TestClass]
public class RoutesTest : RoutesTestClassBase<SomeAreaRegistration>
{
[TestMethod]
public void IdWithoutName()
{
// Area-Name is retrieved from the Registration
// and prepended as "~/AreaName/"
TestRoute("Contacts/Show/0627ED05-BF19-4090-91FC-AD3865B40983", new {
controller = "Contacts",
action = "Show",
id = "0627ED05-BF19-4090-91FC-AD3865B40983"
});
}
[TestMethod]
public void IdAndName()
{
TestRoute("Contacts/Show/0627ED05-BF19-4090-91FC-AD3865B40983-Some-name", new
{
controller = "Contacts",
action = "Show",
id = "0627ED05-BF19-4090-91FC-AD3865B40983",
name= "Some-name"
});
}
}
La base de fijación:
public class RoutesTestClassBase<TAreaRegistration>
{
protected void TestRoute(string url, object expectations)
{
var routes = new RouteCollection();
var areaRegistration = (AreaRegistration)Activator.CreateInstance(typeof(TAreaRegistration));
// Get an AreaRegistrationContext for my class. Give it an empty RouteCollection
var areaRegistrationContext = new AreaRegistrationContext(areaRegistration.AreaName, routes);
areaRegistration.RegisterArea(areaRegistrationContext);
url = "~/" + areaRegistration.AreaName + "/" + url;
// Mock up an HttpContext object with my test path (using Moq)
var context = new Mock<HttpContextBase>();
context.Setup(c => c.Request.AppRelativeCurrentExecutionFilePath).Returns(url);
// Get the RouteData based on the HttpContext
var routeData = routes.GetRouteData(context.Object);
Assert.IsNotNull(routeData, "Should have found the route");
Assert.AreEqual(areaRegistration.AreaName, routeData.DataTokens["area"]);
foreach (PropertyValue property in GetProperties(expectations))
{
Assert.IsTrue(string.Equals(property.Value.ToString(),
routeData.Values[property.Name].ToString(),
StringComparison.OrdinalIgnoreCase)
, string.Format("Expected ''{0}'', not ''{1}'' for ''{2}''.",
property.Value, routeData.Values[property.Name], property.Name));
}
}
private static IEnumerable<PropertyValue> GetProperties(object o)
{
if (o != null)
{
PropertyDescriptorCollection props = TypeDescriptor.GetProperties(o);
foreach (PropertyDescriptor prop in props)
{
object val = prop.GetValue(o);
if (val != null)
{
yield return new PropertyValue { Name = prop.Name, Value = val };
}
}
}
}
private sealed class PropertyValue
{
public string Name { get; set; }
public object Value { get; set; }
}
}
Estoy probando la unidad de mis rutas en ASP.NET MVC 2. Estoy usando MSTest y estoy usando áreas también.
[TestClass]
public class RouteRegistrarTests
{
[ClassInitialize]
public static void ClassInitialize(TestContext testContext)
{
RouteTable.Routes.Clear();
RouteTable.Routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
RouteTable.Routes.IgnoreRoute("{*favicon}", new { favicon = @"(.*/)?favicon.ico(/.*)?" });
AreaRegistration.RegisterAllAreas();
routes.MapRoute(
"default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
[TestMethod]
public void RouteMaps_VerifyMappings_Match()
{
"~/".Route().ShouldMapTo<HomeController>(n => n.Index());
}
}
Cuando ejecuta AreaRegistration.RegisterAllAreas()
sin embargo, lanza esta excepción:
System.InvalidOperationException: System.InvalidOperationException: este método no puede invocarse durante la etapa de inicialización previa al inicio de la aplicación.
Por lo tanto, reconozco que no puedo llamarlo desde el inicializador de mi clase. Pero cuando puedo llamarlo? Obviamente no tengo un Application_Start
en mi prueba.
Bueno, no hay lugar en el proyecto de prueba en el que puedas poner AreaRegistration.RegisterAllAreas (); para hacerlo funcionar, ya que utiliza la clase System.Web.Compilation.BuildManager para compilar el código para el sitio web, y falla si se llama fuera de la tubería de ASP.NET. Creo que es una especie de error, ya que realmente hace que las pruebas sean muy difíciles de ejecutar.
Pero he inventado una solución de 2 pasos :)
Primero debes modificar el archivo App.Config de tu proyecto de prueba
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
</appSettings>
<connectionStrings>
</connectionStrings>
<system.web>
<compilation debug="true">
<assemblies>
<add assembly="!!!NAME_OF_YOUR_MVC_WEB_ASSEMBLY!!!"/>
</assemblies>
</compilation>
</system.web>
</configuration>
En la actualidad, debe hacer referencia a todos los ensamblajes que contengan descendientes AreaRegistration. Segundo, agregue este código feo antes de AreaRegistration.RegisterAllAreas ();
typeof(BuildManager).GetProperty("PreStartInitStage", BindingFlags.NonPublic | BindingFlags.Static).SetValue(null, 2, null);
typeof(BuildManager).GetField("_topLevelFilesCompiledStarted", BindingFlags.NonPublic | BindingFlags.Instance).SetValue( typeof(BuildManager).GetField("_theBuildManager", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null), true);
Esto funciona solo para .Net 4.0 y superior
Creo que estás buscando la clase TestHelper en la biblioteca de MVC Contrib. Eche un vistazo a las pruebas en MVC Contrib (está escondido allí). Encontrarás que todo está bien burlado.
MVCContrib.UnitTests/TestHelper/RoutesTest.cs : ¡debe actualizar el wiki! Buena suerte
using System.Web.Mvc;
using System.Web.Routing;
using NUnit.Framework;
namespace MVCContrib.Application.UnitTests.TestHelper
{
/// <summary>
/// Summary description for UserRoutesTest
/// </summary>
[TestFixture]
public class UserRoutesTest
{
[TestFixtureSetUp]
public void Setup()
{
var routes = RouteTable.Routes;
routes.Clear();
routes.MapRoute(
"Default", // Route name
"{controller}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
}
[Test]
public void homeIndex()
{
"~/user"
.ShouldMapTo<HomeController>(action => action.Index());
}
[Test]
public void HomeShow()
{
"~/home"
.GivenIncomingAs(HttpVerbs.Put)
.ShouldMapTo<HomeController>(action => action.Index());
}
}
}
Esto es un par de años tarde, pero pensé que iba a compartir. Estoy registrando todas las áreas utilizando la reflexión.
public void RegisterAllAreas()
{
List<AreaRegistration> objects = new List<AreaRegistration>();
foreach (Type type in Assembly.GetAssembly(typeof(MvcApplication)).GetTypes()
.Where(myType => myType.IsClass && !myType.IsAbstract && myType.IsSubclassOf(typeof(AreaRegistration))))
{
objects.Add((AreaRegistration)Activator.CreateInstance(type));
}
objects.ForEach(area => area.RegisterArea(new AreaRegistrationContext(area.AreaName, routes)));
}
Para hacer que AreaRegistration.RegisterAllAreas () funcione, ejecute primero el siguiente código:
Tenga en cuenta que typeof(YourMvcSiteApplication).Assembly
¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡ typeof(YourMvcSiteApplication).Assembly
que typeof(YourMvcSiteApplication).Assembly
debe realizar en el ensamblaje web MVC !!!
object manager = typeof(BuildManager).GetField("_theBuildManager", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null);
manager.SetField("_skipTopLevelCompilationExceptions", true);
manager.SetField("_topLevelFilesCompiledStarted", true);
manager.SetField("_topLevelReferencedAssemblies", new List<Assembly> { typeof(YourMvcSiteApplication).Assembly });
Aquí está el método de extensión SetField () de un objeto de instancia:
public static void SetField<T>(this object source, string fieldName, T value)
{
var type = source.GetType();
var info = type.GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (info != null)
{
info.SetValue(source, value);
}
}
Los códigos anteriores funcionan para .NET 3.5, ¡aún no he probado para .NET 4 o 4.5!
Sé que estoy llegando tarde aquí, pero yo solo resolví este problema. Una solución similar a la de Jason (registrar un área a la vez), pero como usted, estoy usando MvcContrib.TestHelper en lugar de hacer mi propia burla.
[TestInitialize]
public void Setup() {
RouteTable.Routes.Clear();
var areaReg = new AdminAreaRegistration();
areaReg.RegisterArea(new AreaRegistrationContext(areaReg.AreaName, RouteTable.Routes));
}
[TestMethod]
public void admin_should_map_to_home() {
"~/Admin".ShouldMapTo<HomeController>(c => c.Index());
}
Tenga en cuenta que MvcContrib tiene una fuerte dependencia de Rhino Mocks. Si bien prefiero usar Moq, estoy bien con la inclusión de la dll de Rhino solo para obtener esta buena funcionalidad.
AreaRegistration
esto creando una instancia de mi clase AreaRegistration
y llamando al método RegisterArea
.
Por ejemplo, dado un Área llamada "Catálogo" con esta ruta:
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Catalog_default",
"Catalog/{controller}/{action}/{id}",
new {controller = "List", action = "Index", id = "" }
);
}
Este es mi método de prueba:
[TestMethod]
public void TestCatalogAreaRoute()
{
var routes = new RouteCollection();
// Get my AreaRegistration class
var areaRegistration = new CatalogAreaRegistration();
Assert.AreEqual("Catalog", areaRegistration.AreaName);
// Get an AreaRegistrationContext for my class. Give it an empty RouteCollection
var areaRegistrationContext = new AreaRegistrationContext(areaRegistration.AreaName, routes);
areaRegistration.RegisterArea(areaRegistrationContext);
// Mock up an HttpContext object with my test path (using Moq)
var context = new Mock<HttpContextBase>();
context.Setup(c => c.Request.AppRelativeCurrentExecutionFilePath).Returns("~/Catalog");
// Get the RouteData based on the HttpContext
var routeData = routes.GetRouteData(context.Object);
Assert.IsNotNull(routeData, "Should have found the route");
Assert.AreEqual("Catalog", routeData.DataTokens["area"]);
Assert.AreEqual("List", routeData.Values["controller"]);
Assert.AreEqual("Index", routeData.Values["action"]);
Assert.AreEqual("", routeData.Values["id"]);
}