使用具有匿名类型的 OkNegotiatedContentResult 对 IHttpActionResult 进行单元测试

Posted

技术标签:

【中文标题】使用具有匿名类型的 OkNegotiatedContentResult 对 IHttpActionResult 进行单元测试【英文标题】:Unit testing IHttpActionResult with OkNegotiatedContentResult having anonymous types 【发布时间】: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中:

    数据类型为 IEnumerable 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 ?

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

【问题讨论】:

看看我这里给的答案***.com/a/42718643/5233410 @NKosi 看起来不错,但我没有异步任务 【参考方案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));效果很好

以上是关于使用具有匿名类型的 OkNegotiatedContentResult 对 IHttpActionResult 进行单元测试的主要内容,如果未能解决你的问题,请参考以下文章

具有匿名类型的 C# LINQ 构建表达式

具有匿名类型模型类的剃刀视图。有可能的?

XML 和 XSD 验证失败:元素同时具有“类型”属性和“匿名类型”子项

csharp 具有匿名类型c#的.net mvc json结果

如何使用表达式构建匿名类型?

具有基于变量的标签值的打字稿匿名对象