Skip to content

Instantly share code, notes, and snippets.

@trailmax
Created November 21, 2013 23:03
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save trailmax/7591440 to your computer and use it in GitHub Desktop.
Save trailmax/7591440 to your computer and use it in GitHub Desktop.
HttpSimulator code from Phil Haak (http://haacked.com/archive/2007/06/19/unit-tests-web-code-without-a-web-server-using-httpsimulator.aspx) This version is StyleCop-ed. Also backing-up in case that page dies. Linked to my blog-post
using System;
using System.Collections.Specialized;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Configuration;
using System.Web.Hosting;
using System.Web.SessionState;
namespace Onboard.Tests.ZeroFriction.HttpMocking
{
public enum HttpVerb
{
GET,
HEAD,
POST,
PUT,
DELETE,
}
/// <summary>
/// Useful class for simulating the HttpContext. This does not actually
/// make an HttpRequest, it merely simulates the state that your code
/// would be in "as if" handling a request. Thus the HttpContext.Current
/// property is populated.
/// http://haacked.com/archive/2007/06/19/unit-tests-web-code-without-a-web-server-using-httpsimulator.aspx
/// </summary>
public class HttpSimulator : IDisposable
{
private const string DefaultPhysicalAppPath = @"c:\InetPub\wwwRoot\";
private readonly NameValueCollection headers = new NameValueCollection();
private readonly NameValueCollection formVars = new NameValueCollection();
private StringBuilder builder;
private Uri referrer;
private string page;
public HttpSimulator()
: this("/", DefaultPhysicalAppPath)
{
}
public HttpSimulator(string applicationPath)
: this(applicationPath, DefaultPhysicalAppPath)
{
}
public HttpSimulator(string applicationPath, string physicalApplicationPath)
{
this.ApplicationPath = applicationPath;
this.PhysicalApplicationPath = physicalApplicationPath;
}
/// <summary>
/// Sets up the HttpContext objects to simulate a GET request.
/// </summary>
/// <remarks>
/// Simulates a request to http://localhost/
/// </remarks>
public HttpSimulator SimulateRequest()
{
return SimulateRequest(new Uri("http://localhost/"));
}
/// <summary>
/// Sets up the HttpContext objects to simulate a GET request.
/// </summary>
/// <param name="url"></param>
public HttpSimulator SimulateRequest(Uri url)
{
return SimulateRequest(url, HttpVerb.GET);
}
/// <summary>
/// Sets up the HttpContext objects to simulate a request.
/// </summary>
/// <param name="url"></param>
/// <param name="httpVerb"></param>
public HttpSimulator SimulateRequest(Uri url, HttpVerb httpVerb)
{
return SimulateRequest(url, httpVerb, null, null);
}
/// <summary>
/// Sets up the HttpContext objects to simulate a POST request.
/// </summary>
/// <param name="url"></param>
/// <param name="formVariables"></param>
public HttpSimulator SimulateRequest(Uri url, NameValueCollection formVariables)
{
return SimulateRequest(url, HttpVerb.POST, formVariables, null);
}
/// <summary>
/// Sets up the HttpContext objects to simulate a POST request.
/// </summary>
/// <param name="url"></param>
/// <param name="formVariables"></param>
/// <param name="headers"></param>
public HttpSimulator SimulateRequest(Uri url, NameValueCollection formVariables, NameValueCollection headers)
{
return SimulateRequest(url, HttpVerb.POST, formVariables, headers);
}
/// <summary>
/// Sets up the HttpContext objects to simulate a request.
/// </summary>
/// <param name="url"></param>
/// <param name="httpVerb"></param>
/// <param name="headers"></param>
public HttpSimulator SimulateRequest(Uri url, HttpVerb httpVerb, NameValueCollection headers)
{
return SimulateRequest(url, httpVerb, null, headers);
}
/// <summary>
/// Sets up the HttpContext objects to simulate a request.
/// </summary>
/// <param name="url"></param>
/// <param name="httpVerb"></param>
/// <param name="formVariables"></param>
/// <param name="headers"></param>
protected virtual HttpSimulator SimulateRequest(Uri url, HttpVerb httpVerb, NameValueCollection formVariables, NameValueCollection headers)
{
HttpContext.Current = null;
ParseRequestUrl(url);
if (this.responseWriter == null)
{
this.builder = new StringBuilder();
this.responseWriter = new StringWriter(builder);
}
SetHttpRuntimeInternals();
var query = ExtractQueryStringPart(url);
if (formVariables != null)
{
formVars.Add(formVariables);
}
if (formVars.Count > 0)
{
httpVerb = HttpVerb.POST; //Need to enforce this.
}
if (headers != null)
{
this.headers.Add(headers);
}
this.workerRequest = new SimulatedHttpRequest(ApplicationPath, PhysicalApplicationPath, PhysicalPath, Page, query, this.responseWriter, host, port, httpVerb.ToString());
this.workerRequest.Form.Add(formVars);
this.workerRequest.Headers.Add(this.headers);
if (referrer != null)
{
this.workerRequest.SetReferrer(referrer);
}
InitializeSession();
InitializeApplication();
#region Console Debug INfo
Console.WriteLine("host: " + host);
Console.WriteLine("virtualDir: " + applicationPath);
Console.WriteLine("page: " + localPath);
Console.WriteLine("pathPartAfterApplicationPart: " + page);
Console.WriteLine("appPhysicalDir: " + physicalApplicationPath);
Console.WriteLine("Request.Url.LocalPath: " + HttpContext.Current.Request.Url.LocalPath);
Console.WriteLine("Request.Url.Host: " + HttpContext.Current.Request.Url.Host);
Console.WriteLine("Request.FilePath: " + HttpContext.Current.Request.FilePath);
Console.WriteLine("Request.Path: " + HttpContext.Current.Request.Path);
Console.WriteLine("Request.RawUrl: " + HttpContext.Current.Request.RawUrl);
Console.WriteLine("Request.Url: " + HttpContext.Current.Request.Url);
Console.WriteLine("Request.Url.Port: " + HttpContext.Current.Request.Url.Port);
Console.WriteLine("Request.ApplicationPath: " + HttpContext.Current.Request.ApplicationPath);
Console.WriteLine("Request.PhysicalPath: " + HttpContext.Current.Request.PhysicalPath);
Console.WriteLine("HttpRuntime.AppDomainAppPath: " + HttpRuntime.AppDomainAppPath);
Console.WriteLine("HttpRuntime.AppDomainAppVirtualPath: " + HttpRuntime.AppDomainAppVirtualPath);
Console.WriteLine("HostingEnvironment.ApplicationPhysicalPath: " + HostingEnvironment.ApplicationPhysicalPath);
Console.WriteLine("HostingEnvironment.ApplicationVirtualPath: " + HostingEnvironment.ApplicationVirtualPath);
#endregion
return this;
}
private static void InitializeApplication()
{
Type appFactoryType = Type.GetType("System.Web.HttpApplicationFactory, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
object appFactory = ReflectionHelper.GetStaticFieldValue<object>("_theApplicationFactory", appFactoryType);
ReflectionHelper.SetPrivateInstanceFieldValue("_state", appFactory, HttpContext.Current.Application);
}
private void InitializeSession()
{
HttpContext.Current = new HttpContext(workerRequest);
HttpContext.Current.Items.Clear();
var session = (HttpSessionState)ReflectionHelper.Instantiate(typeof(HttpSessionState), new Type[] { typeof(IHttpSessionState) }, new FakeHttpSessionState());
HttpContext.Current.Items.Add("AspSession", session);
}
public class FakeHttpSessionState : NameObjectCollectionBase, IHttpSessionState
{
private readonly bool isNewSession = true;
private readonly string sessionID = Guid.NewGuid().ToString();
private readonly HttpStaticObjectsCollection staticObjects = new HttpStaticObjectsCollection();
private readonly object syncRoot = new Object();
private int timeout = 30; //minutes
private int lcid;
private int codePage;
/// <summary>
/// Ends the current session.
/// </summary>
///
public void Abandon()
{
BaseClear();
}
/// <summary>
/// Adds a new item to the session-state collection.
/// </summary>
/// <param name="name">The name of the item to add to the session-state collection. </param>
/// <param name="value">The value of the item to add to the session-state collection. </param>
public void Add(string name, object value)
{
BaseAdd(name, value);
}
/// <summary>
/// Deletes an item from the session-state item collection.
/// </summary>
/// <param name="name">The name of the item to delete from the session-state item collection. </param>
public void Remove(string name)
{
BaseRemove(name);
}
/// <summary>
/// Deletes an item at a specified index from the session-state item collection.
/// </summary>
/// <param name="index">The index of the item to remove from the session-state collection. </param>
public void RemoveAt(int index)
{
BaseRemoveAt(index);
}
/// <summary>
/// Clears all values from the session-state item collection.
/// </summary>
public void Clear()
{
BaseClear();
}
/// <summary>
/// Clears all values from the session-state item collection.
/// </summary>
public void RemoveAll()
{
BaseClear();
}
/// <summary>
/// Copies the collection of session-state item values to a one-dimensional array, starting at the specified index in the array.
/// </summary>
/// <param name="array">The <see cref="T:System.Array"></see> that receives the session values. </param>
/// <param name="index">The index in array where copying starts. </param>
public void CopyTo(Array array, int index)
{
throw new NotImplementedException();
}
/// <summary>
/// Gets the unique session identifier for the session.
/// </summary>
/// <returns> The session ID. </returns>
public string SessionID
{
get { return sessionID; }
}
/// <summary>
/// Gets and sets the time-out period (in minutes) allowed between requests before the session-state provider terminates the session.
/// </summary>
/// <returns> The time-out period, in minutes. </returns>
public int Timeout
{
get { return timeout; }
set { timeout = value; }
}
/// <summary>
/// Gets a value indicating whether the session was created with the current request.
/// </summary>
/// <returns> true if the session was created with the current request; otherwise, false. </returns>
///
public bool IsNewSession
{
get { return isNewSession; }
}
/// <summary>
/// Gets the current session-state mode.
/// </summary>
/// <returns> One of the <see cref="T:System.Web.SessionState.SessionStateMode"></see> values. </returns>
public SessionStateMode Mode
{
get { return SessionStateMode.InProc; }
}
/// <summary>
/// Gets a value indicating whether the session ID is embedded in the URL or stored in an HTTP cookie.
/// </summary>
///
/// <returns>
/// true if the session is embedded in the URL; otherwise, false.
/// </returns>
///
public bool IsCookieless
{
get { return false; }
}
/// <summary>
/// Gets a value that indicates whether the application is configured for cookieless sessions.
/// </summary>
///
/// <returns>
/// One of the <see cref="T:System.Web.HttpCookieMode"></see> values that indicate whether the application is configured for cookieless sessions. The default is <see cref="F:System.Web.HttpCookieMode.UseCookies"></see>.
/// </returns>
///
public HttpCookieMode CookieMode
{
get { return HttpCookieMode.UseCookies; }
}
/// <summary>
/// Gets or sets the locale identifier (LCID) of the current session.
/// </summary>
///
/// <returns>
/// A <see cref="T:System.Globalization.CultureInfo"></see> instance that specifies the culture of the current session.
/// </returns>
///
public int LCID
{
get { return lcid; }
set { lcid = value; }
}
/// <summary>
/// Gets or sets the code-page identifier for the current session.
/// </summary>
///
/// <returns>
/// The code-page identifier for the current session.
/// </returns>
///
public int CodePage
{
get { return codePage; }
set { codePage = value; }
}
/// <summary>
/// Gets a collection of objects declared by &lt;object Runat="Server" Scope="Session"/&gt; tags within the ASP.NET application file Global.asax.
/// </summary>
///
/// <returns>
/// An <see cref="T:System.Web.HttpStaticObjectsCollection"></see> containing objects declared in the Global.asax file.
/// </returns>
///
public HttpStaticObjectsCollection StaticObjects
{
get { return staticObjects; }
}
/// <summary>
/// Gets or sets a session-state item value by name.
/// </summary>
///
/// <returns>
/// The session-state item value specified in the name parameter.
/// </returns>
///
/// <param name="name">The key name of the session-state item value. </param>
public object this[string name]
{
get { return BaseGet(name); }
set { BaseSet(name, value); }
}
/// <summary>
/// Gets or sets a session-state item value by numerical index.
/// </summary>
///
/// <returns>
/// The session-state item value specified in the index parameter.
/// </returns>
///
/// <param name="index">The numerical index of the session-state item value. </param>
public object this[int index]
{
get { return BaseGet(index); }
set { BaseSet(index, value); }
}
/// <summary>
/// Gets an object that can be used to synchronize access to the collection of session-state values.
/// </summary>
///
/// <returns>
/// An object that can be used to synchronize access to the collection.
/// </returns>
///
public object SyncRoot
{
get { return syncRoot; }
}
/// <summary>
/// Gets a value indicating whether access to the collection of session-state values is synchronized (thread safe).
/// </summary>
/// <returns>
/// true if access to the collection is synchronized (thread safe); otherwise, false.
/// </returns>
///
public bool IsSynchronized
{
get { return true; }
}
/// <summary>
/// Gets a value indicating whether the session is read-only.
/// </summary>
///
/// <returns>
/// true if the session is read-only; otherwise, false.
/// </returns>
///
bool IHttpSessionState.IsReadOnly
{
get
{
return true;
}
}
}
/// <summary>
/// Sets the referrer for the request. Uses a fluent interface.
/// </summary>
/// <param name="referer"></param>
/// <returns></returns>
public HttpSimulator SetReferer(Uri referer)
{
if (this.workerRequest != null)
{
this.workerRequest.SetReferrer(referer);
}
this.referrer = referer;
return this;
}
/// <summary>
/// Sets a form variable.
/// </summary>
/// <param name="name"></param>
/// <param name="value"></param>
/// <returns></returns>
public HttpSimulator SetFormVariable(string name, string value)
{
if (this.workerRequest != null)
{
throw new InvalidOperationException("Cannot set form variables after calling Simulate().");
}
formVars.Add(name, value);
return this;
}
/// <summary>
/// Sets a header value.
/// </summary>
/// <param name="name"></param>
/// <param name="value"></param>
/// <returns></returns>
public HttpSimulator SetHeader(string name, string value)
{
if (this.workerRequest != null)
{
throw new InvalidOperationException("Cannot set headers after calling Simulate().");
}
headers.Add(name, value);
return this;
}
private void ParseRequestUrl(Uri url)
{
if (url == null)
{
return;
}
this.host = url.Host;
this.port = url.Port;
this.localPath = url.LocalPath;
this.page = StripPrecedingSlashes(RightAfter(url.LocalPath, ApplicationPath));
this.physicalPath = Path.Combine(this.physicalApplicationPath, this.page.Replace("/", @"\"));
}
static string RightAfter(string original, string search)
{
if (search.Length > original.Length || search.Length == 0)
{
return original;
}
var searchIndex = original.IndexOf(search, 0, StringComparison.InvariantCultureIgnoreCase);
if (searchIndex < 0)
{
return original;
}
return original.Substring(original.IndexOf(search) + search.Length);
}
public string Host
{
get { return this.host; }
}
private string host;
public string LocalPath
{
get { return this.localPath; }
}
private string localPath;
public int Port
{
get { return this.port; }
}
private int port;
/// <summary>
/// Portion of the URL after the application.
/// </summary>
public string Page
{
get { return this.page; }
}
/// <summary>
/// The same thing as the IIS Virtual directory. It's
/// what gets returned by Request.ApplicationPath.
/// </summary>
public string ApplicationPath
{
get
{
return this.applicationPath;
}
set
{
this.applicationPath = value ?? "/";
this.applicationPath = NormalizeSlashes(this.applicationPath);
}
}
private string applicationPath = "/";
/// <summary>
/// Physical path to the application (used for simulation purposes).
/// </summary>
public string PhysicalApplicationPath
{
get
{
return this.physicalApplicationPath;
}
set
{
this.physicalApplicationPath = value ?? DefaultPhysicalAppPath;
//strip trailing backslashes.
this.physicalApplicationPath = StripTrailingBackSlashes(this.physicalApplicationPath) + @"\";
}
}
private string physicalApplicationPath = DefaultPhysicalAppPath;
/// <summary>
/// Physical path to the requested file (used for simulation purposes).
/// </summary>
public string PhysicalPath
{
get { return this.physicalPath; }
}
private string physicalPath = DefaultPhysicalAppPath;
public TextWriter ResponseWriter
{
get { return this.responseWriter; }
set { this.responseWriter = value; }
}
/// <summary>
/// Returns the text from the response to the simulated request.
/// </summary>
public string ResponseText
{
get
{
return (builder ?? new StringBuilder()).ToString();
}
}
private TextWriter responseWriter;
public SimulatedHttpRequest WorkerRequest
{
get { return this.workerRequest; }
}
private SimulatedHttpRequest workerRequest;
private static string ExtractQueryStringPart(Uri url)
{
string query = url.Query ?? string.Empty;
if (query.StartsWith("?"))
{
return query.Substring(1);
}
return query;
}
void SetHttpRuntimeInternals()
{
//We cheat by using reflection.
// get singleton property value
var runtime = ReflectionHelper.GetStaticFieldValue<HttpRuntime>("_theRuntime", typeof(HttpRuntime));
// set app path property value
ReflectionHelper.SetPrivateInstanceFieldValue("_appDomainAppPath", runtime, PhysicalApplicationPath);
// set app virtual path property value
const string VpathTypeName = "System.Web.VirtualPath, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a";
var virtualPath = ReflectionHelper.Instantiate(VpathTypeName, new Type[] { typeof(string) }, new object[] { ApplicationPath });
ReflectionHelper.SetPrivateInstanceFieldValue("_appDomainAppVPath", runtime, virtualPath);
// set codegen dir property value
ReflectionHelper.SetPrivateInstanceFieldValue("_codegenDir", runtime, PhysicalApplicationPath);
HostingEnvironment environment = GetHostingEnvironment();
ReflectionHelper.SetPrivateInstanceFieldValue("_appPhysicalPath", environment, PhysicalApplicationPath);
ReflectionHelper.SetPrivateInstanceFieldValue("_appVirtualPath", environment, virtualPath);
ReflectionHelper.SetPrivateInstanceFieldValue("_configMapPath", environment, new ConfigMapPath(this));
}
protected static HostingEnvironment GetHostingEnvironment()
{
HostingEnvironment environment;
try
{
environment = new HostingEnvironment();
}
catch (InvalidOperationException)
{
//Shoot, we need to grab it via reflection.
environment = ReflectionHelper.GetStaticFieldValue<HostingEnvironment>("_theHostingEnvironment", typeof(HostingEnvironment));
}
return environment;
}
#region --- Text Manipulation Methods for slashes ---
protected static string NormalizeSlashes(string s)
{
if (String.IsNullOrEmpty(s) || s == "/")
{
return "/";
}
s = s.Replace(@"\", "/");
//Reduce multiple slashes in row to single.
var normalized = Regex.Replace(s, "(/)/+", "$1");
//Strip left.
normalized = StripPrecedingSlashes(normalized);
//Strip right.
normalized = StripTrailingSlashes(normalized);
return "/" + normalized;
}
protected static string StripPrecedingSlashes(string s)
{
return Regex.Replace(s, "^/*(.*)", "$1");
}
protected static string StripTrailingSlashes(string s)
{
return Regex.Replace(s, "(.*)/*$", "$1", RegexOptions.RightToLeft);
}
protected static string StripTrailingBackSlashes(string s)
{
if (String.IsNullOrEmpty(s))
{
return string.Empty;
}
return Regex.Replace(s, @"(.*)\\*$", "$1", RegexOptions.RightToLeft);
}
#endregion
internal class ConfigMapPath : IConfigMapPath
{
private readonly HttpSimulator requestSimulation;
public ConfigMapPath(HttpSimulator simulation)
{
requestSimulation = simulation;
}
public string GetMachineConfigFilename()
{
throw new NotImplementedException();
}
public string GetRootWebConfigFilename()
{
throw new NotImplementedException();
}
public void GetPathConfigFilename(string siteID, string path, out string directory, out string baseName)
{
throw new NotImplementedException();
}
public void GetDefaultSiteNameAndID(out string siteName, out string siteID)
{
throw new NotImplementedException();
}
public void ResolveSiteArgument(string siteArgument, out string siteName, out string siteID)
{
throw new NotImplementedException();
}
public string MapPath(string siteID, string path)
{
string page = StripPrecedingSlashes(RightAfter(path, requestSimulation.ApplicationPath));
return Path.Combine(requestSimulation.PhysicalApplicationPath, page.Replace("/", @"\"));
}
public string GetAppPathForPath(string siteID, string path)
{
return requestSimulation.ApplicationPath;
}
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
/// <filterpriority>2</filterpriority>
public void Dispose()
{
if (HttpContext.Current != null)
{
HttpContext.Current = null;
}
}
}
}
using System;
using System.Collections.Specialized;
using System.Reflection;
using System.Web;
using System.Web.Hosting;
using NUnit.Framework;
namespace Onboard.Tests.ZeroFriction.HttpMocking
{
[TestFixture, Category("Example")]
public class HttpSimulatorTests
{
internal class TestHttpHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
var physicalPath = context.Request.MapPath("/MyHandler.ashx");
var username = context.Request.Form["username"];
var id = context.Request.QueryString["id"];
var referer = context.Request.UrlReferrer.ToString();
//Imagine, if you will, a bunch of complex interesting
//and fascinating logic here.
context.Response.Write(physicalPath + ":" + username + ":" + id + ":" + referer);
}
public bool IsReusable
{
get { return true; }
}
}
[Test]
public void CanGetSetSession()
{
using (new HttpSimulator("/", @"c:\inetpub\").SimulateRequest())
{
HttpContext.Current.Session["Test"] = "Success";
Assert.AreEqual("Success", HttpContext.Current.Session["Test"], "Was not able to retrieve session variable.");
}
}
[Test]
public void CanGetSetApplicationVariables()
{
using (new HttpSimulator("/", @"c:\inetpub\").SimulateRequest())
{
HttpContext.Current.Application["Test"] = "Success";
Assert.AreEqual("Success", HttpContext.Current.Application["Test"], "Was not able to retrieve application variable.");
}
}
[Test]
public void TestHttpHandlerWritesCorrectResponse()
{
using (HttpSimulator simulator = new HttpSimulator("/", @"c:\inetpub\"))
{
simulator.SetFormVariable("username", "phil")
.SetReferer(new Uri("http://example.com/1/"))
.SimulateRequest(new Uri("http://localhost/MyHandler.ashx?id=1234"));
TestHttpHandler handler = new TestHttpHandler();
handler.ProcessRequest(HttpContext.Current);
HttpContext.Current.Response.Flush();
string expected = @"c:\inetpub\MyHandler.ashx:phil:1234:http://example.com/1/";
Assert.AreEqual(expected, simulator.ResponseText, "The Expected Response is all wrong.");
} //HttpContext.Current is set to null again.
}
[Test]
public void CanDispose()
{
using (HttpSimulator simulator = new HttpSimulator())
{
simulator.SimulateRequest();
Assert.IsNotNull(HttpContext.Current);
}
Assert.IsNull(HttpContext.Current);
}
[TestCase("http://localhost/Test/Default.aspx", "/Test", "/Test")]
[TestCase("http://localhost/Test/Default.aspx", "/Test/", "/Test")]
[TestCase("http://localhost/Test/Default.aspx", "//Test//", "/Test")]
[TestCase("http://localhost/Test/Subtest/Default.aspx", "/Test", "/Test")]
[TestCase("http://localhost/Test/Subtest/Default.aspx", "/Test/", "/Test")]
[TestCase("http://localhost/Test/Subtest/Default.aspx", "//Test//", "/Test")]
[TestCase("http://localhost/Test/Default.aspx", "", "/")]
[TestCase("http://localhost/Test/Default.aspx", "/", "/")]
[TestCase("http://localhost/Test/Default.aspx", null, "/")]
public void CanSetApplicationPathCorrectly(string url, string appPath, string expectedAppPath)
{
HttpSimulator simulator = new HttpSimulator(appPath, @"c:\inetpub\wwwroot\site1\test");
Assert.AreEqual(expectedAppPath, simulator.ApplicationPath);
simulator.SimulateRequest(new Uri(url));
Assert.AreEqual(expectedAppPath, HttpContext.Current.Request.ApplicationPath);
Assert.AreEqual(expectedAppPath, HttpRuntime.AppDomainAppVirtualPath);
Assert.AreEqual(expectedAppPath, HostingEnvironment.ApplicationVirtualPath);
}
[TestCase("http://localhost/Test/default.aspx", "/Test", @"c:\projects\test", @"c:\projects\test\", @"c:\projects\test\default.aspx")]
[TestCase("http://localhost/Test/Subtest/default.aspx", "/Test", @"c:\projects\test", @"c:\projects\test\", @"c:\projects\test\Subtest\default.aspx")]
[TestCase("http://localhost/test/default.aspx", "/", @"c:\inetpub\wwwroot\", @"c:\inetpub\wwwroot\", @"c:\inetpub\wwwroot\test\default.aspx")]
[TestCase("http://localhost/test/default.aspx", "/", @"c:\inetpub\wwwroot", @"c:\inetpub\wwwroot\", @"c:\inetpub\wwwroot\test\default.aspx")]
public void CanSetAppPhysicalPathCorrectly(string url, string appPath, string appPhysicalPath, string expectedPhysicalAppPath, string expectedPhysicalPath)
{
HttpSimulator simulator = new HttpSimulator(appPath, appPhysicalPath);
Assert.AreEqual(expectedPhysicalAppPath, simulator.PhysicalApplicationPath);
simulator.SimulateRequest(new Uri(url), HttpVerb.GET);
Assert.AreEqual(expectedPhysicalPath, simulator.PhysicalPath);
Assert.AreEqual(expectedPhysicalAppPath, HttpRuntime.AppDomainAppPath);
Assert.AreEqual(expectedPhysicalAppPath, HostingEnvironment.ApplicationPhysicalPath);
Assert.AreEqual(expectedPhysicalPath, HttpContext.Current.Request.PhysicalPath);
}
[Test]
public void CanGetQueryString()
{
var simulator = new HttpSimulator();
simulator.SimulateRequest(new Uri("http://localhost/Test.aspx?param1=value1&param2=value2&param3=value3"));
for (var i = 1; i <= 3; i++)
{
Assert.AreEqual("value" + i, HttpContext.Current.Request.QueryString["param" + i], "Could not find query string field 'param{0}'", i);
}
simulator.SimulateRequest(new Uri("http://localhost/Test.aspx?param1=new-value1&param2=new-value2&param3=new-value3&param4=new-value4"));
for (var i = 1; i <= 4; i++)
{
Assert.AreEqual("new-value" + i, HttpContext.Current.Request.QueryString["param" + i], "Could not find query string field 'param{0}'", i);
}
simulator.SimulateRequest(new Uri("http://localhost/Test.aspx?"));
Assert.AreEqual(string.Empty, HttpContext.Current.Request.QueryString.ToString());
Assert.AreEqual(0, HttpContext.Current.Request.QueryString.Count);
simulator.SimulateRequest(new Uri("http://localhost/Test.aspx"));
Assert.AreEqual(string.Empty, HttpContext.Current.Request.QueryString.ToString());
Assert.AreEqual(0, HttpContext.Current.Request.QueryString.Count);
simulator.SimulateRequest(new Uri("http://localhost/Test.aspx?param-name"));
Assert.AreEqual("param-name", HttpContext.Current.Request.QueryString.ToString());
Assert.AreEqual(1, HttpContext.Current.Request.QueryString.Count);
Assert.IsNull(HttpContext.Current.Request.QueryString["param-name"]);
}
[Test]
public void CanSimulateFormPost()
{
using (var simulator = new HttpSimulator())
{
var form = new NameValueCollection();
form.Add("Test1", "Value1");
form.Add("Test2", "Value2");
simulator.SimulateRequest(new Uri("http://localhost/Test.aspx"), form);
Assert.AreEqual("Value1", HttpContext.Current.Request.Form["Test1"]);
Assert.AreEqual("Value2", HttpContext.Current.Request.Form["Test2"]);
}
using (var simulator = new HttpSimulator())
{
simulator.SetFormVariable("Test1", "Value1")
.SetFormVariable("Test2", "Value2")
.SimulateRequest(new Uri("http://localhost/Test.aspx"));
Assert.AreEqual("Value1", HttpContext.Current.Request.Form["Test1"]);
Assert.AreEqual("Value2", HttpContext.Current.Request.Form["Test2"]);
}
}
[Test]
public void CanGetResponse()
{
HttpSimulator simulator = new HttpSimulator();
simulator.SimulateRequest();
HttpContext.Current.Response.Write("Hello World!");
HttpContext.Current.Response.Flush();
Assert.AreEqual("Hello World!", simulator.ResponseText);
}
[Test]
public void CanGetReferer()
{
HttpSimulator simulator = new HttpSimulator();
simulator.SetReferer(new Uri("http://example.com/Blah.aspx")).SimulateRequest();
Assert.AreEqual(new Uri("http://example.com/Blah.aspx"), HttpContext.Current.Request.UrlReferrer);
simulator = new HttpSimulator();
simulator.SimulateRequest().SetReferer(new Uri("http://x.example.com/Blah.aspx"));
Assert.AreEqual(new Uri("http://x.example.com/Blah.aspx"), HttpContext.Current.Request.UrlReferrer);
}
[TestCase("http://localhost:60653/Test.aspx", null, null, "localhost", 60653, "/", "/Test.aspx", @"c:\InetPub\wwwRoot\")]
[TestCase("http://localhost:60653/Test.aspx?test=true", null, null, "localhost", 60653, "/", "/Test.aspx", @"c:\InetPub\wwwRoot\")]
[TestCase("http://localhost:60653/Test.aspx", "/", @"c:\InetPub\wwwRoot\", "localhost", 60653, "/", "/Test.aspx", @"c:\InetPub\wwwRoot\")]
[TestCase("http://localhost:60653/Test/Test.aspx", "/", @"c:\InetPub\wwwRoot\", "localhost", 60653, "/", "/Test/Test.aspx", @"c:\InetPub\wwwRoot\")]
[TestCase("http://localhost:60653/AppPath/Test.aspx", "/AppPath", @"c:\InetPub\wwwRoot\AppPath\", "localhost", 60653, "/AppPath", "/AppPath/Test.aspx", @"c:\InetPub\wwwRoot\AppPath\")]
[TestCase("http://localhost:60653/AppPath/Test.aspx", "/AppPath/", @"c:\InetPub\wwwRoot\AppPath\", "localhost", 60653, "/AppPath", "/AppPath/Test.aspx", @"c:\InetPub\wwwRoot\AppPath\")]
public void CanParseRequestUrl(string url, string appPath, string physicalPath, string expectedHost, int expectedPort, string expectedAppPath, string expectedPage, string expectedAppDomainAppPath)
{
HttpSimulator simulator = new HttpSimulator(appPath, physicalPath);
Assert.AreEqual(expectedAppPath, simulator.ApplicationPath);
Assert.AreEqual(expectedAppDomainAppPath, simulator.PhysicalApplicationPath);
}
[TestCase("http://localhost/AppPath/default.aspx", "/AppPath", "/AppPath/default.aspx")]
[TestCase("http://localhost/AppPath/default.aspx", "/", "/AppPath/default.aspx")]
public void CanGetLocalPathCorrectly(string url, string appPath, string expectedLocalPath)
{
HttpSimulator simulator = new HttpSimulator(appPath, @"c:\inetpub\wwwroot\AppPath\");
simulator.SimulateRequest(new Uri(url));
Assert.AreEqual(expectedLocalPath, HttpContext.Current.Request.Path);
Assert.AreEqual(expectedLocalPath, HttpContext.Current.Request.Url.LocalPath);
}
[TestCase("http://localhost:60653/Test.aspx", null, null, "localhost", 60653, "/", "/Test.aspx", @"c:\InetPub\wwwRoot\")]
[TestCase("http://localhost:60653/Test.aspx", "/", @"c:\InetPub\wwwRoot\", "localhost", 60653, "/", "/Test.aspx", @"c:\InetPub\wwwRoot\")]
[TestCase("http://localhost:60653/Test/Test.aspx", "/", @"c:\InetPub\wwwRoot\", "localhost", 60653, "/", "/Test/Test.aspx", @"c:\InetPub\wwwRoot\")]
[TestCase("http://localhost:60653/AppPath/Test.aspx", "/AppPath", @"c:\InetPub\wwwRoot\AppPath\", "localhost", 60653, "/AppPath", "/AppPath/Test.aspx", @"c:\InetPub\wwwRoot\AppPath\")]
[TestCase("http://localhost:60653/AppPath/Test.aspx", "/AppPath/", @"c:\InetPub\wwwRoot\AppPath\", "localhost", 60653, "/AppPath", "/AppPath/Test.aspx", @"c:\InetPub\wwwRoot\AppPath\")]
public void CanSimulateRequest(string url, string appPath, string physicalPath, string expectedHost, int expectedPort, string expectedAppPath, string expectedLocalPath, string expectedPhysicalPath)
{
HttpSimulator simulator = new HttpSimulator(appPath, physicalPath);
simulator.SimulateRequest(new Uri(url));
Assert.AreEqual(expectedHost, HttpContext.Current.Request.Url.Host);
Assert.AreEqual(expectedPort, HttpContext.Current.Request.Url.Port);
Assert.AreEqual(expectedAppPath, HttpContext.Current.Request.ApplicationPath);
Assert.AreEqual(expectedPhysicalPath, HttpContext.Current.Request.PhysicalApplicationPath);
Assert.AreEqual(expectedLocalPath, HttpContext.Current.Request.Url.LocalPath);
}
[TestCase("/", "/", @"c:\inetpub\wwwroot\")]
[TestCase("/Test/Test.aspx", "/", @"c:\inetpub\wwwroot\Test\Test.aspx")]
[TestCase("/Test/Blah/Test.aspx", "/", @"c:\inetpub\wwwroot\Test\Blah\Test.aspx")]
[TestCase("/Test", "/Test", @"c:\inetpub\wwwroot")]
[TestCase("/Test/", "/Test", @"c:\inetpub\wwwroot\")]
public void CanMapPath(string virtualPath, string appPath, string expectedMapPath)
{
var url = new Uri("http://localhost/Test/Test.aspx");
var simulator = new HttpSimulator(appPath, @"c:\inetpub\wwwroot\");
simulator.SimulateRequest(url);
//Create a virtual path object.
var vpath = ReflectionHelper.Instantiate("System.Web.VirtualPath, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", new Type[] { typeof(string) }, virtualPath);
Assert.IsNotNull(vpath);
var environment = HttpSimulatorTester.CallGetEnvironment();
var vpathString = ReflectionHelper.InvokeProperty<string>(vpath, "VirtualPathString");
var appVirtPath = ReflectionHelper.GetPrivateInstanceFieldValue<object>("_appVirtualPath", environment);
Assert.IsNotNull(appVirtPath);
Console.WriteLine("VPATH: " + vpath);
Console.WriteLine("App-VPATH: " + appVirtPath);
Console.WriteLine("vpath.VirtualPathString == '{0}'", vpathString);
var mapping = ReflectionHelper.InvokeNonPublicMethod<string>(typeof(HostingEnvironment), "GetVirtualPathToFileMapping", vpath);
Console.WriteLine("GetVirtualPathToFileMapping: --->{0}<---", (mapping ?? "{NULL}"));
var o = ReflectionHelper.GetPrivateInstanceFieldValue<object>("_configMapPath", environment);
Console.WriteLine("_configMapPath: {0}", o ?? "{null}");
var mappedPath = ReflectionHelper.InvokeNonPublicMethod<string>(environment, "MapPathActual", vpath, false);
Console.WriteLine("MAPPED: " + mappedPath);
Assert.AreEqual(expectedMapPath, HttpContext.Current.Request.MapPath(virtualPath));
}
[Test]
public void CanInstantiateVirtualPath()
{
var virtualPathType = Type.GetType("System.Web.VirtualPath, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", true);
var constructor = virtualPathType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(string) }, null);
Assert.IsNotNull(constructor);
}
[Test]
public void CanGetHostingEnvironment()
{
var environment = HttpSimulatorTester.CallGetEnvironment();
Assert.IsNotNull(environment);
environment = HttpSimulatorTester.CallGetEnvironment();
Assert.IsNotNull(environment);
}
[TestCase("/", "/")]
[TestCase("", "/")]
[TestCase("/test", "/test")]
[TestCase("test/", "/test")]
[TestCase("/test/", "/test")]
[TestCase("/test/////", "/test")]
[TestCase("////test/", "/test")]
[TestCase("/test/test////", "/test/test")]
[TestCase("/////test/test////", "/test/test")]
[TestCase("/////test///test////", "/test/test")]
public void CanNormalizeSlashes(string s, string expected)
{
Assert.AreEqual(expected, HttpSimulatorTester.CallNormalizeSlashes(s));
}
[Test]
public void CanStripTrailing()
{
Assert.AreEqual(@"c:\blah\blah2", HttpSimulatorTester.CallStripTrailingBackSlashes(@"c:\blah\blah2\"));
}
}
internal class HttpSimulatorTester : HttpSimulator
{
public static string CallNormalizeSlashes(string s)
{
return NormalizeSlashes(s);
}
public static string CallStripTrailingBackSlashes(string s)
{
return StripTrailingBackSlashes(s);
}
public static HostingEnvironment CallGetEnvironment()
{
return GetHostingEnvironment();
}
}
}
using System;
using System.Reflection;
namespace Onboard.Tests.ZeroFriction.HttpMocking
{
/// <summary>
/// Helper class to simplify common reflection tasks.
/// </summary>
public sealed class ReflectionHelper
{
private ReflectionHelper()
{
}
/// <summary>
/// Returns the value of the private member specified.
/// </summary>
/// <param name="fieldName">Name of the member.</param>
/// /// <param name="type">Type of the member.</param>
public static T GetStaticFieldValue<T>(string fieldName, Type type)
{
var field = type.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Static);
if (field != null)
{
return (T)field.GetValue(type);
}
return default(T);
}
/// <summary>
/// Returns the value of the private member specified.
/// </summary>
/// <param name="fieldName">Name of the member.</param>
/// <param name="typeName"></param>
public static T GetStaticFieldValue<T>(string fieldName, string typeName)
{
var type = Type.GetType(typeName, true);
var field = type.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Static);
if (field != null)
{
return (T)field.GetValue(type);
}
return default(T);
}
/// <summary>
/// Sets the value of the private static member.
/// </summary>
/// <param name="fieldName"></param>
/// <param name="type"></param>
/// <param name="value"></param>
public static void SetStaticFieldValue<T>(string fieldName, Type type, T value)
{
var field = type.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Static);
if (field == null)
{
throw new ArgumentException(string.Format("Could not find the private instance field '{0}'", fieldName));
}
field.SetValue(null, value);
}
/// <summary>
/// Sets the value of the private static member.
/// </summary>
/// <param name="fieldName"></param>
/// <param name="typeName"></param>
/// <param name="value"></param>
public static void SetStaticFieldValue<T>(string fieldName, string typeName, T value)
{
var type = Type.GetType(typeName, true);
var field = type.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Static);
if (field == null)
{
throw new ArgumentException(string.Format("Could not find the private instance field '{0}'", fieldName));
}
field.SetValue(null, value);
}
/// <summary>
/// Returns the value of the private member specified.
/// </summary>
/// <param name="fieldName">Name of the member.</param>
/// <param name="source">The object that contains the member.</param>
public static T GetPrivateInstanceFieldValue<T>(string fieldName, object source)
{
var field = source.GetType().GetField(fieldName, BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.Instance);
if (field != null)
{
return (T)field.GetValue(source);
}
return default(T);
}
/// <summary>
/// Returns the value of the private member specified.
/// </summary>
/// <param name="memberName">Name of the member.</param>
/// <param name="source">The object that contains the member.</param>
/// <param name="value">The value to set the member to.</param>
public static void SetPrivateInstanceFieldValue(string memberName, object source, object value)
{
var field = source.GetType().GetField(memberName, BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.Instance);
if (field == null)
{
throw new ArgumentException(string.Format("Could not find the private instance field '{0}'", memberName));
}
field.SetValue(source, value);
}
public static object Instantiate(string typeName)
{
return Instantiate(typeName, null, null);
}
public static object Instantiate(string typeName, Type[] constructorArgumentTypes, params object[] constructorParameterValues)
{
return Instantiate(Type.GetType(typeName, true), constructorArgumentTypes, constructorParameterValues);
}
public static object Instantiate(Type type, Type[] constructorArgumentTypes, params object[] constructorParameterValues)
{
var constructor = type.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, constructorArgumentTypes, null);
return constructor.Invoke(constructorParameterValues);
}
/// <summary>
/// Invokes a non-public static method.
/// </summary>
/// <typeparam name="TReturn"></typeparam>
/// <param name="type"></param>
/// <param name="methodName"></param>
/// <param name="parameters"></param>
/// <returns></returns>
public static TReturn InvokeNonPublicMethod<TReturn>(Type type, string methodName, params object[] parameters)
{
var paramTypes = Array.ConvertAll(parameters, new Converter<object, Type>(delegate(object o) { return o.GetType(); }));
var method = type.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Static, null, paramTypes, null);
if (method == null)
{
throw new ArgumentException(string.Format("Could not find a method with the name '{0}'", methodName), "method");
}
return (TReturn)method.Invoke(null, parameters);
}
public static TReturn InvokeNonPublicMethod<TReturn>(object source, string methodName, params object[] parameters)
{
var paramTypes = Array.ConvertAll(parameters, new Converter<object, Type>(delegate(object o) { return o.GetType(); }));
var method = source.GetType().GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance, null, paramTypes, null);
if (method == null)
{
throw new ArgumentException(string.Format("Could not find a method with the name '{0}'", methodName), "method");
}
return (TReturn)method.Invoke(source, parameters);
}
public static TReturn InvokeProperty<TReturn>(object source, string propertyName)
{
var propertyInfo = source.GetType().GetProperty(propertyName);
if (propertyInfo == null)
{
throw new ArgumentException(string.Format("Could not find a propertyName with the name '{0}'", propertyName), "propertyName");
}
return (TReturn)propertyInfo.GetValue(source, null);
}
public static TReturn InvokeNonPublicProperty<TReturn>(object source, string propertyName)
{
var propertyInfo = source.GetType().GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Instance, null, typeof(TReturn), new Type[0], null);
if (propertyInfo == null)
{
throw new ArgumentException(string.Format("Could not find a propertyName with the name '{0}'", propertyName), "propertyName");
}
return (TReturn)propertyInfo.GetValue(source, null);
}
public static object InvokeNonPublicProperty(object source, string propertyName)
{
var propertyInfo = source.GetType().GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Instance);
if (propertyInfo == null)
{
throw new ArgumentException(string.Format("Could not find a propertyName with the name '{0}'", propertyName), "propertyName");
}
return propertyInfo.GetValue(source, null);
}
}
}
#region Disclaimer/Info
///////////////////////////////////////////////////////////////////////////////////////////////////
// Subtext WebLog
//
// Subtext is an open source weblog system that is a fork of the .TEXT
// weblog system.
//
// For updated news and information please visit http://subtextproject.com/
// Subtext is hosted at SourceForge at http://sourceforge.net/projects/subtext
// The development mailing list is at subtext-devs@lists.sourceforge.net
//
// This project is licensed under the BSD license. See the License.txt file for more information.
///////////////////////////////////////////////////////////////////////////////////////////////////
#endregion
using System;
using System.Collections.Specialized;
using System.IO;
using System.Text;
using System.Web.Hosting;
namespace Onboard.Tests.ZeroFriction.HttpMocking
{
/// <summary>
/// Used to simulate an HttpRequest.
/// </summary>
public class SimulatedHttpRequest : SimpleWorkerRequest
{
readonly string host;
readonly string verb;
readonly int port;
readonly string physicalFilePath;
private readonly NameValueCollection headers = new NameValueCollection();
private readonly NameValueCollection formVariables = new NameValueCollection();
Uri referrer;
public SimulatedHttpRequest(string applicationPath, string physicalAppPath, string physicalFilePath, string page, string query, TextWriter output, string host, int port, string verb)
: base(applicationPath, physicalAppPath, page, query, output)
{
if (host == null)
{
throw new ArgumentNullException("host", "Host cannot be null.");
}
if (host.Length == 0)
{
throw new ArgumentException("Host cannot be empty.", "host");
}
if (applicationPath == null)
{
throw new ArgumentNullException("applicationPath", "Can't create a request with a null application path. Try empty string.");
}
this.host = host;
this.verb = verb;
this.port = port;
this.physicalFilePath = physicalFilePath;
}
internal void SetReferrer(Uri referer)
{
this.referrer = referer;
}
/// <summary>
/// Returns the specified member of the request header.
/// </summary>
/// <returns>
/// The HTTP verb returned in the request
/// header.
/// </returns>
public override string GetHttpVerbName()
{
return verb;
}
/// <summary>
/// Gets the name of the server.
/// </summary>
/// <returns></returns>
public override string GetServerName()
{
return host;
}
public override int GetLocalPort()
{
return this.port;
}
/// <summary>
/// Gets the headers.
/// </summary>
/// <value>The headers.</value>
public NameValueCollection Headers
{
get
{
return this.headers;
}
}
/// <summary>
/// Gets the format exception.
/// </summary>
/// <value>The format exception.</value>
public NameValueCollection Form
{
get
{
return formVariables;
}
}
/// <summary>
/// Get all nonstandard HTTP header name-value pairs.
/// </summary>
/// <returns>An array of header name-value pairs.</returns>
public override string[][] GetUnknownRequestHeaders()
{
if (this.headers == null || this.headers.Count == 0)
{
return null;
}
var headersArray = new string[this.headers.Count][];
for (var i = 0; i < this.headers.Count; i++)
{
headersArray[i] = new string[2];
headersArray[i][0] = this.headers.Keys[i];
headersArray[i][1] = this.headers[i];
}
return headersArray;
}
public override string GetKnownRequestHeader(int index)
{
if (index == 0x24)
{
return referrer == null ? string.Empty : referrer.ToString();
}
if (index == 12 && this.verb == "POST")
{
return "application/x-www-form-urlencoded";
}
return base.GetKnownRequestHeader(index);
}
/// <summary>
/// Returns the virtual path to the currently executing
/// server application.
/// </summary>
/// <returns>
/// The virtual path of the current application.
/// </returns>
public override string GetAppPath()
{
var appPath = base.GetAppPath();
return appPath;
}
public override string GetAppPathTranslated()
{
var path = base.GetAppPathTranslated();
return path;
}
public override string GetUriPath()
{
var uriPath = base.GetUriPath();
return uriPath;
}
public override string GetFilePathTranslated()
{
return physicalFilePath;
}
/// <summary>
/// Reads request data from the client (when not preloaded).
/// </summary>
/// <returns>The number of bytes read.</returns>
public override byte[] GetPreloadedEntityBody()
{
var formText = string.Empty;
foreach (string key in this.formVariables.Keys)
{
formText += string.Format("{0}={1}&", key, this.formVariables[key]);
}
return Encoding.UTF8.GetBytes(formText);
}
/// <summary>
/// Returns a value indicating whether all request data
/// is available and no further reads from the client are required.
/// </summary>
/// <returns>
/// <see langword="true"/> if all request data is available; otherwise,
/// <see langword="false"/>.
/// </returns>
public override bool IsEntireEntityBodyIsPreloaded()
{
return true;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment