【问题标题】:Unit testing IHttpActionResult with OkNegotiatedContentResult having anonymous types使用具有匿名类型的 OkNegotiatedContentResult 对 IHttpActionResult 进行单元测试
【发布时间】:2021-11-20 08:12:41
【问题描述】:

我正在使用 MS Test 对我的 Web API 控制器进行单元测试。

下面是我的 Web API 控制器方法,返回类型:

public IHttpActionResult MethodName(){ //Code logic 
    return Ok(new { Data = result, Total = (result.Count() > 0 ? result.First().TotalCount : 0) });
}

在上面的代码sn-p中:

  1. 数据类型为 IEnumerable
  2. Total Count 是 Int 类型

现在测试上面的 API 方法返回类型是我尝试过的:

[TestMethod]
public void TestMethodName(){
var result = controller.MethodName();
//Below line is having issue and thus the contentResult is null 
var contentResult = result as OkNegotiatedContentResult<List<ViewModelName>>;
}

将 Int 添加到 OkNegotiatedContentResult 会引发错误,因为它仅排除单个参数

以下是 API 的实际返回类型:

{System.Web.Http.Results.OkNegotiatedContentResultf__AnonymousType0, int>>}

我应该如何转换结果以便我可以将其内容用于 Assert ?

提前谢谢你。 代码示例表示赞赏。

【问题讨论】:

标签: c# unit-testing asp.net-web-api mstest


【解决方案1】:

您遇到的问题是您的OkNegotiatedContentResult&lt;T&gt; 具有T 的匿名类型。当您像使用new { Date = .., Total =.. } 一样创建临时类型时,编译器会为您生成一个匿名类型。按照设计,匿名类型的名称永远不能在 C# 代码中使用,但仅在编译后的 JIT 代码中有效。

这就是为什么将您的结果转换为OkNegotiatedContentResult&lt;List&lt;ViewModelName&gt;&gt; 将不起作用的原因,因为T 不是List&lt;ViewModelName&gt;,而是anonymoustype&lt;System.Collections.Generic.List&lt;ViewModel'&gt;, int&gt;&gt;。

在我看来,最好的选择不是使用匿名类型,而是创建一个具有您正在寻找的两个属性的实际类,并在您的控制器和测试中使用该类型。

public class MyData
{
  public List<ViewModel> Data { get; set; }
  public int Total { get; set; }
}

如果您不想更改代码,可以使用反射或动态关键字,如下所示:

dynamic contentResult = (dynamic)result.Content;
var data = contentResult.Data

【讨论】:

  • 我明白你的回答。仍然没有任何方法可以在不更改其实现的情况下对其进行测试@WouterdeKort
  • @Jimesh 我已经用另一种使用动态的解决方案更新了答案
  • 有一个构建错误指出:“IHttpActionResult 不包含 Content 的定义,并且找不到接受 IHttpActionResult 类型的第一个参数的异常方法内容” 仅供参考:我已经添加了 Microsoft.CSharp。动态的dll
  • @Jimesh 将您的操作方法的返回类型更改为OkNegotiatedContentResult
  • 不是替代方法,因为将返回类型从 IHttpActionResult 更改为 OkNegotiatedContentResult 需要传递 ,这最终是第一个解决方案“创建一个实际的类而不是使用匿名类型”。
【解决方案2】:

这是一个解决方案,乍一看可能有点麻烦。

所以,首先确保我们收到了OkNegotiatedContentResult

//Act
var actionResult = controller.MethodName();

//Assert - OkNegotiated result
var resultType = actionResult.GetType().GetGenericTypeDefinition();
Assert.AreEqual(resultType.GetGenericTypeDefinition(), typeof(OkNegotiatedContentResult<>));

然后确保匿名类型定义了Data 属性

免责声明:以下代码在运行时无法检索Content

//Assert - Anonymous type
dynamic content = ((dynamic)actionResult).Content;
Assert.IsTrue(content.GetType().GetProperty("Data") != null);

最后确保Data 属性是一个集合

//Assert - Data property 
Assert.IsInstanceOfType(content.Data, typeof(IEnumerable<ViewModelName>));
var data = (IEnumerable<ViewModelName>)content.Data;

【讨论】:

  • 感谢您的努力。主要问题是“var content = result.Content”。错误:“IHttpActionResult 不包含 Content 的定义,不包含扩展方法 Content,但可以找到 IHttpActionResult 类型的第一个参数”
  • 在此之前,虽然 Assert.IsInstanceOfType 我得到:“Assert.IsInstanceOfType 失败。预期类型:1[System.Object]>. Actual type:<System.Web.Http.Results.OkNegotiatedContentResult1[f__AnonymousType02[System.Collections.Generic.List1 [ViewModel],System.Int32]]>。"
  • @Jimesh 请记住,我使用了actionResult 变量来存储controller.MethodName 调用的结果。然后我在将其转换为OkNegotiatedContentResult 后使用了result。在您的测试代码中,您的result 确实是IHttpActinResult,而在我的版本中,它应该是OkNegotiatedContentResult,它确实具有Content 属性。
  • 我的sn-p代码和你的一样。获取:Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll 中出现“Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException”类型的异常,但未在用户代码中处理附加信息:Assert.IsInstanceOfType 失败。预期类型:1[System.Object]>. Actual type:<System.Web.Http.Results.OkNegotiatedContentResult1[f__AnonymousType02[System.Collections.Generic.List1[ViewModel],System.Int32]]>。
  • 彼得,您可以保留它,因为它可能以某种方式对人们有所帮助。示例: Assert.AreEqual(resultType.GetGenericTypeDefinition(), typeof(OkNegotiatedContentResult));效果很好
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-29
  • 1970-01-01
  • 1970-01-01
  • 2014-03-15
  • 1970-01-01
相关资源
最近更新 更多