CodeGo.net>如何对返回JsonResult的Action方法进行单元测试?

如果我有这样的控制器:

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

基本上,我从存储库中获取东西,然后将其投影到匿名类型的actionResult.Data中。

如何进行单元测试?

actionResult.Data具有一个名为List<T>的属性,但与我们预期的一样,其类型为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”的属性-因此无法确定问题所在。

RPM1984 asked 2020-08-10T06:13:56Z
8个解决方案
53 votes

我知道我对这些人有些迟了,但是我发现了为什么动态解决方案不起作用的原因:

AssemblyInfo.cs返回一个匿名对象,默认情况下,它们是internal,因此需要使它们对测试项目可见。

打开您的ASP.NET MVC应用程序项目,然后从名为“属性”的文件夹中找到AssemblyInfo.cs。 打开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]

我认为最好有一个记录下来。 奇迹般有效

Sergi Papaseit answered 2020-08-10T06:14:44Z
18 votes

RPM,您看起来是正确的。 关于dynamic,我还有很多要学习的知识,我也无法获得Marc的工作方法。 所以这是我以前的做法。 您可能会发现它很有帮助。 我只是写了一个简单的扩展方法:

    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));
Matt Greer answered 2020-08-10T06:14:06Z
6 votes

我参加聚会有点晚了,但是我创建了一个小包装,然后让我使用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);
lc. answered 2020-08-10T06:15:13Z
1 votes

我扩展了Matt Greer的解决方案,并提出了这一小扩展:

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

我只是这样运行单元测试:-设置预期的数据结果:

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

-确认结果:

        // Assert
        result.IsJson().WithModel(expected);
Thai Anh Duc answered 2020-08-10T06:15:42Z
1 votes

这是我使用的一个,也许对任何人都有用。 它测试一个操作,该操作返回用于客户端功能的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);
}
MartijnK answered 2020-08-10T06:16:02Z
1 votes

我的解决方案是编写扩展方法:

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);
        }
    }
}
Denis Kiryanov answered 2020-08-10T06:16:21Z
0 votes

如果在测试中您知道确切的Json数据结果应该是什么,则可以执行以下操作:

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

附言 如果您使用过FluentAssertions.Mvc5,那就可以了-但是将其转换为您使用的任何测试工具并不难。

AssassinLV answered 2020-08-10T06:16:46Z
0 votes

这就是我的主张

foreach (var item in jsonResult.Data as dynamic) {
    ((int)item.Id).ShouldBe( expected Id value );
    ((string)item.name).ShouldBe( "expected name value" );
}
Harshal answered 2020-08-10T06:17:06Z
translate from https://stackoverflow.com:/questions/4989471/how-to-unit-test-an-action-method-which-returns-jsonresult