断言包含匿名类型的JsonResult

Dim*_*rov 34 c# asp.net-mvc json unit-testing

我试图在我的一个控制器中单独测试一个方法,返回一个JsonResult.令我惊讶的是,以下代码不起作用:

[HttpPost]
public JsonResult Test() {
    return Json(new {Id = 123});
}
Run Code Online (Sandbox Code Playgroud)

这是我测试它的方式(还要注意测试代码驻留在另一个程序集中):

// Act
dynamic jsonResult = testController.Test().Data;

// Assert
Assert.AreEqual(123, jsonResult.Id);
Run Code Online (Sandbox Code Playgroud)

Assert抛出一个异常:

'object'不包含'Id'的定义

我已经通过使用以下方法解决了它:

[HttpPost]
public JsonResult Test() {
   dynamic data = new ExpandoObject();
   data.Id = 123;
   return Json(data);
}
Run Code Online (Sandbox Code Playgroud)

我试图理解为什么不是第一个工作?它似乎也基本上适用于任何匿名类型.

Kir*_*oll 35

需要说明的是,您遇到的具体问题是C#dynamic不适用于非公共成员.这是设计的,大概是为了阻止那种事情.因为正如LukLed所说,匿名类型只在同一个程序集中公开(或者更确切地说,匿名类型只是标记internal,而不是public),你正在遇到这个障碍.

可能是最干净的解决方案供您使用InternalsVisibleTo.它允许您命名另一个可以访问其非公共成员的程序集.将其用于测试是其存在的主要原因之一.在您的示例中,您将在主项目的AssemblyInfo.cs中放置以下行:

[assembly: InternalsVisibleTo("AssemblyNameOfYourTestProject")]
Run Code Online (Sandbox Code Playgroud)

一旦你这样做,错误就会消失(我自己就试过了).

或者,你可能只是使用强力反射:

Assert.AreEqual(123, jsonResult.GetType().GetProperty("Id").GetValue(jsonResult, null));
Run Code Online (Sandbox Code Playgroud)


Sco*_*NET 15

在阅读了这里的回复,然后在更远的地方看了一下,我发现了一个2009 msdn博客文章,再次采用了不同的方法.但是......在评论中,Kieran使用了一个非常简单而且非常优雅的解决方案.ToString().

在你原来的情况下:

[HttpPost]
public JsonResult Test()
{
    return Json(new {Id = 123});
}
Run Code Online (Sandbox Code Playgroud)

你可以通过这样做来测试:

var jsonResult = controller.Test();
Assert.AreEqual("{Id = 123}", jsonResult.Data.ToString());
Run Code Online (Sandbox Code Playgroud)

我更喜欢这个解决方案:

  • 避免更改原始代码(InternalsVisibleTo,ExpandoObject),
  • 避免使用MvcContribRhinoMocks(这些都没有问题,但为什么只是为了能够测试JsonResult?),以及
  • 避免使用Reflection(增加了测试的复杂性).


Luk*_*Led 5

匿名类型是内部的,因此您不能将它们公开给另一个带有测试的库。如果将测试代码与控制器放在同一库中,则它将起作用。

  • 您也可以将测试程序集声明为“朋友程序集”,以公开内部信息。http://msdn.microsoft.com/zh-CN/library/0tke9fxk(v=vs.80).aspx (2认同)