运行测试时,HttpResponseBase.Headers为空

时间:2010-11-01 01:48:55

标签: c# asp.net-mvc unit-testing moq

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

WriteFile(HttpResponseBase response)的{​​{1}}方法中,我在最后有以下代码:

FileResult

它将使用// Write the final output with specific encoding. response.OutputStream.Write(output, 0, output.Length); response.AppendHeader("Content-Encoding", encoding); utf-8,具体取决于请求的gzip标头中的编码。

然后在我的测试中,我设置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()); 总是返回null:

Content-Encoding

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

奇怪的是,当我实际调试Web项目中的var response = mockResponse.Object; Assert.AreEqual("utf-8", response.Headers["Content-Encoding"]); 时,标题已正确发送。

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

3 个答案:

答案 0 :(得分: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));

我怀疑AppendHeader的调用内部还有一些内容,它不喜欢在没有实际HttpResponseBase的情况下添加标题。

如果有更好的主意,请随时提出建议。希望这可以帮助将来的某个人。

答案 1 :(得分:4)

您遇到的问题与您尝试部分模拟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"));

第二种方法是利用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"]);

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

答案 2 :(得分:2)

根据提供的设置代码(不确定是否有拼写错误),我认为应该如下所示

var Response = new Mock<HttpResponseBase>();
Response.Setup(r => r.OutputStream).Returns(new MemoryStream()); 
Response.Setup(r => r.Headers).Returns(new NameValueCollection());