¿Cómo probar unitariamente un método de Acción que devuelve JsonResult?


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, cojo cosas de mi repositorio, luego lo proyecto en un List<T> de tipos anónimos.

¿Cómo puedo probarlo por unidad?

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", "name", "type"), tengo que usar 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 Obtengo un error de tiempo de ejecución en el bucle, indicando "object does not contain a definition for id".

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

Author: SteveC, 2011-02-14

7 answers

RPM, parece ser correcto. Todavía tengo mucho que aprender sobre dynamic y tampoco puedo conseguir el enfoque de Marc para trabajar. Así que así es como lo estaba haciendo antes. Puede que le resulte ú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 aserciones en mis 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));
 15
Author: Matt Greer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-02-16 03:05:25

Sé que llego un poco tarde a esto, pero descubrí 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 ser visibles para el proyecto tests.

Abra su ASP.NET MVC proyecto de aplicación y buscar AssemblyInfo.cs de 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 como un encanto

 50
Author: Sergi Papaseit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-03-24 22:17:00

Llego un poco tarde a la fiesta, pero he creado una pequeña envoltura que me permite usar las propiedades dynamic. A partir de esta respuesta tengo esto trabajando en ASP.NET Core 1.0 RC2, pero creo que si reemplaza resultObject.Value con resultObject.Data debería funcionar para versiones no 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);
 6
Author: lc.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-13 09:20:18

Extiendo la solución de Matt Greer y se me ocurre 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 acabo de ejecutar el unittest como este: - Establecer el resultado de los datos esperados:

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

- Afirmar el resultado:

        // Assert
        result.IsJson().WithModel(expected);
 1
Author: Thai Anh Duc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-23 08:26:08

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 lado del cliente. Utiliza Moq y FluentAssertions.

[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
Author: MartijnK,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-09 07:38:07

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);
        }
    }
}
 1
Author: Denis Kiryanov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-29 20:57:32

Si en la prueba sabes exactamente cuál debe ser el resultado de los datos Json, entonces puedes hacer algo como esto:

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

P.D. Esto sería si hubiera usado FluentAssertions.Mvc5 - pero no debería ser difícil convertirlo a cualquier herramienta de prueba que utilice.

 0
Author: AssassinLV,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-30 14:05:28