How to Mock Out a .Net Httpwebresponse

Is it possible to mock out a .NET HttpWebResponse?

You may wish to change your consuming code to take in an interface for a factory that creates requests and responses that can be mocked which wrap the actual implementation.

Update: Revisiting

I've been getting downvotes long after my answer was accepted, and I admit my original answer was poor quality and made a big assumption.

Mocking HttpWebRequest in 4.5+

The confusion from my original answer lies in the fact that you can mock HttpWebResponse in 4.5, but not earlier versions. Mocking it in 4.5 also utilizes obsolete constructors. So, the recommended course of action is to abstract the request and response. Anyways, below is a complete working test using .NET 4.5 with Moq 4.2.

[Test]
public void Create_should_create_request_and_respond_with_stream()
{
// arrange
var expected = "response content";
var expectedBytes = Encoding.UTF8.GetBytes(expected);
var responseStream = new MemoryStream();
responseStream.Write(expectedBytes, 0, expectedBytes.Length);
responseStream.Seek(0, SeekOrigin.Begin);

var response = new Mock<HttpWebResponse>();
response.Setup(c => c.GetResponseStream()).Returns(responseStream);

var request = new Mock<HttpWebRequest>();
request.Setup(c => c.GetResponse()).Returns(response.Object);

var factory = new Mock<IHttpWebRequestFactory>();
factory.Setup(c => c.Create(It.IsAny<string>()))
.Returns(request.Object);

// act
var actualRequest = factory.Object.Create("http://www.google.com");
actualRequest.Method = WebRequestMethods.Http.Get;

string actual;

using (var httpWebResponse = (HttpWebResponse)actualRequest.GetResponse())
{
using (var streamReader = new StreamReader(httpWebResponse.GetResponseStream()))
{
actual = streamReader.ReadToEnd();
}
}

// assert
actual.Should().Be(expected);
}

public interface IHttpWebRequestFactory
{
HttpWebRequest Create(string uri);
}

Better answer: Abstract the Response and Request

Here's a safer bare-bones implementation of an abstraction that will work for prior versions (well, down to 3.5 at least):

[Test]
public void Create_should_create_request_and_respond_with_stream()
{
// arrange
var expected = "response content";
var expectedBytes = Encoding.UTF8.GetBytes(expected);
var responseStream = new MemoryStream();
responseStream.Write(expectedBytes, 0, expectedBytes.Length);
responseStream.Seek(0, SeekOrigin.Begin);

var response = new Mock<IHttpWebResponse>();
response.Setup(c => c.GetResponseStream()).Returns(responseStream);

var request = new Mock<IHttpWebRequest>();
request.Setup(c => c.GetResponse()).Returns(response.Object);

var factory = new Mock<IHttpWebRequestFactory>();
factory.Setup(c => c.Create(It.IsAny<string>()))
.Returns(request.Object);

// act
var actualRequest = factory.Object.Create("http://www.google.com");
actualRequest.Method = WebRequestMethods.Http.Get;

string actual;

using (var httpWebResponse = actualRequest.GetResponse())
{
using (var streamReader = new StreamReader(httpWebResponse.GetResponseStream()))
{
actual = streamReader.ReadToEnd();
}
}

// assert
actual.Should().Be(expected);
}

public interface IHttpWebRequest
{
// expose the members you need
string Method { get; set; }

IHttpWebResponse GetResponse();
}

public interface IHttpWebResponse : IDisposable
{
// expose the members you need
Stream GetResponseStream();
}

public interface IHttpWebRequestFactory
{
IHttpWebRequest Create(string uri);
}

// barebones implementation

private class HttpWebRequestFactory : IHttpWebRequestFactory
{
public IHttpWebRequest Create(string uri)
{
return new WrapHttpWebRequest((HttpWebRequest)WebRequest.Create(uri));
}
}

public class WrapHttpWebRequest : IHttpWebRequest
{
private readonly HttpWebRequest _request;

public WrapHttpWebRequest(HttpWebRequest request)
{
_request = request;
}

public string Method
{
get { return _request.Method; }
set { _request.Method = value; }
}

public IHttpWebResponse GetResponse()
{
return new WrapHttpWebResponse((HttpWebResponse)_request.GetResponse());
}
}

public class WrapHttpWebResponse : IHttpWebResponse
{
private WebResponse _response;

public WrapHttpWebResponse(HttpWebResponse response)
{
_response = response;
}

public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

private void Dispose(bool disposing)
{
if (disposing)
{
if (_response != null)
{
((IDisposable)_response).Dispose();
_response = null;
}
}
}

public Stream GetResponseStream()
{
return _response.GetResponseStream();
}
}

Mocking WebResponse's from a WebRequest

I found this question while looking to do exactly the same thing. Couldn't find an answer anywhere, but after a bit more digging found that the .Net Framework has built in support for this.

You can register a factory object with WebRequest.RegisterPrefix which WebRequest.Create will call when using that prefix (or url). The factory object must implement IWebRequestCreate which has a single method Create which returns a WebRequest. Here you can return your mock WebRequest.

I've put some sample code up at
http://blog.salamandersoft.co.uk/index.php/2009/10/how-to-mock-httpwebrequest-when-unit-testing/

Mocking HttpWebRequest Accept with Moq

What you are trying to do is not possible using Moq. Moq cannot mock a non-virtual method of a concrete implementation (using an IInterface would be fine).

You have a couple of options,

  1. Create a simple interface that is implemented using HttpWebRequest (e.g. IHttpClient) and use that in your unit tests
  2. Use a different mocking framework (take a look at this post for ideas).

How to mock a HttpResponse in Asp.NetCore controller?

The request and response are associated with the controller's current HttpContext, which you will need to set via the ControllerContext when setting up the controller.

//...
//Arrange
HttpContext httpContext = new DefaultHttpContext();
//Controller needs a controller context
var controllerContext = new ControllerContext() {
HttpContext = httpContext,
};
//assign context to controller
var _controller = new MyController(){
ControllerContext = controllerContext,
};

//Act
int actual = _controller.SetCookies(<some parameter>);

//...

The DefaultHttpContext used in the example above, will have a non-null empty request and response by default. This should give you access to the response to exercise your unit test as desired.

Set ContentEncoding when mocking WebResponse

This is what i would do:

      var webHeaderCollectionFieldInfo = typeof (HttpWebResponse).GetField("m_HttpResponseHeaders",
BindingFlags.Instance | BindingFlags.NonPublic);

var webHeaderCollection = new WebHeaderCollection();
webHeaderCollection.Set("Content-Encoding", "cheese");
webHeaderCollectionFieldInfo.SetValue(response.Object, webHeaderCollection);

Add that instead of your last line.

Mock HttpWebResponse with value for the property CharacterSet

I did further research into this, and it seems that you're not going to be able to do what you want. The issue is that the CharacterSet property derives itself using the headers collection. While you can override the Headers property, the CharacterSet property looks directly at the field. The only way the header collection field is set to a non-null value is via an internal constructor which copies it from something called cordata.

The result is that there's no safe way to mock the CharacterSet. I suppose you could use reflection to find the field and set it that way, but that seems like it wouldn't be a good idea as the implementation could change and break your test.

Mocking HttpResponse WriteAsync

Moq cannot Setup extension methods. If you know what the extension method accesses then some cases you can mock a safe path through the extension method.

WriteAsync(HttpResponse, String, CancellationToken)

Writes the given text to the response body. UTF-8 encoding will be used.

directly accesses the HttpResponse.Body.WriteAsync where Body is a Stream via the following overload

/// <summary>
/// Writes the given text to the response body using the given encoding.
/// </summary>
/// <param name="response">The <see cref="HttpResponse"/>.</param>
/// <param name="text">The text to write to the response.</param>
/// <param name="encoding">The encoding to use.</param>
/// <param name="cancellationToken">Notifies when request operations should be cancelled.</param>
/// <returns>A task that represents the completion of the write operation.</returns>
public static Task WriteAsync(this HttpResponse response, string text, Encoding encoding, CancellationToken cancellationToken = default(CancellationToken))
{
if (response == null)
{
throw new ArgumentNullException(nameof(response));
}

if (text == null)
{
throw new ArgumentNullException(nameof(text));
}

if (encoding == null)
{
throw new ArgumentNullException(nameof(encoding));
}

byte[] data = encoding.GetBytes(text);
return response.Body.WriteAsync(data, 0, data.Length, cancellationToken);
}

This means you would need mock response.Body.WriteAsync

//Arrange
var expected = "Hello World";
string actual = null;
var responseMock = new Mock<HttpResponse>();
responseMock
.Setup(_ => _.Body.WriteAsync(It.IsAny<byte[]>(),It.IsAny<int>(), It.IsAny<int>(), It.IsAny<CancellationToken>()))
.Callback((byte[] data, int offset, int length, CancellationToken token)=> {
if(length > 0)
actual = Encoding.UTF8.GetString(data);
})
.ReturnsAsync();

//...code removed for brevity

//...
Assert.AreEqual(expected, actual);

The callback was used to capture the arguments passed to the mocked member. Its value was stored in a variable to be asserted later in the test.



Related Topics



Leave a reply



Submit