it-swarm-ru.tech

Как выполнить юнит-тест метода Action, который возвращает 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);
}

По сути, я беру материал из своего хранилища, а затем проецирую его на List<T> анонимных типов.

Как я могу протестировать его?

У System.Web.Mvc.JsonResult есть свойство с именем Data, но оно имеет тип object, как мы и ожидали.

Значит ли это, что если я хочу проверить, что у объекта JSON есть свойства, которые я ожидаю («id», «name», «type»), я должен использовать отражение?

Правка:

Вот мой тест:

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

Но я получаю ошибку времени выполнения в цикле, утверждая, что «объект не содержит определения для id». 

Когда я ставлю точку останова, actionResult.Data определяется как List<T> анонимных типов, поэтому я решаю, что если я перечислю их, я смогу проверить свойства. Внутри цикла объект делает имеет свойство с именем "id" - поэтому не уверен, в чем проблема.

40
RPM1984

RPM, ты выглядишь правильно. Мне еще многое предстоит узнать о dynamic, и я не могу понять подход Марка к работе. Вот как я это делал раньше. Вы можете найти это полезным. Я только что написал простой метод расширения:

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

Тогда я просто использую это, чтобы сделать утверждения на моих данных 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));
16
Matt Greer

Я знаю, что немного опоздал с этими парнями, но я выяснил, почему динамическое решение не работает:

JsonResult возвращает анонимный объект, и по умолчанию это internal, поэтому его необходимо сделать видимым для проекта тестов.

Откройте проект приложения ASP.NET MVC и найдите AssemblyInfo.cs в папке с именем Properties. Откройте AssemblyInfo.cs и добавьте следующую строку в конец этого файла.

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

Цитата из:http://weblogs.asp.net/gunnarpeipman/archive/2010/07/24/asp-net-mvc-using-dynamic-type-to-test-controller-actions-returning -jsonresult.aspx

Я думал, что было бы хорошо иметь этот для записи. Работает как шарм

51
Sergi Papaseit

Я немного опоздал на вечеринку, но я создал небольшую оболочку, которая позволяет мне затем использовать свойства dynamic. На данный момент у меня есть эта работа на ASP.NET Core 1.0 RC2, но я считаю, что если вы замените resultObject.Value на resultObject.Data, это должно работать для неосновных версий.

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

Использование, предполагая следующий контроллер:

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

Тест (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.

Мое решение состоит в том, чтобы написать метод расширения:

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

Вот тот, который я использую, возможно, он пригодится всем. Он проверяет действие, которое возвращает объект JSON для использования в клиентской функциональности. Он использует Moq и 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

Я расширяю решение от Мэтта Грира и придумываю это небольшое расширение:

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

И я просто запускаю unittest следующим образом: - Установите ожидаемый результат данных:

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

- Утвердить результат:

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

Если в тесте вы знаете, каким должен быть результат данных Json, вы можете просто сделать что-то вроде этого:

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

Постскриптум Это было бы, если бы вы использовали FluentAssertions.Mvc5 - но это не должно быть трудно преобразовать его в любые инструменты тестирования, которые вы используете.

0
AssassinLV