运行测试时,HttpResponseBase.Headers为空

The*_*Sky 5 c# asp.net-mvc unit-testing moq

我正在使用Moq创建一个Mock<HttpResponseBase>测试FileResult我正在为我的MVC2应用程序创建.

WriteFile(HttpResponseBase response)方法中FileResult,我在最后有以下代码:

// Write the final output with specific encoding.
response.OutputStream.Write(output, 0, output.Length);
response.AppendHeader("Content-Encoding", encoding);
Run Code Online (Sandbox Code Playgroud)

它将使用utf-8gzip取决于请求Accept-Encoding标头的编码.

那么在我的测试中,我设置了我的Mock<HttpResponseBase>喜欢:

var mockResponse = new Mock<HttpResponseBase>();
mockResponse.Setup(r => r.OutputStream).Returns(new MemoryStream());
mockResponse.Setup(r => r.Headers).Returns(new NameValueCollection());
Run Code Online (Sandbox Code Playgroud)

但是当我实际检查标头已设置时,Content-Encoding 总是返回null:

var response = mockResponse.Object;
Assert.AreEqual("utf-8", response.Headers["Content-Encoding"]);
Run Code Online (Sandbox Code Playgroud)

奇怪的是,OutputStream获取写入它的数据,我可以声明它正在写入正确的值.

奇怪的是,当我实际调试FileResultWeb项目时,标头被正确发送.

有没有人对此有所了解?如有必要,我可以提供更多代码.

Igo*_*aka 5

您遇到的问题与您试图部分模拟HttpResponseBase课程的事实有关。写入输出流有效,因为属性(OutputStream在本例中)是模拟的,并且是从 SUT(被测系统)访问的。

但是,当您模拟Headers属性时,只会模拟该属性而不是AppendHeader,而这正是您的 SUT 实际所做的。Moq 创建的默认模拟只是将所有方法和属性存根作为返回默认值,因此AppendHeader实际上并没有做任何事情。

对此有两种解决方案,第一种是纯粹的交互测试,是我的首选方法。不要嘲笑Headers,而是验证AppendHeader

Mock<HttpResponseBase> responseMock = new Mock<HttpResponseBase>();
//the rest of response setup
FileResult sut = new MyFileResult();
sut.WriteFile(responseMock.Object);
responseMock.Verify(response=>response.AppendHeader("Content-Encoding", "utf8"));
Run Code Online (Sandbox Code Playgroud)

第二种方法是利用 Moq 的部分模拟,这将使模拟对象调用实际类的方法,除非它们已明确设置。

Mock<HttpResponseBase> responseMock = new Mock<HttpResponseBase>(){ CallBase = true };
//the rest of response setup
FileResult sut = new MyFileResult();
sut.WriteFile(responseMock.Object);
Assert.AreEqual("utf-8", responseMock.Object.Headers["Content-Encoding"]);
Run Code Online (Sandbox Code Playgroud)

我更喜欢第一个版本,因为您正在测试与框架的交互。第二个版本更像是一个基于状态的测试,所以理论上你甚至不需要模拟,你可以使用实际的类。HttpResponseBase然而,在 的情况下,它几乎是有意义的,因为它的构造函数是受保护的,因此通过创建模拟,您基本上是从它内联派生的,而无需手动编写测试替身。


The*_*Sky 4

我最终只是模拟了AppendHeader将标头强制添加到 的HttpResponseBase标头中的方法:

mockResponse
    .Setup(r => r.AppendHeader(It.IsAny<string>(), It.IsAny<string>()))
    .Callback((string k, string v) => mockResponse.Object.Headers.Add(k, v));
Run Code Online (Sandbox Code Playgroud)

我怀疑调用内部还有一些东西AppendHeader不喜欢在没有实际HttpResponseBase到位的情况下添加标头。

如果有更好的想法,欢迎提出。希望这对将来的人有帮助。