如何unit testing返回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); } 

基本上,我从我的仓库中获取东西,然后将其投影到匿名types的List<T>中。

我如何进行unit testing?

System.Web.Mvc.JsonResult有一个名为Data的属性,但是它的types是object ,正如我们所期望的那样。

那么这是否意味着如果我想testingJSON对象具有我期望的属性(“id”,“name”,“type”),我必须使用reflection?

编辑:

这是我的testing:

 // 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>的匿名types,所以我想如果我通过这些枚举,我可以检查属性。 在循环内部,对象确实有一个名为“id”的属性 – 所以不知道是什么问题。

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

我知道我对这个家伙有点晚了,但我发现为什么dynamic解决scheme不起作用:

JsonResult返回一个匿名对象,默认情况下,它们是internal ,所以它们需要在testing项目中可见。

打开ASP.NET MVC应用程序项目,从名为Properties的文件夹中findAssemblyInfo.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

我认为有这个logging是很好的。 奇迹般有效

我晚了一点,但我创造了一个小包装让我使用dynamic属性。 至于这个答案,我已经在ASP.NET Core 1.0 RC2上工作,但我相信如果你用resultObject.ValuereplaceresultObject.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"}); } } 

testing(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); 

我从马特·格里尔(Matt Greer)那里推出了这个解决scheme,

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

这是我使用的一个,也许这对任何人都有用。 它testing一个返回JSON对象的动作,以用于客户端function。 它使用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); } 

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

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

如果在testing中您知道Json数据结果应该是什么,那么您可以执行如下操作:

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

PS这将是如果你使用FluentAssertions.Mvc5 – 但它不应该很难转换为任何testing工具,你使用。