2011-02-14 34 views
39

Si tengo un controlador de la siguiente manera:¿Cómo se prueba un método de acción que devuelve JsonResult?

[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 proyectarlo en un List<T> de tipos anónimos.

¿Cómo puedo probar la unidad?

tiene una propiedad llamada Data, pero es del tipo object, como esperábamos.

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

EDIT:

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 me da un error de ejecución en el bucle, indicando "objeto no contiene una definición para id".

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

+0

Re la edición - podría intentar algo así como var items = (IEnumerable) actionResult.Data; foreach (obj dinámico en elementos) {...} –

+1

He probado aquí con ' var list = (IList) datos; Assert.AreEqual (list.Count, 2); obj dinámico = datos [0]; Assert.AreEqual (obj.id, 12); Assert.AreEqual (obj.name, "Fred"); Assert.AreEqual (obj.type, 'a'); obj = data [1]; Assert.AreEqual (obj.id, 14); Assert.AreEqual (obj.name, "Jim"); Assert.AreEqual (obj.type, 'c'); foreach (d dinámico en la lista) { if (d.id == null) throw new InvalidOperationException(); } 'y parecía estar bien ... –

+0

déjame probar ese código mañana cuando llegue a la oficina. aclamaciones. – RPM1984

Respuesta

14

RPM, parece ser correcto. Todavía tengo mucho que aprender sobre 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 simple método de extensión:

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 sólo tiene que utilizar eso para hacer afirmaciones en mi datos 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)); 
+0

Me alegro de no estar solo tratando de hacer que la solución de Marc funcione. Esto funciona genial Eliminaré mi respuesta y la aceptaré, ya que definitivamente es el mejor enfoque. ¡Gracias! – RPM1984

+1

Para el registro, una solución dinámica funcionaría. Básicamente sería lo mismo que esto con una sintaxis más bonita. Internamente todavía estaría usando el reflejo. Al igual que ViewBag es simplemente ViewData con una sintaxis más bonita. Simplemente no he investigado lo suficientemente dinámico como para implementarlo todavía. Lo haré cuando tenga un poco de tiempo de inactividad. Sé que implica implementar 'IDynamicObject' y envolver JsonResult.Data con él. –

+1

¿De dónde viene el método ThrowIfNull? ¿Hay una clase que extiendes o me estoy perdiendo algo? – morganpdx

49

Sé que soy un poco tarde en este los individuos, pero he encontrado por qué la solución dinámica no funcionaba:

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

Abra su proyecto de aplicación ASP.NET MVC y busque 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 por el registro.Funciona como un encanto

1

que extienden la solución de Matt Greer y llegar a 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 yo sólo corro el unittest como esto: - Ajuste los datos esperados Resultado:

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

- Asegurar el resultado:

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

Aquí hay una que u se, quizás 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); 
} 
1

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); 
     } 
    } 
} 
5

estoy un poco tarde a la fiesta, pero creó un pequeño envoltorio que me permite luego usar dynamic propiedades. A partir de esta respuesta, estoy trabajando en ASP.NET Core 1.0 RC2, pero creo que si reemplaza 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); 
0

Si en la prueba se sabe qué es exactamente el resultado datos JSON debe ser entonces puede hacer algo como esto:

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

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

Cuestiones relacionadas