amura.cxg
amura.cxg

Reputation: 2427

Unit testing with HttpContext.Application

I'm trying to unit test some code that reads a value from HttpContext.Current.Application but it keeps failing because it can't retrieve the value.

I've tried creating my own HttpContext, setting HttpContext.Current to it, then writing values to it but it does not seem to store the new values.

Code Referencing the HttpContext.Current.Application

public static void UpdateApplicationVariable(string keyToUpdate, object toSave)
{
    HttpContext.Current.Application.Lock();
    HttpContext.Current.Application[keyToUpdate] = toSave;
    HttpContext.Current.Application.UnLock();
}

public static object GetApplicationVariable(string keyToReturn)
{
    return HttpContext.Current.Application[keyToReturn];
}

Setup Code

HttpContext.Current = new HttpContext(
    new HttpRequest(null, "http://tempuri.org", null),
    new HttpResponse(null)
);

UpdateApplicationVariable("GeneralSettings", new GeneralSettings()
{
    NumberDecimalPlaces = 2
});

//settings is null
GeneralSettings settings = GetApplicationVariable("GeneralSettings") as GeneralSettings;

Upvotes: 0

Views: 413

Answers (1)

Nkosi
Nkosi

Reputation: 247018

Don't mock HttpContext!!!! He doesn't like to be mocked!

Instead figure out what is the functionality you would like to achieve and design an abstraction around that. This will allow for your code to be more testable as it is not so tightly coupled to HttpContext.

public interface IApplicationSettings {
    object this[string key] { get; set; }
}

And in your Code referencing the HttpContext.Current.Application can be changed...

public static class SomeUtilityClass {        

    public static IApplicationSettings Application {get;set;}

    public static void UpdateApplicationVariable(string keyToUpdate, object toSave)
    {
        Application[keyToUpdate] = toSave;
    }

    public static object GetApplicationVariable(string keyToReturn)
    {
        return Application[keyToReturn];
    }    
}

The concrete version would have the actual code you need to access HttpContext. Something like...

public class ConcreteApplicationSettings : IApplicationSettings {
    public string this[string keyToReturn] {
        get { 
            return HttpContext.Current.Application[keyToReturn]; 
        }
        set {
            HttpContext.Current.Application.Lock();
            HttpContext.Current.Application[keyToUpdate] = value;
            HttpContext.Current.Application.UnLock();
        }
    }    
}

Now in your Code setup for testing you can forego the HttpContext completely by using a mocked,faked or stubbed version of your abstraction...

[TestClass]
public class ApplicationVariablesTests {

    public class FakeApplicationSettings : IApplicationSettings {
        Dictionary<string,object> Application = new Dictionary<string,object>();
        public string this[string keyToReturn] {
            get { return Application[keyToReturn]; }
            set { Application[keyToUpdate] = value; }
        }    
    }

    [TestMethod]
    public void Should_Update_General_Settings() {
        //Arrange
        SomeUtilityClass.Application = new FakeApplicationSettings();
        SomeUtilityClass.UpdateApplicationVariable("GeneralSettings", new GeneralSettings()
        {
            NumberDecimalPlaces = 2
        });
        //Act
        GeneralSettings settings = SomeUtilityClass.GetApplicationVariable("GeneralSettings") as GeneralSettings;
        //Assert
        Assert.IsNotNull(settings);
        Assert.AreEqual(2, settings.NumberDecimalPlaces);
    }
}

In production code you will use the actual version instead of a fake. This also allows you to swap in and out different versions in the fusture if you decide to use something else to store your variables.

Upvotes: 1

Related Questions