sbouaked
sbouaked

Reputation: 980

unit test on a static function that read a file

I have the followed function and trying to add Unit Test on a old project. I'm a beginner in Unit Test so forgive me if the question is stupid ...

public static string GetDefaultName(bool isResponsive)
    {
        //Read web.config file
        Configuration configuration = WebConfigurationManager.OpenWebConfiguration(System.Web.HttpContext.Current.Request.ApplicationPath);
        if (!isResponsive)
        {
            if (configuration.AppSettings.Settings.AllKeys.Contains("defaultTheme"))
            {
                return configuration.AppSettings.Settings["defaultTheme"].Value;
            }
            else
                return "default";
        }
        else
        {
            // ...
        }
    }

And I'm trying to write an Unit Test in this way :

 [TestMethod]
    public void ReturnDefaulThemeNametIfThemeIsResponsive()
    {
        var theme = new Theme {isResponsive = true};

        var defaultName = Themes.GetDefaultName(theme.isResponsive);
        Assert.AreEqual(defaultName, "defaultThemeResponsive");
    }

I wonder what is the best way to test this static function, and how to mock the part who read the web.config file ?

Upvotes: 1

Views: 625

Answers (2)

Hintham
Hintham

Reputation: 1086

The way your method is designed at the moment does not allow you to mock the part that reads the config file. If you want to be able to do that you need to make it a parameter to your method. One way to make that easier is to define an interface like

public interface ISetting
{
    string GetConfigItem(string itemName);
}

Then wrap the Configuration object in a settings manager class that implements this.

public class MySettings:ISetting
{
    public string GetConfigItem(string ItemName)
    {
        // return value of the setting. In your case code that gets value of "defaultTheme"
    }
}

Your method will now have a dependency on ISetting.

For testing purposes you can create a mock that implements the interface and will return what ever value you want independent of the current state and content of the web.config

public class SettingsTestHelper:ISetting
{
    private _valueToReturn;
    public SettingsTestHelper(string valueToReturn)
    {
        _valueToReturn=valueToReturn;
    }
    public string GetConfigItem(string itemName)
    {
        return valueToReturn;
    }
}

With this you can now create a unit test(doesn't compile, but you'll get the idea)

[TestMethod]
public void CanGetSetting()
{
    var helper = new SettingsTestHelper("default");
    var result = ClasThatImplementsYourStaticMethod.GetDefaultName(helper, true);
    Assert.AreEqual(expected, actual);
}

Upvotes: 1

Nkosi
Nkosi

Reputation: 247163

I try to stay away from static utilities that have dependencies as they are difficult to unit test. But in this case it is possible. You will have to do some refactoring.

First you need to abstract all calls to access configuration.

public interface IThemeSettings {
    bool Contains(string key);
    string this[string key] { get; }
}

You can then update the static Themes utility class to use this abstraction as a dependency

public static class Themes {
    private static IThemeSettings themes;

    public static void Configure(Func<IThemeSettings> factory) {
        if (factory == null) throw new InvalidOperationException("Must provide a valid factory method");
        themes = factory();
    }

    public static string GetDefaultName(bool isResponsive) {
        if (themes == null) throw new InvalidOperationException("Themes has not been configured.");
        string result = string.Empty;
        if (!isResponsive) {
            if (themes.Contains("defaultTheme")) {
                result = themes["defaultTheme"];
            } else
                result = "default";
        } else {
            // ...
        }
        return result;
    }

    //...
}

That wat you can now configure the utility to use mocks when testing

[TestMethod]
public void ReturnDefaulThemeNametIfThemeIsResponsive() {
    //Arrange
    var key = "defaultTheme";
    var expected = "defaultThemeResponsive";

    var mockSettings = new Mock<IThemeSettings>();
    mockSettings.Setup(m => m.Contains(key)).Returns(true);
    mockSettings.Setup(m => m[key]).Returns(expected);

    //In production you would also do something like this with
    //the actual production implementation, not a mock
    Themes.Configure(() => mockSettings.Object);

    var theme = new Theme { isResponsive = true };

    //Act
    var defaultName = Themes.GetDefaultName(theme.isResponsive);

    //Assert
    Assert.AreEqual(expected, defaultName);
}

In this case I used Moq as the mocking framework.

Some advice. Try not to have your classes tightly coupled to HttpContext. Your classes should depend on abstractions and not on concretions.

Upvotes: 1

Related Questions