Ratna ( http://ratnazone.com ) uses the "HttpServerUtility.MapPath" code to map virtual paths to the physical file path. This particular code has worked perfectly for the product. In our last iteration, we replaced HttpServerUtility.MapPath with HttpRequest.MapPath.
Under the hoods, HttpServerUtility.MapPath and HttpRequest.MapPath the same code and will lead to the same display. Both of these methods are problematic when it comes to unit testing.
Find "server.mappath null reference" in your favorite search engine. You will receive more than 10,000 hits. Almost all of these hits are because the test code calls HttpContext.Current and HttpServerUtility.MapPath. When ASP.NET code runs without HTTP, HttpContext.Current will be null.
This problem (HttpContext.Current is null) can be solved very easily by creating an HttpWorkerRequest and initializing HttpContext.Current with that. Here is the code for this -
string appPhysicalDir = @"c:\inetpub\wwwroot"; string appVirtualDir = "/"; SimpleWorkerRequest request = new SimpleWorkerRequest(appVirtualDir, appPhysicalDir, "/", null, new StringWriter()); HttpContext.Current = new HttpContext(request);
With this simple code in unit test, HttpContext.Current is initialized. Infact, if you notice, HttpContext.Current.Server (HttpServerUtility) will also be indexed. However, at that moment, the code is trying to use Server.MapPath, the next 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, this is getting the same exception. If Request.MapPath is used in the code, the problem can be easily resolved in the unit test. 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 track card. This is not enough because HttpRequest does not have a HostingEnvironment Set (which allows MapPath). Unfortunately, creating a HostingEnvironment is not trivial. Thus, for the unit test "mock host", which only provides MapPath functionality, is created. Again, this MockHost handles a lot of internal code. Here's the pseudo code for the layout node. The full 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 HostingEnvironment with the created one }
With the above code, when MapPath is called on HttpRequest, it should be able to resolve the path.
As a last step in unit test add the following code -
MockHost host = new MockHost(@"c:\inetpub\wwwroot\", "/"); host.Setup();
Since HostingEnvironment is now initialized, test paths can resolve virtual paths when the HttpContext.Current.Request.MapPath method is called (along with HostingEnvironment.MapPath and HttpServerUtility.MapPath).
Download the MockHost code here: http://pastebin.com/ar05Ze7p