断言包含匿名类型的JsonResult

本文关键字:JsonResult 类型 包含匿 断言 | 更新日期: 2023-09-27 18:16:29

我试图单元测试一个方法在我的控制器返回一个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);
}

我想知道为什么第一个不工作?除了匿名类型,它似乎基本上也可以工作。

断言包含匿名类型的JsonResult

要清楚,您遇到的具体问题是c#动态不能与非公共成员一起工作。这是故意的,大概是为了阻止这种事情发生。正如LukLed所说,匿名类型仅在同一程序集中是公共的(或者更准确地说,匿名类型只是标记为internal,而不是public),因此您会遇到这个障碍。

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

[assembly: InternalsVisibleTo("AssemblyNameOfYourTestProject")]

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

或者,您可以直接使用暴力反射:

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

在阅读了这里的回复之后,我发现了一篇2009年msdn博客文章,再次采用了不同的方法。但. .评论中是Kieran的一个非常简单和优雅的解决方案…使用.ToString() .

原文:

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

你可以这样测试:

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

我更喜欢这个解决方案,因为它:

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

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