it-swarm.com.de

Wie teste ich eine Action-Methode, die JsonResult zurückgibt?

Wenn ich einen Controller wie diesen habe:

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

Grundsätzlich hole ich mir Material aus meinem Repository und projiziere es dann in einen List<T> von anonymen Typen.

Wie kann ich das Gerät testen?

System.Web.Mvc.JsonResult hat eine Eigenschaft namens Data, die jedoch wie erwartet vom Typ object ist.

Bedeutet das also, wenn ich testen möchte, dass das JSON-Objekt die erwarteten Eigenschaften ("id", "name", "type") hat, muss ich Reflection verwenden?

BEARBEITEN:

Hier ist mein Test:

// 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.");
}

Ich bekomme jedoch einen Laufzeitfehler in der Schleife, der besagt "Objekt enthält keine Definition für ID". 

Wenn ich einen Haltepunkt definiere, ist actionResult.Data als List<T> von anonymen Typen definiert. Wenn ich also durch diese Aufzählungen gehe, kann ich die Eigenschaften überprüfen. Innerhalb der Schleife hat das Objekt does eine Eigenschaft namens "id" - also nicht sicher, was das Problem ist.

40
RPM1984

RPM, Sie scheinen richtig zu sein. Ich habe noch viel über dynamic zu lernen, und ich kann auch Marcs Arbeitsweise nicht erreichen. So habe ich es vorher gemacht. Sie finden es vielleicht hilfreich. Ich habe gerade eine einfache Erweiterungsmethode geschrieben:

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

Dann benutze ich das nur, um Aussagen über meine Json-Daten zu machen:

        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));
16
Matt Greer

Ich weiß, ich bin etwas spät dran, aber ich habe herausgefunden, warum die dynamische Lösung nicht funktioniert hat:

JsonResult gibt ein anonymes Objekt zurück. Diese sind standardmäßig internal, daher müssen sie für das Testprojekt sichtbar gemacht werden.

Öffnen Sie Ihr ASP.NET MVC-Anwendungsprojekt, und suchen Sie AssemblyInfo.cs aus dem Ordner mit dem Namen Properties. Öffnen Sie AssemblyInfo.cs und fügen Sie am Ende dieser Datei die folgende Zeile hinzu.

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

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

Ich dachte, es wäre schön, wenn ich das hier haben könnte. Klappt wunderbar

51
Sergi Papaseit

Ich bin etwas spät dran, aber ich habe einen kleinen Wrapper erstellt, mit dem ich dynamic-Eigenschaften verwenden kann. Mit dieser Antwort habe ich an ASP.NET Core 1.0 RC2 gearbeitet, aber ich glaube, wenn Sie resultObject.Value durch resultObject.Data ersetzen, sollte dies für Nicht-Core-Versionen funktionieren.

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

Verwendung, vorausgesetzt, der folgende Controller:

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

Der Test (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
lc.

Meine Lösung ist, die Erweiterungsmethode zu schreiben:

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
Denis Kiryanov

Hier ist eine, die ich benutze, vielleicht ist es für jeden von Nutzen. Es testet eine Aktion, die ein JSON-Objekt zur Verwendung in clientseitigen Funktionen zurückgibt. Es verwendet Moq und 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
MartijnK

Ich erweitere die Lösung von Matt Greer und komme mit dieser kleinen Erweiterung:

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

Und ich führe einfach das unittest wie folgt aus: - Setzen Sie das erwartete Datenergebnis:

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

- Bestätigen Sie das Ergebnis:

        // Assert
        result.IsJson().WithModel(expected);
1
Thai Anh Duc

Wenn Sie im Test wissen, was genau das Json-Datenergebnis sein soll, können Sie Folgendes tun:

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

P.S. Dies wäre der Fall, wenn Sie FluentAssertions.Mvc5 verwendet hätten - es sollte jedoch nicht schwer sein, es in beliebige Test-Tools umzuwandeln, die Sie verwenden.

0
AssassinLV

So behaupte ich es

foreach (var item in jsonResult.Data as dynamic) {
    ((int)item.Id).ShouldBe( expected Id value );
    ((string)item.name).ShouldBe( "expected name value" );
}
0
Harshal