简体   繁体   中英

Unit Testing Application_Start

I am looking for any kind of information (prefer Moq) on how to unit test the Application_Start method in Global.asax. I am using ASP.NET MVC and trying to get to that elusive 100% code coverage!

The fact that I'm using MVC is not the point. And saying that not testing Start is not necessary isn't really the answer either. What if I had other code in there? I need to know how to test it.

Some organizations do require those meaningless numbers, and have issues beyond cost. For companies dealing with $ensitive information "Good enough" is not good enough. I had exactly the same issue and like Klas Mellbourn, need to get to 100% (if not higher!) The following worked for me. Although, I'd have preferred to mark it "Exclude from code coverage"

public class Global : HttpApplication
{
    public override void Init()
    {
        AreaRegistration.RegisterAllAreas(); //will error out on app_start
        base.Init();
    }

    /// <summary>
    /// Application_Start method.
    /// </summary>
    /// <param name="sender">The caller</param>
    /// <param name="e">The event arguments</param>
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "KMM: This method is called dynamically by the framework.")]
    protected void Application_Start(object sender, EventArgs e)
    {
        var container = StructureMapRegistry.Initialize();
        GlobalConfiguration.Configuration.DependencyResolver = new StructureMapResolver(container);
        GlobalConfiguration.Configure(WebApiConfig.Register);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
    }
}

Then the unit test looked like this:

 public class GlobalTest : Global
    {
        private HttpRequestMessage FakeRequest;

        DateTime? effectiveDate = DateTime.Now.AddYears(-4);
        private string policyNumber = "1234567890";

        [TestMethod]
        public void ApplicationStart()
        {
            var sender = new object();
            var e = new EventArgs();
            try
            {
                Application_Start(sender, e); // this will error b/c not fully loaded yet.
            }
            catch (InvalidOperationException)
            {
                Thread.Sleep(2000); // give the app time to launch

                Application_Start(sender, e);
            }
            Assert.IsTrue(true);
        }
    }

and finally, I needed to set a flag in my WebApiConfig to prevent the routes from being registered twice.

 public static class WebApiConfig
    {
        private static bool isRegistered;
        /// <summary>
        /// Registers the configuration.
        /// </summary>
        /// <param name="config">The Http Configuration.</param>
        public static void Register(HttpConfiguration config)
        {
            if (isRegistered)
            {
                return;
            }
            config.MapHttpAttributeRoutes();

Now, before the haters and purists start marking this down, the assignment is to test all code. I personally detest modifying code to suit tests. That's not the same thing as making code testable. Adding the isRegistered flag is an example of the sort of artifact that is necessary to support the test needing to call the app_start 2x. It's a small thing and since that code only gets called on app_start, I'm not going to fuss over it too much. I would certainly be interested in what others have done in this regard.

In a typical ASP.NET MVC application the Application_Start event is often used to register custom routes. Here's a nice post explaining how to unit test your custom routes.

I've found the best way to unit test stuff in Global.asax is to make sure the whole thing is in a testable static method.

Then pass in to that method everything it needs from Global.asax.

So for example, if you were doing a check of Session on Application Start, you could have a method like this:

public static void CheckSession(HttpSessionStateBase session)
{
...
}

Then, your Application Start would just be this:

protected void Application_Start(object sender, EventArgs e)
{
    CheckSession(new HttpSessionStateWrapper(Session));
} 

This example is obviously a little silly as you'd probably do something like this in Session Start :) You could pass into that method whatever it needs, Request, Response, Cache, etc.

However, it gets the point across. The only code that wouldn't be covered would be the actual single line call in Application_Start. Everything else could be covered in a test using Moq like so:

var session = new Moq<HttpSessionStateBase>();
...Set Expectations...
Global.CheckSession(session.Object);
...Do Asserts...

You wouldn't achieve the 100% code coverage you're looking for, but you'd be darned close, and you would be fulfilling the "spirit" of TDD law if not exactly the letter :)

this function is called the first time your site is visited. recycling the app pool will cause it to be triggered again

In most situations, this event handler has no code , so don't waste time pursuing meaningless numbers!

If you want to test the actions of an event-based item like this, separate the actions into a separate, testable method and just call it from the handler. Or, as darin suggested, test the effects of the method - in this particular case, that your routing table got registered correctly.

BTW, I've never reached 100% coverage on a non-trivial app... We generally target 80% as "good enough". Some modules are 100% testable, but many aren't practical to do so.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM