Reputation: 1551
How to create mock HttpWebRequest and HttpWebResponse object I am trying to unittest the following piece of code
HttpWebrequest request;
if (null != request)
{
var response = (HttpWebResponse)request.GetResponse();
using (var sr = new StreamReader(response.GetResponseStream()))
{
jsonResult = sr.ReadToEnd();
}
var myRecords = SerializationHelper.Deserialize<Records>(jsonResult);
}
Upvotes: 13
Views: 13170
Reputation: 1
I think wrapping and abstracting is the most correct way. However, here is my hacky solution.
Just use serialization constructor overload (it is also obsolete, but it generates a warning that can be suppressed) and override methods you need to change.
public class FakeHttpWebRequest : HttpWebRequest
{
private static readonly SerializationInfo SerializationInfo = GetSerializationInfo();
private readonly WebResponse response;
public FakeHttpWebRequest(WebResponse response)
#pragma warning disable CS0618 // Type or member is obsolete
: base(
SerializationInfo,
new StreamingContext())
#pragma warning restore CS0618 // Type or member is obsolete
{
this.response = response;
}
public override WebResponse GetResponse()
{
return response;
}
public override Stream GetRequestStream()
{
return new MemoryStream();
}
private static SerializationInfo GetSerializationInfo()
{
// dummy data required for HttpWebRequest() constructor
var serializationInfo = new SerializationInfo(typeof(HttpWebRequest), new FormatterConverter());
serializationInfo.AddValue("_HttpRequestHeaders", new WebHeaderCollection(), typeof(WebHeaderCollection));
serializationInfo.AddValue("_Proxy", null, typeof(IWebProxy));
serializationInfo.AddValue("_KeepAlive", false);
serializationInfo.AddValue("_Pipelined", false);
serializationInfo.AddValue("_AllowAutoRedirect", false);
serializationInfo.AddValue("_AllowWriteStreamBuffering", false);
serializationInfo.AddValue("_HttpWriteMode", 0);
serializationInfo.AddValue("_MaximumAllowedRedirections", 0);
serializationInfo.AddValue("_AutoRedirects", 0);
serializationInfo.AddValue("_Timeout", 0);
serializationInfo.AddValue("_ContentLength", (long)0);
serializationInfo.AddValue("_MediaType", "");
serializationInfo.AddValue("_OriginVerb", "GET");
serializationInfo.AddValue("_ConnectionGroupName", "");
serializationInfo.AddValue("_Version", new Version(1, 0), typeof(Version));
serializationInfo.AddValue("_OriginUri", new Uri("https://fake.uri"), typeof(Uri));
return serializationInfo;
}
}
public class FakeHttpWebResponse : HttpWebResponse
{
private static readonly SerializationInfo SerializationInfo = GetSerializationInfo();
private readonly Stream responseStream;
public FakeHttpWebResponse(HttpStatusCode statusCode, Stream responseStream)
#pragma warning disable CS0618 // Type or member is obsolete
: base(
SerializationInfo,
new StreamingContext())
#pragma warning restore CS0618 // Type or member is obsolete
{
this.StatusCode = statusCode;
this.responseStream = responseStream;
}
public override HttpStatusCode StatusCode { get; }
public override Stream GetResponseStream()
{
return responseStream;
}
private static SerializationInfo GetSerializationInfo()
{
// dummy data required for HttpWebResponse() constructor
var serializationInfo = new SerializationInfo(typeof(HttpWebResponse), new FormatterConverter());
serializationInfo.AddValue("m_HttpResponseHeaders", new WebHeaderCollection(), typeof(WebHeaderCollection));
serializationInfo.AddValue("m_Uri", new Uri("https://fake.uri"), typeof(Uri));
serializationInfo.AddValue("m_Certificate", null, typeof(System.Security.Cryptography.X509Certificates.X509Certificate));
serializationInfo.AddValue("m_Version", new Version(), typeof(Version));
serializationInfo.AddValue("m_StatusCode", (int)HttpStatusCode.HttpVersionNotSupported);
serializationInfo.AddValue("m_ContentLength", (long)0);
serializationInfo.AddValue("m_Verb", "GET");
serializationInfo.AddValue("m_StatusDescription", "");
serializationInfo.AddValue("m_MediaType", "");
return serializationInfo;
}
}
Finally, in your produciton code provide a way to replace WebRequest creation to your test code.
/// <summary>
/// Delegate to create new web request. Can be set to mock actual requests.
/// </summary>
public Func<string, WebRequest> CreateWebRequest { get; set; } = url => WebRequest.Create(url);
And use this delegate from your production code instead of WebRequest.Create() directly.
In tests you can set this delegate to whatever you need.
Upvotes: 0
Reputation: 193
You can use reflections to rewrite fields of response you need to adjust to create mock response. Here is example of creating a response with status code 429:
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
statusCode = (int)response.StatusCode;
ActivateCallback(responseCallback, response, url, string.Empty);
var fieldStatusCode = response.GetType().GetField("m_StatusCode",
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance);
var statusCodeNew = (HttpStatusCode)409;
fieldStatusCode.SetValue(response, statusCodeNew);
string responceBody = "{\"error\":{\"code\":\"AF429\",\"message\":\"Too many requests. Method=GetContents, PublisherId=00000000-0000-0000-0000-000000000000\"}}";
var propStream = response.GetType().GetField("m_ConnectStream",
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance);
System.IO.MemoryStream ms = new System.IO.MemoryStream(
System.Text.Encoding.UTF8.GetBytes(responceBody));
propStream.SetValue(response, ms);
ms.Position = 0;
Upvotes: 1
Reputation: 728
In a project I've been working on, I needed to do some overriding in an ASMX web service that was throwing errors only in production to find out what XML the service was actually streaming back. In order to do that, I had to create a fresh HttpWebResponse object. The basic trick is using Activator.CreateInstance (which bypasses the fact that the constructor is deprecated). In my example below, I am leveraging the fact that I was simply cloning an existing HttpWebResponse object and resetting the stream, but the technique would be the same for creating one entirely from scratch.
string sLastXML;
public string LastXML
{
get
{
return sLastXML;
}
}
protected override System.Net.WebResponse GetWebResponse(System.Net.WebRequest request)
{
// Get the XML Returned
System.Net.HttpWebResponse oResponse = (System.Net.HttpWebResponse)request.GetResponse();
System.IO.Stream oStream = oResponse.GetResponseStream();
byte[] inStream = new byte[oResponse.ContentLength];
int iActual = 0;
while (iActual < oResponse.ContentLength)
{
iActual += oStream.Read(inStream, iActual, (int)oResponse.ContentLength - iActual);
}
sLastXML = System.Text.Encoding.Default.GetString(inStream);
// Create new stream
System.IO.MemoryStream oNewStream = new System.IO.MemoryStream();
oNewStream.Write(inStream, 0, (int)oResponse.ContentLength);
oNewStream.Position = 0;
// Create new response object
System.Net.HttpWebResponse oNewResponse = (System.Net.HttpWebResponse)System.Activator.CreateInstance(typeof(System.Net.HttpWebResponse));
System.Reflection.PropertyInfo oInfo = oNewResponse.GetType().GetProperty("ResponseStream", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.FlattenHierarchy);
oInfo.SetValue(oNewResponse,oNewStream);
System.Reflection.FieldInfo oFInfo = oNewResponse.GetType().GetField("m_HttpResponseHeaders", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.FlattenHierarchy);
oFInfo.SetValue(oNewResponse, oFInfo.GetValue(oResponse));
oFInfo = oNewResponse.GetType().GetField("m_ContentLength", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.FlattenHierarchy);
oFInfo.SetValue(oNewResponse, oFInfo.GetValue(oResponse));
oFInfo = oNewResponse.GetType().GetField("m_Verb", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.FlattenHierarchy);
oFInfo.SetValue(oNewResponse, oFInfo.GetValue(oResponse));
oFInfo = oNewResponse.GetType().GetField("m_StatusCode", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.FlattenHierarchy);
oFInfo.SetValue(oNewResponse, oFInfo.GetValue(oResponse));
oFInfo = oNewResponse.GetType().GetField("m_StatusDescription", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.FlattenHierarchy);
oFInfo.SetValue(oNewResponse, oFInfo.GetValue(oResponse));
oFInfo = oNewResponse.GetType().GetField("m_IsMutuallyAuthenticated", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.FlattenHierarchy);
oFInfo.SetValue(oNewResponse, oFInfo.GetValue(oResponse));
oFInfo = oNewResponse.GetType().GetField("m_IsVersionHttp11", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.FlattenHierarchy);
oFInfo.SetValue(oNewResponse, oFInfo.GetValue(oResponse));
oFInfo = oNewResponse.GetType().GetField("m_MediaType", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.FlattenHierarchy);
oFInfo.SetValue(oNewResponse, oFInfo.GetValue(oResponse));
oFInfo = oNewResponse.GetType().GetField("m_Uri", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.FlattenHierarchy);
oFInfo.SetValue(oNewResponse, oFInfo.GetValue(oResponse));
oFInfo = oNewResponse.GetType().GetField("m_UsesProxySemantics", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.FlattenHierarchy);
oFInfo.SetValue(oNewResponse, oFInfo.GetValue(oResponse));
oNewResponse.Cookies = oResponse.Cookies;
return oNewResponse;
}
Upvotes: 1
Reputation: 114
You can also use the SOAP formatter to construct a mock HttpWebRequest. First capture the HttpWebRequest you want to mock up as a string, then you can customize it in your unit test and rebuild the HttpWebRequest by deserializing the string.
Here's an example:
static void Main(string[] args)
{
SoapFormatter formatter = new SoapFormatter();
HttpWebRequest myReq = (HttpWebRequest)WebRequest.Create("http://www.google.com");
HttpWebResponse resp = (HttpWebResponse)myReq.GetResponse();
// save this soapRequest as a string and customize it for your mocking up
MemoryStream target = new MemoryStream();
using(target)
{
formatter.Serialize(target, resp);
}
string soapRequest = Encoding.UTF8.GetString(target.GetBuffer());
// now you can use the string to reconstruct the object from the string without needing anything special (other than substituting your own values into the XML)
HttpWebResponse myMockedObject = (HttpWebResponse)formatter.Deserialize(
new MemoryStream(Encoding.UTF8.GetBytes(soapRequest)));
}
Upvotes: 0
Reputation: 31
A mock HttpResponse could look like this eg:
public SenHttpMockResponse(HttpListenerContext context)
{
HttpListenerResponse response = context.Response;
response.Headers.Add("Content-type", @"application/json");
JObject message = JObject.Parse(@"{'SomeParameterName':'ParameterValue'}");
StreamWriter writer = new StreamWriter(response.OutputStream);
writer.Write(message);
writer.Close();
}
On default the Response code is 200 in this case, but you also can adjust with Response.StatusCode = 400;
To mock a request you could use eg a debugger tool. For example the Firefox addon HttpRequester is pretty cool. You can make all types of request and you ll see your response too. Its a handy stuff I would say
Upvotes: 0
Reputation: 8979
It's an old question, but maybe it can be usefull for someone. Adapted from this question in msdn:
private static WebResponse CreateWebResponse(HttpStatusCode httpStatus, MemoryStream responseObject)
{
TcpListener l = new TcpListener(IPAddress.Loopback, 0);
l.Start();
int port = ((IPEndPoint)l.LocalEndpoint).Port;
l.Stop();
// Create a listener.
string prefix = "http://localhost:" + port + "/";
HttpListener listener = new HttpListener();
listener.Prefixes.Add(prefix);
listener.Start();
try
{
listener.BeginGetContext((ar) =>
{
HttpListenerContext context = listener.EndGetContext(ar);
HttpListenerRequest request = context.Request;
// Obtain a response object.
HttpListenerResponse response = context.Response;
response.StatusCode = (int)httpStatus;
// Construct a response.
if (responseObject != null)
{
byte[] buffer = responseObject.ToArray();
// Get a response stream and write the response to it.
Stream output = response.OutputStream;
output.Write(buffer, 0, buffer.Length);
}
response.Close();
}, null);
WebClient client = new WebClient();
try
{
WebRequest request = WebRequest.Create(prefix);
request.Timeout = 30000;
return request.GetResponse();
}
catch (WebException e)
{
return e.Response;
}
}
finally
{
listener.Stop();
}
return null;
}
In here you can write whatever you want in responseObject or leave it null.
Upvotes: 2
Reputation: 7452
I haven't used it personally but the Moles framework should let you do what you are trying to do. see here for an example that intercepts calls to DateTime.Now
.
Upvotes: 0
Reputation: 7470
You can't create mock for HttpWebResponse, rather than mocking out the HttpWebResponse is wraping call behind an interface, and mock that interface.
Upvotes: 2