2

my unit test works, but after I added some code to get values from Custom Config section in web.config file, unit test stops working. following is my code, those line marked with "//new" are the new code I added, and they break the test.

public partial class AccountController : Controller
{
    private readonly string _twitterConsumerKey;
    private readonly string _twitterConsumerSecret;
    private readonly string _twitterAccessToken;
    private readonly string _twitterAccessTokenSecret;

    private readonly IUserService _userService;

    public AccountController(IUserService userService)
    {
        if (userService == null)
            throw new ArgumentNullException("userService");


        _userService = userService;

        _twitterConsumerKey = TwitterSettings.Settings.ConsumerKey;  //new code
        _twitterConsumerSecret = TwitterSettings.Settings.ConsumerSecret;  //new code
        _twitterAccessToken = TwitterSettings.Settings.AccessToken;  //new code
        _twitterAccessTokenSecret = TwitterSettings.Settings.AccessTokenSecret;  //new code

    }





public class TwitterSettings : ConfigurationSection
{

    private static TwitterSettings settings = ConfigurationManager.GetSection("Twitter") as TwitterSettings;
    public static TwitterSettings Settings { get { return settings; } }

    [ConfigurationProperty("ConsumerKey", IsRequired = true)]
    public string ConsumerKey
    {
        get { return (string)this["ConsumerKey"]; }
        set { this["ConsumerKey"] = value; }
    }

    [ConfigurationProperty("ConsumerSecret", IsRequired = true)]
    public string ConsumerSecret
    {
        get { return (string)this["ConsumerSecret"]; }
        set { this["ConsumerSecret"] = value; }
    }


    [ConfigurationProperty("AccessToken", IsRequired = true)]
    public string AccessToken
    {
        get { return (string)this["AccessToken"]; }
        set { this["AccessToken"] = value; }
    }

    [ConfigurationProperty("AccessTokenSecret", IsRequired = true)]
    public string AccessTokenSecret
    {
        get { return (string)this["AccessTokenSecret"]; }
        set { this["AccessTokenSecret"] = value; }
    }


}

when I call this controller in my unit test. I got following error message: "Failed: System.NullReferenceException: Object reference not set to an instance of an object". Do I need to create a ISetting interface???

  [Test]
    public void Login_Action_Get_Returns_Login_View()
    {
        // Arrange

        var expectedViewName = "~/Views/Account/Login.cshtml";

       // it breaks here.
        _accountController = new AccountController(_userService.Object, _mappingService.Object, _authenticationService.Object);

        // Act

        var result = _accountController.Login() as ViewResult;

        // Assert

        Assert.AreEqual(expectedViewName, result.ViewName, "View name should be {0}", expectedViewName);
    }
Sergey Berezovskiy
  • 232,247
  • 41
  • 429
  • 459
qinking126
  • 11,385
  • 25
  • 74
  • 124

2 Answers2

7

Nice practice is testing units in isolation. That also means abstracting your environment - database, file system (including configuration files), etc. So, extract TwitterSettings interface from your TwitterSettings class:

public interface ITwitterSettings
{
    string ConsumerKey { get; set; }
    string ConsumerSecret { get; set; }
    string AccessToken { get; set; }
    string AccessTokenSecret { get; set; }
}

Implement this interface by your settings:

public class TwitterSettings : ConfigurationSection, ITwitterSettings

And inject this dependency to controller:

public partial class AccountController : Controller
{
    private readonly IUserService _userService;
    private readonly ITwitterSettings _twitterSettings;

    public AccountController(IUserService userService, 
                             ITwitterSettings twitterSettings)
    {
        if (userService == null)
            throw new ArgumentNullException("userService");    

        _userService = userService;
        _twitterSettings = twitterSettings;
    }   
}

BTW abstracting of environment makes tests run faster, and your tests will fail only if your SUT logic is broken, not if database server not responding, or web.config is not found.

Sergey Berezovskiy
  • 232,247
  • 41
  • 429
  • 459
  • Thanks for answering all my unit test questions. now after this one, I think i am pretty comfortable using it. – qinking126 Jul 23 '12 at 16:05
2

A unit test project will not use the web.config file of the MVC project, because it's running it's own application context.

Create an App.Config file in the root of your test project and add your custom config section to it. Run your controller tests now to see if it works. This isn't a proper unit test, but it will prove that your custom code does work.

You'll have to write additional helper methods if you want to test the validity of the actual web.config file in place.

This link may help

Community
  • 1
  • 1
cygnim
  • 1,985
  • 2
  • 16
  • 22
  • i think lazyberezovsky's solutions is better. what you suggested will work, but I ended up maintaining 2 copies of config file. but thanks for your help. – qinking126 Jul 23 '12 at 16:08