I have some code I'm trying to unit test and from what I can tell, it's simply not possible. There are several questions similar to this, some of which incorrectly claim to solve this problem however none them can actually test my product code. Below is a sample of the important lines;
WebRequest req = (WebRequest)WebRequest.Create(url);
WebResponse resp = (WebResponse)req.GetResponse();
/*HttpWebResponse httpResp = (HttpWebResponse)resp;
if (httpResp.StatusCode != (HttpStatusCode)200)
{
_log.Error(String.Format("Recieved non-200 status: {0}", httpResp.StatusCode));
return default(T); // default(T) == null for all reference types
}*/
In the above code, the commented out block is what should actually be in my product. The two lines above it I've had to use in it's palce to make it unit testable.
My mock for unit testing is based my the example here which says it mocks HttpWebResponse
but in fact mocks WebResponse
. From what I can tell it's not possible to mock the HttpWebResponse
because it's constructors have both been deprecated... Here is an example modifying the code from that blog posting;
class TestWebReponse : HttpWebResponse
{
Stream responseStream;
/// <summary>Initializes a new instance of <see cref="TestWebReponse"/>
/// with the response stream to return.</summary>
public TestWebReponse(Stream responseStream)
{
this.responseStream = responseStream;
}
/// <summary>See <see cref="WebResponse.GetResponseStream"/>.</summary>
public override Stream GetResponseStream()
{
return responseStream;
}
}
However the above will not compile due to the HttpWebResponse does not have a constructor that takes 0 arguments
error. Does anyone have a work around for this that employs a similar mechanism for intercepting the product codes request but is capable of returning an HttpWebResponse
? Mocking WebResponse
just simply isn't very useful for these types of applications (no rest consumer should be using WebRequest
/WebResponse
in favor of HttpWebRequest
/HttpWebResponse
) and this is the point at which I must test so using Moq or a similar utility to make one of my methods return the object is not an option, as it would just circumvent the code I'm trying to test (which to be clear is two things; is my request valid and do we correctly deserialize the response). So the only restriction for answers is that the test code needs to remain similar to this;
string response = JsonConvert.SerializeObject(expected);
WebRequest.RegisterPrefix("test", new TestWebRequestCreate());
TestWebRequest request = TestWebRequestCreate.CreateTestRequest(response);
var client = new FakeProprietaryHTTPClient();
PropietaryDataType res = client.MakeRequestThatReturnsDeserializedPropietaryDataType();
// do asserts against that PropertaryDataType
To simulate TestWebRequest, TestWebResponse classs, i'm going to use concept called PartialMock.
With PartialMock you can create an instance from WebRequest, WebResponse and then override some methods.(we usually apply this approach on abstract classes)
class TestWebRequestCreate : IWebRequestCreate
{
static WebRequest _nextRequest;
static readonly object LockObject = new object();
static public WebRequest NextRequest
{
get { return _nextRequest; }
set
{
lock (LockObject)
{
_nextRequest = value;
}
}
}
public WebRequest Create(Uri uri)
{
return _nextRequest;
}
public static WebRequest CreateTestRequest(string response)
{
var request = MockRepository.GeneratePartialMock<WebRequest>();
CreateTestWebRequest(request, response);
NextRequest = request;
return request;
}
private static void CreateTestWebRequest(WebRequest request, string responseStr)
{
var requestStream = new MemoryStream();
request.Stub(x => x.GetRequestStream()).Return(requestStream);
request.Stub(x => x.Method).PropertyBehavior();
request.Stub(x => x.ContentType).PropertyBehavior();
request.Stub(x => x.ContentLength).PropertyBehavior();
var response = CreateTestWebResponse(responseStr);
request.Stub(x => x.GetResponse()).Return(response);
}
private static WebResponse CreateTestWebResponse(string responseStr)
{
var response = MockRepository.GeneratePartialMock<HttpWebResponse>();
var responseStream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(responseStr));
response.Stub(x => x.GetResponseStream()).Return(responseStream);
return response;
}
}
now test all classes together:
var response = "my response string here";
WebRequest.RegisterPrefix("test", new TestWebRequestCreate());
var request = TestWebRequestCreate.CreateTestRequest(response);
var url = "test://MyUrl";
var f = WebRequest.Create(url);
var responce = f.GetResponse() as HttpWebResponse;
var stream = responce.GetResponseStream();
var content = new StreamReader(stream).ReadLine();
Assert.AreEqual(response, content);
with this concept i simulate the inheritance in your link.
Edit:
I used rhino mocks - partial mock in my example.
This is a link to my solution.
If you're happy to move to HttpClient (which you should of you can), there's a library for describing fake responses using a fluent interface: MockHttp
HttpClient is way better designed to handle mocking (and has an object model around almost the entire http spec)
Disclaimer: I'm the author of said library
I don't know about Mock, but 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 able to leverage the fact that I was simply cloning an existing HttpWebResponse object, 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;
}