We have reasonably complex HTTP client application here. Using TDD proved to be pretty difficult.
We started by using simple interface:
public interface IHttp
{
TextReader RequestAndOpenTextReader(
Uri uri, string method, TextReader body = null开发者_StackOverflow);
WebHeaderCollection RequestAndGetHeaders(
Uri uri, string method, TextReader body = null);
void Request(Uri uri, string method, TextReader body = null);
}
However application requires more and more exposure into details of HTTP and continuing to evolve abovementioned interface seems to be a bit redundant.
So goes question. Is there any convenient and standard way to mock out HttpWebRequest/HttpWebResponse without using Profiler API based mocking framework?
Or some alternative open source implementation of client HTTP API?
I wrote a fake for HttpWebRequest, not much implemented in it yet. It has a parameterless constructor so it is easy to use:
FakeHttpWebRequest fakeHttpWebRequest = new FakeHttpWebRequest();
Assert.AreEqual("application/x-www-form-urlencoded", fakeHttpWebRequest.ContentType);
/// <summary>
/// http://reflector.webtropy.com/default.aspx/4@0/4@0/untmp/DEVDIV_TFS/Dev10/Releases/RTMRel/ndp/fx/src/Net/System/Net/HttpWebRequest@cs/1305376/HttpWebRequest@cs
/// </summary>
public class FakeHttpWebRequest : HttpWebRequest
{
private static readonly SerializationInfo SerializationInfo;
private readonly MemoryStream _requestStream;
static FakeHttpWebRequest()
{
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", 500);
SerializationInfo.AddValue("_ReadWriteTimeout", 500);
SerializationInfo.AddValue("_MaximumResponseHeadersLength", 128);
SerializationInfo.AddValue("_ContentLength", 0);
SerializationInfo.AddValue("_MediaType", 0);
SerializationInfo.AddValue("_OriginVerb", 0);
SerializationInfo.AddValue("_ConnectionGroupName", null);
SerializationInfo.AddValue("_Version", HttpVersion.Version11, typeof(Version));
SerializationInfo.AddValue("_OriginUri", new Uri("http://test.se"), typeof(Uri));
serializationInfo.AddValue("_NextExtension", _NextExtension);
}
public FakeHttpWebRequest()
: base(SerializationInfo, new StreamingContext())
{
_requestStream = new MemoryStream();
}
/// <summary>
/// Initializes a new instance of the <see cref="T:System.Net.HttpWebRequest"/> class from the specified instances of the <see cref="T:System.Runtime.Serialization.SerializationInfo"/> and <see cref="T:System.Runtime.Serialization.StreamingContext"/> classes.
/// </summary>
/// <param name="serializationInfo">A <see cref="T:System.Runtime.Serialization.SerializationInfo"/> object that contains the information required to serialize the new <see cref="T:System.Net.HttpWebRequest"/> object. </param><param name="streamingContext">A <see cref="T:System.Runtime.Serialization.StreamingContext"/> object that contains the source and destination of the serialized stream associated with the new <see cref="T:System.Net.HttpWebRequest"/> object. </param>
protected FakeHttpWebRequest(SerializationInfo serializationInfo, StreamingContext streamingContext)
: base(serializationInfo, streamingContext)
{
}
#region Overrides of HttpWebRequest
/// <summary>
/// Gets a <see cref="T:System.IO.Stream"/> object to use to write request data.
/// </summary>
/// <returns>
/// A <see cref="T:System.IO.Stream"/> to use to write request data.
/// </returns>
/// <exception cref="T:System.Net.ProtocolViolationException">The <see cref="P:System.Net.HttpWebRequest.Method"/> property is GET or HEAD.-or- <see cref="P:System.Net.HttpWebRequest.KeepAlive"/> is true, <see cref="P:System.Net.HttpWebRequest.AllowWriteStreamBuffering"/> is false, <see cref="P:System.Net.HttpWebRequest.ContentLength"/> is -1, <see cref="P:System.Net.HttpWebRequest.SendChunked"/> is false, and <see cref="P:System.Net.HttpWebRequest.Method"/> is POST or PUT. </exception><exception cref="T:System.InvalidOperationException">The <see cref="M:System.Net.HttpWebRequest.GetRequestStream"/> method is called more than once.-or- <see cref="P:System.Net.HttpWebRequest.TransferEncoding"/> is set to a value and <see cref="P:System.Net.HttpWebRequest.SendChunked"/> is false. </exception><exception cref="T:System.NotSupportedException">The request cache validator indicated that the response for this request can be served from the cache; however, requests that write data must not use the cache. This exception can occur if you are using a custom cache validator that is incorrectly implemented. </exception><exception cref="T:System.Net.WebException"><see cref="M:System.Net.HttpWebRequest.Abort"/> was previously called.-or- The time-out period for the request expired.-or- An error occurred while processing the request. </exception><exception cref="T:System.ObjectDisposedException">In a .NET Compact Framework application, a request stream with zero content length was not obtained and closed correctly. For more information about handling zero content length requests, see Network Programming in the .NET Compact Framework.</exception><PermissionSet><IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/><IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/><IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/><IPermission class="System.Net.DnsPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/><IPermission class="System.Net.WebPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/><IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/></PermissionSet>
public override Stream GetRequestStream()
{
return _requestStream;
}
#endregion
}
精彩评论