How to mock application path when unit testing Web App

后端 未结 6 1280
囚心锁ツ
囚心锁ツ 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 13:13

    Trying to make parts of ASP.NET happy with various types of tests seems, to me, to be quite fragile. And I am inclined to believe that the mocking route only works if you basically avoid using ASP.NET or MVC and, instead, write your own webserver from scratch.

    Instead, just use ApplicationHost.CreateApplicationHost to create a properly-initialized AppDomain. Then run your test code from within that domain using AppDomain.DoCallback.

    using System;
    using System.Web.Hosting;
    
    public class AppDomainUnveiler : MarshalByRefObject
    {
        public AppDomain GetAppDomain()
        {
            return AppDomain.CurrentDomain;
        }
    }
    
    public class Program
    {
        public static void Main(string[] args)
        {
            var appDomain = ((AppDomainUnveiler)ApplicationHost.CreateApplicationHost(
                typeof(AppDomainUnveiler),
                "/",
                Path.GetFullPath("../Path/To/WebAppRoot"))).GetAppDomain();
            try
            {
                appDomain.DoCallback(TestHarness);
            }
            finally
            {
                AppDomain.Unload(appDomain);
            }
        }
    
        static void TestHarness()
        {
            //…
        }
    }
    

    Note: when trying this myself, my test runner code was in a separate assembly from the WebAppRoot/bin directory. This is an issue because, when HostApplication.CreateApplicationHost creates a new AppDomain, it sets its base directory to something like your WebAppRoot directory. Therefore, you must define AppDomainUnveiler in an assembly that is discoverable in the WebAppRoot/bin directory (so it must be in your webapp’s codebase and cannot be stored separately in a testing assembly, unfortunately). I suggest that if you want to be able to keep your test code in a separate assembly, you subscribe to AppDomain.AssemblyResolve in AppDomainUnveiler’s constructor. Once your testing assembly gets the AppDomain object, it can use AppDomain.SetData to pass along information about where to load the testing assembly. Then your AssemblyResolve subscriber can use AppDomain.GetData to discover where to load the test assembly from. (I’m not sure, but the sort of objects you can SetData/GetData might be quite limited—I’ve just used strings myself to be safe). This is a bit annoying, but I think it is the best way to separate concerns in this situation.

提交回复
热议问题