I am testing code in a MVC HTML helper that throws an error when trying to get the application path:
//appropriate code that uses System.IO.Path to get direc
I'm including a solution from a blog post, which is no longer available (http://blog.jardalu.com/2013/4/23/httprequest_mappath_vs_httpserverutility_mappath)
Complete code: http://pastebin.com/ar05Ze7p
Ratna (http://ratnazone.com) code uses "HttpServerUtility.MapPath" for mapping virtual paths to physical file path. This particular code has worked very well for the product. In our latest iteration, we are replacing HttpServerUtility.MapPath with HttpRequest.MapPath.
Under the hoods, HttpServerUtility.MapPath and HttpRequest.MapPath are the same code and will result in the same mapping. Both of these methods are problematic when it comes to unit testing.
Search for "server.mappath null reference" in your favourite search engine. You are going to get over 10,000 hits. Almost all of these hits are because test code calls HttpContext.Current and HttpServerUtility.MapPath. When the ASP.NET code is executed without HTTP, HttpContext.Current will be null.
This issue (HttpContext.Current is null) can be solved very easily by creating a HttpWorkerRequest and intializing HttpContext.Current with that. Here is the code to do that -
string appPhysicalDir = @"c:\inetpub\wwwroot"; string appVirtualDir = "/"; SimpleWorkerRequest request = new SimpleWorkerRequest(appVirtualDir, appPhysicalDir, "/", null, new StringWriter()); HttpContext.Current = new HttpContext(request);With that simple code in unit test, HttpContext.Current is initialized. Infact, if you notice, HttpContext.Current.Server (HttpServerUtility) will also be intiailzed. However, the moment, the code tries to use Server.MapPath, the following exception will get thrown.
System.ArgumentNullException occurred HResult=-2147467261 Message=Value cannot be null. Parameter name: path Source=mscorlib ParamName=path StackTrace: at System.IO.Path.CheckInvalidPathChars(String path, Boolean checkAdditional) InnerException: HttpContext.Current = context;Infact, if the code uses HttpContext.Current.Request.MapPath, it is going to get the same exception. If the code uses Request.MapPath, the issue can be resolved in the unit test easily. The following code in unit test shows how.
string appPhysicalDir = @"c:\inetpub\wwwroot"; string appVirtualDir = "/"; SimpleWorkerRequest request = new SimpleWorkerRequest(appVirtualDir, appPhysicalDir, "/", null, new StringWriter()); FieldInfo fInfo = request.GetType().GetField("_hasRuntimeInfo", BindingFlags.Instance | BindingFlags.NonPublic); fInfo.SetValue(request, true); HttpContext.Current = new HttpContext(request);In the above code, the request worker will be able to resolve the map path. This is not enough though, because HttpRequest does not have the HostingEnvironment set (which resolves MapPath). Unfortunately, creating a HostingEnvironment is not trivial. So for unit-test, a "mock host" that just provides the MapPath functionality is created. Again, this MockHost hacks lot of internal code. Here is the pseudo-code for the mock host. Complete code can be downloaded here: http://pastebin.com/ar05Ze7p
public MockHost(physicalDirectory, virtualDirectory){ ... } public void Setup() { Create new HostingEnvironment Set Call Context , mapping all sub directories as virtual directory Initialize HttpRuntime's HostingEnvironment with the created one }With the above code when MapPath is called on HttpRequest by it should be able to resolve the path.
As a last step, in the unit test, add the following code -
MockHost host = new MockHost(@"c:\inetpub\wwwroot\", "/"); host.Setup();Since now a HostingEnvironment has been initialized, the test code will be able to resolve virtual paths when HttpContext.Current.Request.MapPath method is called (along with HostingEnvironment.MapPath and HttpServerUtility.MapPath).
Download MockHost code here: http://pastebin.com/ar05Ze7p