How to mock application path when unit testing Web App

后端 未结 6 1392
囚心锁ツ
囚心锁ツ 2021-01-04 12:35

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         


        
6条回答
  •  暗喜
    暗喜 (楼主)
    2021-01-04 12:56

    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

提交回复
热议问题