C# 运行测试时,HttpResponseBase.Header为空

C# 运行测试时,HttpResponseBase.Header为空,c#,asp.net-mvc,unit-testing,moq,C#,Asp.net Mvc,Unit Testing,Moq,我正在使用Moq创建一个Mock来测试我正在为我的MVC2应用程序创建的FileResult 在FileResult的WriteFile(HttpResponseBase response)方法中,我在末尾有以下代码: // Write the final output with specific encoding. response.OutputStream.Write(output, 0, output.Length); response.AppendHeader("Content-Enco

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

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

// 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
,如下所示:

var mockResponse = new Mock<HttpResponseBase>();
mockResponse.Setup(r => r.OutputStream).Returns(new MemoryStream());
mockResponse.Setup(r => r.Headers).Returns(new NameValueCollection());
奇怪的是OutputStream得到了写入它的数据,我可以断言它正在写入正确的值

奇怪的是,当我在web项目中调试
FileResult
时,头被正确发送


有人对此有所了解吗?如果需要的话,我可以提供更多的代码。

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

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

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

mockResponse
    .Setup(r => r.AppendHeader(It.IsAny<string>(), It.IsAny<string>()))
    .Callback((string k, string v) => mockResponse.Object.Headers.Add(k, v));
mockResponse
.Setup(r=>r.AppendHeader(It.IsAny(),It.IsAny())
.Callback((字符串k,字符串v)=>mockResponse.Object.Headers.Add(k,v));
我怀疑在
AppendHeader
的调用中,有一个更深层的东西不喜欢在没有实际
HttpResponseBase
的情况下添加头


如果有更好的主意,请随时提出。希望这对将来的人有所帮助。

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

但是,当您模拟
Headers
属性时,只有该属性被模拟,而不是
AppendHeader
,这是SUT实际执行的操作。Moq创建的默认mock只是将所有方法和属性存根为返回默认值,因此
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"));
Mock responseMock=new Mock();
//响应设置的其余部分
FileResult sut=新的MyFileResult();
sut.WriteFile(responseMock.Object);
验证(response=>response.AppendHeader(“内容编码”,“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"]);
Mock-responseMock=new-Mock(){CallBase=true};
//响应设置的其余部分
FileResult sut=新的MyFileResult();
sut.WriteFile(responseMock.Object);
Assert.AreEqual(“utf-8”,responseMock.Object.Header[“内容编码”]);

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

您可以包含更多关于Moq设置的代码吗?是的,这是文章中的一个错误。我在帖子的代码中对它的命名与我实际使用的代码不同。在“real”代码中,它是我创建的“MockHttpContext”类的一个名为“Response”的属性。上面的代码是唯一修改
Mock
@thecloudlessky的部分-我的答案是没有意义的,然后这就成功了。有趣的是,您必须使用
MemoryStream
来设置标题。在这之前试过一百种其他的东西。谢谢,@Ahmad.Yeah我没意识到Moq是stubing
AppendHeader
。设置
CallBase=true
会显示这一点。我喜欢您验证它是否设置了头的想法,但它并不能真正确保头设置为发送的最终状态(如您所说)。我可能会意外地调用另一个
AppendHeader
,然后用另一个调用覆盖它(尽管我想我也可以验证这一点)。谢谢你的想法!
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"]);