断言包含匿名类型的JsonResult

时间:2013-06-01 18:55:20

标签: c# asp.net-mvc json unit-testing

我试图在我的一个控制器中单元测试一个方法,返回一个JsonResult。令我惊讶的是,以下代码无效:

[HttpPost]
public JsonResult Test() {
    return Json(new {Id = 123});
}

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

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

// Assert
Assert.AreEqual(123, jsonResult.Id);

Assert抛出异常:

  

'object'不包含'Id'

的定义

我已经解决了以下问题:

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

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

3 个答案:

答案 0 :(得分:32)

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

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

[assembly: InternalsVisibleTo("AssemblyNameOfYourTestProject")]

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

或者,您可以使用强力反射:

Assert.AreEqual(123, jsonResult.GetType().GetProperty("Id").GetValue(jsonResult, null));

答案 1 :(得分:14)

在这里阅读了回复,然后在更远的地方看了一遍,我发现2009 msdn blog post又采用了不同的方法。但是......在评论中Kieran使用.ToString()是一个非常简单且非常优雅的解决方案。

在原始案例中:

[HttpPost]
public JsonResult Test()
{
    return Json(new {Id = 123});
}

您可以通过以下方式进行测试:

var jsonResult = controller.Test();
Assert.AreEqual("{Id = 123}", jsonResult.Data.ToString());

我更喜欢这个解决方案:

  • 避免更改原始代码(InternalsVisibleToExpandoObject),
  • 避免使用 MvcContrib RhinoMocks (这些都没有问题,但为什么只是为了能够测试JsonResult而添加?),并且,
  • 避免使用反射(增加了测试的复杂性)。

答案 2 :(得分:5)

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