I have been assigned to write unit tests for an application that I do not have the right to modify. The method I want to unit test makes a call like this:
HttpWebResponse response = await request.GetResponseAsync() as HttpWebResponse;
I have used Microsoft Fakes for thousands of other tests in this project, so I thought I would do the same here. The simplest and cleanest solution seems to me to shim the request.GetResponseAsync()
method. Then I can return some fake content and ensure that the method handles it properly without actually making a request.
GetResponseAsync()
returns a Task<WebResponse>
object. So I would normally do something like:
using (ShimsContext.Create())
{
System.Net.Fakes.ShimWebRequest.AllInstances.GetResponseAsync = (x) =>
{
return new Task<WebResponse>(() =>
{
HttpWebResponse toRet = new HttpWebResponse();
return toRet;
});
}
}
The problem is that the above doesn't compile because
'System.Net.HttpWebResponse.HttpWebResponse()' is obsolete: 'This API supports the .NET Framework infrastructure and is not intended to be used directly from your code.'
I understand that the type is obsolete and we should be using something different now, but the legacy code I am trying to test does not allow me that luxury. I have looked at many questions on this topic, but none seem to answer this question.
I'll split my answer into 2 sections; the first section is the solution you are looking for... and in the second one I'll discuss on the other options you have in the context of UT(so this answer will help others...)
Since you've already used MsFakes you can create an instance using Shims.
The following snippet is an example which shows the the way to initialize and use ShimHttpWebResponse
:
[Test]
public async Task InitializeShimHttpWebResponse()
{
using (ShimsContext.Create())
{
ShimWebRequest.AllInstances.GetResponseAsync = (x) =>
{
/* you can replace the var with WebResponse if you aren't going to set any behavior */
var res = new ShimHttpWebResponse();
return Task.FromResult((WebResponse)res);
};
ShimWebRequest.CreateString = uri =>
{
WebRequest req = new ShimFileWebRequest();
return req;
};
var request = WebRequest.Create("");
var response = await request.GetResponseAsync() as HttpWebResponse;
Assert.IsNotNull(response);
}
}
Fakes configuration:
<Fakes xmlns="http://schemas.microsoft.com/fakes/2011/">
<Assembly Name="System" Version="4.0.0.0"/>
<ShimGeneration>
<Add FullName="System.Net.HttpWebResponse"/>
<Add FullName="System.Net.WebRequest"/>
<Add FullName="System.Net.HttpWebRequest"/>
<Add FullName="System.Net.FileWebRequest"/>
</ShimGeneration>
</Fakes>
Just to summarize this section; IMO, for the generic case, using a code weaving tool(MsFakes) is the correct way to handle this situation.(I explain it more in the next section)
As I see you have 4 options to create a new instance of HttpWebResponse
:
1. Using reflection - a bad idea in this case(UT..)
2. Inheritance - custom mock...
3. Using proxy based frameworks Eg; Moq, Rhinomocks and etc.
4. Using code weaving tool(as you've already used...) Eg; Msfakes, Typemock Isolator and etc.
There is one more option: Create an integration test instead of UT...
1. Reflection:
HttpWebResponse
has 3 C'tors;(public, internal, protected)
To use the internal\public C'tors you'll have to use reflection, however in most cases it won't work out of the box and then you'll have to violate some the UT rules(small, fast and etc...)
The 2 cases where reflection will work out of the box are:
You don't call any problematic property/method on the instance and the SUT(subject under test) just pass this instance to one of his dependencies
You'll use more reflection initialize the instance fields.
While the first one is a simple case(if this is your case then you should use reflection) the second on is a bad practice in the context of UT; your UTs won't be small/readable/maintainable, the execution time is going to increase, Microsoft may do some refactoring which may break your UTs.
For the protected one you should call it through inheritance(compile vs. runtime...).
2. Inheritance:
The protected C'tor also has the obsolete attribute however the property IsError is set to false, this allows you to inherit this class and then you'll be able to change the behavior of methods/properties; virtual - override, non virtual - only if you have access the the class members(or reflection)
The main disadvantage of this option is the amount of code you'll have to produce and his complexity.
3. Using proxy based frameworks:
Behind the scene those tools use reflection to create an inherited instance based on the class(combine option 1 and 2) Those tools have some built in methods to make your fake code smaller/readable/maintainable.
Cons:(I'm not going to point out all the cons of proxy based tools...)
you still can't change the behavior of non virtual methods.
you don't have an access the the instance members.
Those 2 can be solved by using code weaving tools.
4. Code weaving tools:
Those tools allows you to do almost anything this is the reason why those tools are the best for the generic situation(I can summarize the main disadvantage with the sentence - with great power comes great responsibility...).
In your case they provide you the best solution;
Since HttpWebResponse
has a non virtual methods and you don't want to refactor your code, this the correct solution for you.
However Msfakes doesn't provide you the additional methods/functionality for UT(AssertsWasCalled
, counting and etc...), so unless you are going to replace the tool with other tools, IMO you should combine it with the proxy base tools(free tools!!!!)