James Lawruk
James Lawruk

Reputation: 31345

Unit test for a method that takes a HttpResponse object as a parameter. OutputStream is not available

I am trying to create a unit test for a method that takes a HttpResponse object as a parameter. What the correct way of doing this? I hope you seasoned unit testers out there can help me.

Additional information: I tried creating a fake HttpResponse object by passing in a StringWriter.

StringBuilder sb = new StringBuilder();
StringWriter sw = new StringWriter(sb);
HttpResponse response = new HttpResponse(sw);
RssGenerator.Generate(response, otherParameters);

The test fails with the message: System.Web.HttpException: OutputStream is not available when a custom TextWriter is used. The method being tested is part of a class library dll. It uses the Response object's OutputStream to create an RSSFeed with an XMLWriter.

Upvotes: 9

Views: 17384

Answers (6)

If you want a more detailed answer, here is what I ended doing in my project:

_mockResponse = new Mock<HttpResponseBase>();
_contentType = string.Empty;
_mockResponse.SetupSet(r => r.ContentType = It.IsAny<string>()).Callback<string>(value => _contentType = value);
_header = new KeyValuePair<string, string>();
_mockResponse.Setup(r => r.AddHeader(It.IsAny<string>(), It.IsAny<string>())).Callback((string name, string value) => _header = new KeyValuePair<string, string>(name, value));
_writer = new StringBuilder();
_mockResponse.Setup(r => r.Write(It.IsAny<string>())).Callback<string>(s => _writer.Append(s));
var data = "This is the string to send as response";

UTIL.SendStringAsHttpResponse(data, _mockResponse.Object);

Assert.That(_contentType, Is.EqualTo("application/CSV"));
Assert.That(_header.Value, Is.EqualTo("attachment; filename=download.csv"));
Assert.That(_writer.ToString(), Is.EqualTo(data));

where SendStringAsHttpResponse is defined as

public static void SendStringAsHttpResponse(string data, HttpResponseBase response)

Upvotes: 2

Frank Schwieterman
Frank Schwieterman

Reputation: 24480

Need to use namespace System.Web.Abstractions

In particular, take a HttpResponseBase (http://msdn.microsoft.com/en-us/library/system.web.httpresponsebase.aspx) as an input parameter instead of HttpResponse. Then you can mock the HttpResponseBase from your tests. When you have a real HttpResponse to pass in, use HttpResponseWrapper to produce a HttpResponseBase.

Upvotes: 13

ryber
ryber

Reputation: 4555

So the problem with Mocking HttpResponse is that it's sealed so you cant extend it (and most mocking frameworks that Mock concrete classes just overwrite virtual methods). Microsoft was also kind enough to not provide interfaces for most of these...so thanks MS.

For this reason (amongst others) I usually end up writing my own Interfaces for all the sealed MS classes and then writing wrapper implementations that take the MS class in the constructor and shunt them in as soon as possible in the request. Then you can create mocks of the interfaces and have them do whatever you like.

Upvotes: 1

Rodney Gitzel
Rodney Gitzel

Reputation: 2710

If I couldn't refactor the method signature (perhaps the method really only needs one value from the HttpResponse?), I would have Eclipse generate a null implementation of HttpResponse (yes, I'm in Java, but it's the same idea) -- i.e. implements all the methods returning null -- and simply fill in just the ones I need in the method to be tested, and have them return some constant values. (Yeah, could use a mocking framework of some sort, too.)

The reason you can't create your own instance of the existing implementation is it will be heavily tied to your application server, and will likely need all sorts of other gunk to even be instantiated.

If it turned out I need something more complicated than a simple null version of HttpResponse, then I would look at NOT testing this method directly: perhaps a test one or two levels up would be simpler and would provide me the same level of confidence in the code.

Upvotes: 0

Guffa
Guffa

Reputation: 700362

The constructor for HttpResponse isn't intended for your use, it's only there for the ASP.NET page cycle to use, so there is no documentation on how to create one.

You have to set up the HttpResponse object so that it uses an HttpWriter, otherwise it won't let you access the underlying stream. However, the constructor set the _httpWriter variable to null, so you have to find some other way to set it.

You can use the .NET Reflector program to dig around in the HttpResponse class and see what it does.

Upvotes: 1

crunchdog
crunchdog

Reputation: 13478

To be truly unit, you should mock HttpResponse and everything else to only test your methods code. I don't know of any C# mocking utilities, but this question might help.

You can then do something like this (example is Java with Mockito):

HttpResponse response = mock(HttpResponse.class);
OutputStream outputStream = mock(OutputStream.class);
when(response.getOutputStream()).thenReturn(outputStream); 

RssGenerator.generate(response, otherParameters);

Of course, you probably need to configure your mock more than this, but you get the general idea.

Upvotes: 3

Related Questions