![](/img/trans.png)
[英]How to unit test an Action method that takes Array of an Object and returns JsonResult in ASP.Net MVC
[英]How to unit test an Action method which returns 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”的属性 - 所以不确定是什么问题。
我知道我对这些人有点晚了,但我发现了动态解决方案不起作用的原因:
JsonResult
返回一个匿名对象,默认情况下它们是internal
,因此需要使它们对测试项目可见。
打开您的 ASP.NET MVC 应用程序项目并从名为 Properties 的文件夹中找到
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
我认为把这个记录下来会很好。 像魅力一样工作
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
属性。 在这个答案中,我已经在 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);
这是我使用的一个,也许它对任何人都有用。 它测试返回用于客户端功能的 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);
}
我扩展了 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);
我的解决办法是写扩展方法:
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);
}
}
}
如果在测试中您知道 Json 数据结果究竟应该是什么,那么您可以执行以下操作:
result.Data.ToString().Should().Be(new { param = value}.ToString());
PS 如果您使用过 FluentAssertions.Mvc5,这将是 - 但将其转换为您使用的任何测试工具应该不难。
这就是我断言它的方式
foreach (var item in jsonResult.Data as dynamic) {
((int)item.Id).ShouldBe( expected Id value );
((string)item.name).ShouldBe( "expected name value" );
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.