简体   繁体   中英

.NET: how can I unit test this method?

(Similar to .NET how to fudge the HttpContext object !)

I have a method that I want to test:

    public void MyMethod()
    {
        if (HttpContext.Current.Application["myValue"] != null)
            Console.WriteLine("Boo!");
    }

How can I write a test WITHOUT refactoring the method so that the Console.WriteLine is hit?

I tried with:

        TextWriter tw = new StringWriter();
        HttpWorkerRequest wr = new SimpleWorkerRequest("/webapp", @"path...", "logon.asp", "", tw);
        HttpContext.Current = new HttpContext(wr);
        HttpContext.Current.Application.Add("KeyValue", "myValue");
        MyMethod();

but HttpContext.Current.Application.count is always zero, I cannot add values to it!

The thing is you should refactor this method so that it's easier to test! To properly test this you want to mock HttpContext (here's one example as to how: How to use Rhino Mocks to Mock an HttpContext.Application ).

If you're specifically prohibited from refactoring the method and are also required to test it, you can use a detouring system, such as Moles or TypeMock Isolator , to completely detour the static and HttpContext calls. This can get a bit ugly, however and it's much preferable to perform refactoring on the original code.


Sequence of detoured mocks would be:

  1. Detour HttpContext.Current to return a HttpContext detour stub (your own implementation that complies with the HttpContext class interface, but looks exactly like HttpContext to the tested code.)

  2. In your HttpContext detour stub, mock Application["myValue"] to return your desired value (either null or non-null in this case.) This will probably require creating a detour class for the return value of Application.

  3. Now you can configure the detours and run your method. The calls to HttpContext.Current and the HttpContext methods will be rerouted through the Profiling API to your detour stubs.

You can use HttpSimulator class from www.koders.com - Subtext.TestLibrary

Just invoking

new Subtext.TestLibrary.HttpSimulator().SimulateRequest();

sets your HttpContext properly, so you can also add values to Application dictionary.

As Jeff said, it sets HttpApplicationFactory._theApplicationFactory._state using Reflection as below, but you don't have to bother about it, actually.

Type appFactoryType = Type.GetType("System.Web.HttpApplicationFactory, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
object appFactory = ReflectionHelper.GetStaticFieldValue<object>("_theApplicationFactory", appFactoryType);
ReflectionHelper.SetPrivateInstanceFieldValue("_state", appFactory, HttpContext.Current.Application);

HttpContext.Application is supplied by a private singleton that you can't access normally.

You may be able to set this via reflection, but I personally wouldn't even bother.

Instead you should isolate your testable code from global state like HttpContext.Current : just pass the value you're looking for into the method you want to test.

You can see the problem clearly looking at the code in Reflector (or in the .NET source if you've downloaded that): the HttpContext.Application get accessor returns a new HttpApplicationState instance every time you call it unless something (like the ASP.NET framework) sets HttpApplicationFactory._theApplicationFactory._state .

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