views:

3807

answers:

10

When you guys are unit testing an application that relies on values from an app.config file? How do you test that those values are read in correctly and how your program reacts to incorrect values entered into a config file?

It would be ridiculous to have to modify the config file for the NUnit app, but I can't read in the values from the app.config I want to test.

Edit: I think I should clarify perhaps. I'm not worried about the ConfigurationManager failing to read the values, but I am concerned with testing how my program reacts to the values read in.

A: 

Actually, thinking on it further, I suppose what I should do is create a ConfigFileReader class for use in my project and then fake it out in the unit test harness?

Is that the usual thing to do?

Dana
+2  A: 

you can read the app.config file with the ConfigurationManager class; you can even write to it

Steven A. Lowe
Ahhh, so I can set values in the ConfigurationManager collection? I always assumed it was read-only. I guess that's what I get for making assumptions :P
Dana
+1  A: 

You can always wrap the reading-in bit in an interface, and have a specific implementation read from the config file. You would then write tests using Mock Objects to see how the program handled bad values. Personally, I wouldn't test this specific implementation, as this is .NET Framework code (and I'm assuming - hopefully - the MS has already tested it).

Yuval
+11  A: 

I usually isolate external dependencies like reading a config file in their own facade-class with very little functionality. In tests I can create a mock version of this class that implements and use that instead of the real config file. You can create your own mockup's or use a framework like moq or rhino mocks for this.

That way you can easily try out your code with different configuration values without writing complex tests that first write xml-configuration files. The code that reads the configuration is usually so simple that it needs very little testing.

Mendelt
A: 

The simplest option is to wrap the methods that read configuration such that you can substitute in values during testing. Create an interface that you use for reading config and have an implementation of that interface get passed in as a constructor parameter or set on the object as a property (as you would using dependency injection/inversion of control). In the production environment, pass in an implementation that really reads from configuration; in the test environment, pass in a test implementation that returns a known value.

If you don't have the option of refactoring the code for testability yet still need to test it, Typemock Isolator provides the ability to actually mock the .NET framework configuration classes so you can just say "next time I ask for such-and-such appSettings value, return this known value."

Travis Illig
A: 

I had the same issue,

you can use Nunit-console.exe c:\path1\testdll1.dll c:\path2\testdll2.dll

this works fine even though if both dlls point to different app.configs ex testdll1.dll.config and testdll2.dll.config

if you want to use Nunit project config and wrap these two dlls then there is no way you can have two configs

you have to have project1.config if your Nunit project is project1.nunit in the same location as Project1.nunit sits.

hope this helps

sundar venugopal
+5  A: 

You can modify your config section at runtime in your test setup. E.g:

// setup
System.Configuration.Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
config.Sections.Add("sectionname", new ConfigSectionType());
ConfigSectionType section = (ConfigSectionType)config.GetSection("sectionname");
section.SomeProperty = "value_you_want_to_test_with";
config.Save(ConfigurationSaveMode.Modified);
ConfigurationManager.RefreshSection("sectionname");

// carry out test ...

You can of course setup your own helper methods to do this more elegantly.

Hope it helps!

/Dan

+2  A: 

I was facing similar problems with web.config.... I find an interesting solution. You can capsulate configuration reading function, eg. something like this:

public class MyClass {

public static Func<string, string> GetConfigValue = s => ConfigurationManager.AppSettings[s];

//...

}

And then normally use

string connectionString = MyClass.GetConfigValue("myConfigValue");

but in unit test initialize "override" the function like this:

MyClass.GetConfigValue = s =>  s == "myConfigValue" ? "Hi", "string.Empty";

More about it:

http://rogeralsing.com/2009/05/07/the-simplest-form-of-configurable-dependency-injection/

Tuomas Hietanen
A: 

A more elegant solution is to use plain old dependency injection on the configuration settings themselves. IMHO this is cleaner than having to mock a configuration reading class/wrapper etc.

For example, say a class "Weather" requires a "ServiceUrl" in order to function (e.g. say it calls a web service to get the weather). Rather than having some line of code that actively goes to a configuration file to get that setting (whether that code be in the Weather class or a separate configuration reader that could be mocked as per some of the other responses), the Weather class can allow the setting to be injected, either via a parameter to the constructor, or possibly via a property setter. That way, the unit tests are extremely simple and direct, and don't even require mocking.

The value of the setting can then be injected using an Inversion of Control (or Dependency Injection) container, so the consumers of the Weather class don't need to explicitly supply the value from somewhere, as it's handled by the container.

Claytona
A: 

That worked for me: public static void BasicSetup() {

     ConnectionStringSettings connectionStringSettings = new ConnectionStringSettings();
     connectionStringSettings.Name = "testmasterconnection";
     connectionStringSettings.ConnectionString = "server=localhost;user=some;database=some;port=3306;";
     ConfigurationManager.ConnectionStrings.Clear();
     ConfigurationManager.ConnectionStrings.Add(connectionStringSettings);                  

  }