【问题标题】:How can I cast/convert an anonymous type from HttpResponseMessage for unit testing?如何从 HttpResponseMessage 转换/转换匿名类型以进行单元测试?
【发布时间】:2015-06-03 15:50:30
【问题描述】:

我的任务是为以下 Web API 2 操作编写单元测试:

public HttpResponseMessage Get()
{
    IEnumerable<KeyValuePair<long, string>> things = _service.GetSomething();
    return ActionContext.Request.CreateResponse(things.Select(x => new 
        { 
            Thing1 = x.Prop1.ToString(), 
            Thing2 = x.Prop2 
        }).ToArray());
}

我正在测试状态代码并且工作正常,但我无法弄清楚如何提取内容数据并对其进行测试。到目前为止,这是我的测试:

[TestMethod]
public void GetReturnsOkAndExpectedType()
{
    var controller = GetTheController();
    var response = controller.Get();
    Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
    dynamic responseContent;
    Assert.IsTrue(response.TryGetContentValue(out responseContent));
    //???? How can I cast/convert responseContent here ????
}

如果我调试测试并在即时窗口中检查 responseContent,我会看到这一点(我已经模拟/存根在一个假值中进行测试):

{<>f__AnonymousType1<string, string>[1]}
    [0]: { Thing1 = "123", Thing2 = "unit test" }

我可以将其转换为对象数组,但如果我尝试通过属性名称提取值,我会收到错误(再次立即窗口):

((object[])responseContent)[0].Thing1
'object' does not contain a definition for 'Thing1' and no extension method 'Thing1' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?)

同样,如果我尝试将其转换并投影到相同形状的匿名类型,它将无法编译:

//Thing1 and Thing2 get red-lined here as 'cannot resolve symbol'
var castResult = ((object[]) responseContent).ToList().Select(x => new {x.Thing1, x.Thing2});  

我知道如果我使用JsonConvert 之类的东西对所有内容进行序列化/反序列化,我可能可以实现我想要做的事情,但这似乎不是“正确”的方法。我觉得我在这里遗漏了一些基本的东西。如何从HttpResponseMessage 转换/转换匿名类型以进行单元测试?

【问题讨论】:

  • 可能您最好的选择是使用反射或将结果和数组中的每个项目转换为动态对象(在这两种情况下,您都需要逐个验证属性)。见this question
  • 如果您将[assembly: InternalsVisibleTo("YourTestAssembly")] 添加到您的控制器组件中,那么您将能够在您的测试中写入Assert.AreEqual("Foo", responseContent[0].Thing1);
  • @DanielJ.G.虽然我喜欢InternalsVisibleTo 属性的想法,但如果可能的话,一般的想法是不要更改控制器代码进行测试。您使用反射的其他评论有效。您为什么不将其发布为答案,以便我投票/接受?
  • 我看到你更喜欢反射方法:),我知道为了测试目的而修改生产代码并不理想,但 InternalsVisibleTo 已添加到包含控制器的项目的 AssemblyInfo.cs 文件中在我看来,生成的测试代码看起来更好。无论如何,这两个选项都可以测试您的代码!
  • @DanielJ.G.我同意。我是这个团队的新手,此时我正试图对代码和团队都保持非侵入性;-)

标签: unit-testing c#-4.0 asp.net-web-api2 mstest anonymous-types


【解决方案1】:

正如@Daniel J.G. 所说。在上面的 cmets 中,一种选择是使用反射来获取属性的值。由于您似乎正在使用 MS 测试框架,另一种选择是使用 PrivateObject 类为您做一些反射工作。

所以,你可以在你的测试中做这样的事情:

var poContent = ((object[])responseContent).Select(x => new PrivateObject(x)).ToArray();

Assert.AreEqual("123", poContent[0].GetProperty("Thing1"));
Assert.AreEqual("unit test", poContent[0].GetProperty("Thing2"));

【讨论】:

    猜你喜欢
    • 2010-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-26
    • 1970-01-01
    相关资源
    最近更新 更多