net mvc asp c# asp.net-mvc json unit-testing jsonresult

c# - mvc - ¿Cómo probar un método de Acción que devuelve JsonResult?



return json c# jsonresult (7)

Aquí hay uno que uso, tal vez sea de utilidad para cualquiera. Prueba una acción que devuelve un objeto JSON para su uso en la funcionalidad del cliente. Utiliza Moq y FluentAsertions.

[TestMethod] public void GetActivationcode_Should_Return_JSON_With_Filled_Model() { // Arrange... ActivatiecodeController activatiecodeController = this.ActivatiecodeControllerFactory(); CodeModel model = new CodeModel { Activation = "XYZZY", Lifespan = 10000 }; this.deviceActivatieModelBuilder.Setup(x => x.GenereerNieuweActivatiecode()).Returns(model); // Act... var result = activatiecodeController.GetActivationcode() as JsonResult; // Assert... ((CodeModel)result.Data).Activation.Should().Be("XYZZY"); ((CodeModel)result.Data).Lifespan.Should().Be(10000); }

Si tengo un controlador como este:

[HttpPost] public JsonResult FindStuff(string query) { var results = _repo.GetStuff(query); var jsonResult = results.Select(x => new { id = x.Id, name = x.Foo, type = x.Bar }).ToList(); return Json(jsonResult); }

Básicamente, tomo cosas de mi repositorio y luego las proyecto en una List<T> de tipos anónimos.

¿Cómo puedo hacer una prueba unitaria?

System.Web.Mvc.JsonResult tiene una propiedad llamada Data , pero es de tipo object , como esperábamos.

Entonces, ¿eso significa que si quiero probar que el objeto JSON tiene las propiedades que espero ("id", "nombre", "tipo"), tengo que usar la reflexión?

EDITAR:

Aquí está mi prueba:

// Arrange. const string autoCompleteQuery = "soho"; // Act. var actionResult = _controller.FindLocations(autoCompleteQuery); // Assert. Assert.IsNotNull(actionResult, "No ActionResult returned from action method."); dynamic jsonCollection = actionResult.Data; foreach (dynamic json in jsonCollection) { Assert.IsNotNull(json.id, "JSON record does not contain /"id/" required property."); Assert.IsNotNull(json.name, "JSON record does not contain /"name/" required property."); Assert.IsNotNull(json.type, "JSON record does not contain /"type/" required property."); }

Pero recibo un error de tiempo de ejecución en el ciclo, que dice "el objeto no contiene una definición para el id".

Cuando breakpoint, actionResult.Data se define como una List<T> de tipos anónimos, así que supongo que si enumera a través de estos, puedo verificar las propiedades. Dentro del ciclo, el objeto tiene una propiedad llamada "id", por lo que no está seguro de cuál es el problema.


Extiendo la solución de Matt Greer y propongo esta pequeña extensión:

public static JsonResult IsJson(this ActionResult result) { Assert.IsInstanceOf<JsonResult>(result); return (JsonResult) result; } public static JsonResult WithModel(this JsonResult result, object model) { var props = model.GetType().GetProperties(); foreach (var prop in props) { var mv = model.GetReflectedProperty(prop.Name); var expected = result.Data.GetReflectedProperty(prop.Name); Assert.AreEqual(expected, mv); } return result; }

Y solo ejecuto la prueba de unidad de esta manera: - Establezca el resultado esperado de los datos:

var expected = new { Success = false, Message = "Name is required" };

- Afirma el resultado:

// Assert result.IsJson().WithModel(expected);


Llego un poco tarde a la fiesta, pero creé un pequeño contenedor que me permite usar propiedades dynamic . A partir de esta respuesta, estoy trabajando en ASP.NET Core 1.0 RC2, pero creo que si reemplazas resultObject.Value con resultObject.Data , debería funcionar para versiones que no sean core.

public class JsonResultDynamicWrapper : DynamicObject { private readonly object _resultObject; public JsonResultDynamicWrapper([NotNull] JsonResult resultObject) { if (resultObject == null) throw new ArgumentNullException(nameof(resultObject)); _resultObject = resultObject.Value; } public override bool TryGetMember(GetMemberBinder binder, out object result) { if (string.IsNullOrEmpty(binder.Name)) { result = null; return false; } PropertyInfo property = _resultObject.GetType().GetProperty(binder.Name); if (property == null) { result = null; return false; } result = property.GetValue(_resultObject, null); return true; } }

Uso, asumiendo el siguiente controlador:

public class FooController : Controller { public IActionResult Get() { return Json(new {Bar = "Bar", Baz = "Baz"}); } }

La prueba (xUnit):

// Arrange var controller = new FoosController(); // Act var result = await controller.Get(); // Assert var resultObject = Assert.IsType<JsonResult>(result); dynamic resultData = new JsonResultDynamicWrapper(resultObject); Assert.Equal("Bar", resultData.Bar); Assert.Equal("Baz", resultData.Baz);


Mi solución es escribir el método de extensión:

using System.Reflection; using System.Web.Mvc; namespace Tests.Extensions { public static class JsonExtensions { public static object GetPropertyValue(this JsonResult json, string propertyName) { return json.Data.GetType().GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public).GetValue(json.Data, null); } } }


RPM, pareces estar en lo cierto. Todavía tengo mucho que aprender sobre la dynamic y tampoco puedo hacer que Marc se acerque al trabajo. Así que aquí es cómo lo estaba haciendo antes. Puede ser útil. Acabo de escribir un método de extensión simple:

public static object GetReflectedProperty(this object obj, string propertyName) { obj.ThrowIfNull("obj"); propertyName.ThrowIfNull("propertyName"); PropertyInfo property = obj.GetType().GetProperty(propertyName); if (property == null) { return null; } return property.GetValue(obj, null); }

Entonces solo uso eso para hacer afirmaciones en mis datos de Json:

JsonResult result = controller.MyAction(...); ... Assert.That(result.Data, Is.Not.Null, "There should be some data for the JsonResult"); Assert.That(result.Data.GetReflectedProperty("page"), Is.EqualTo(page));


Sé que llegué un poco tarde a esto chicos, pero descubrí por qué la solución dinámica no funcionaba:

JsonResult devuelve un objeto anónimo y estos son, de forma predeterminada, internal , por lo que deben hacerse visibles para el proyecto de pruebas.

Abra su proyecto de aplicación ASP.NET MVC y encuentre AssemblyInfo.cs desde la carpeta llamada Propiedades. Abra AssemblyInfo.cs y agregue la siguiente línea al final de este archivo.

[assembly: InternalsVisibleTo("MyProject.Tests")]

Citado de: http://weblogs.asp.net/gunnarpeipman/archive/2010/07/24/asp-net-mvc-using-dynamic-type-to-test-controller-actions-returning-jsonresult.aspx

Pensé que sería bueno tener este para el registro. Funciona de maravilla


Si en la prueba sabes exactamente qué resultado de datos Json debería ser, entonces puedes hacer algo como esto:

result.Data.ToString().Should().Be(new { param = value}.ToString());

PS Esto sería si hubiera usado FluentAssertions.Mvc5, pero no debería ser difícil convertirlo en las herramientas de prueba que use.