views:

1064

answers:

3

If an assembly contains an app.config file, ConfigurationManager will load it as long as it is in the same directory as the NUnit project that is executing through NUnit-Gui. To illustrate consider the following folder structure.

+ TestFolder
    testProject.nunit
  + AssemblyAFolder
      assemblyA.dll
      assemblyA.dll.config
  + AssemblyBFolder
      assemblyB.dll
      assemblyB.dll.config

Both AssemblyA and AssemblyB exercise code that calls into ConfigurationManager. If I run these test assemblies independently in NUnit-Gui, ConfigurationManager will correctly resolve the local configuration files.

However, if I load testProject.nunit into NUnit-Gui (which contains references to both AssemblyA and AssemblyB), ConfigurationManager looks for the configuration file in TestFolder regardless of which assembly is currently executing.

Is there a way to direct NUnit to reload the application configuration to the one present in the current assembly's directory?

Here are the contents of testProject.nunit:

<NUnitProject>
  <Settings activeconfig="Debug" />
  <Config name="Debug" binpathtype="Auto">
    <assembly path="AssemblyAFolder\assemblyA.dll" />
    <assembly path="AssemblyBFolder\assemblyB.dll" />
  </Config>
</NUnitProject>
+2  A: 

This blog post gives an explanation of why the config files load the way they do. Basically NUnit lets the framework handle the config files and doesn't do any of the management.

http://nunit.com/blogs/?p=9

You can also use the 'testProject.config' file that would be loaded in your case to reference the config files for each of the assemblies. Using the appSettings file attribute to add keys:

http://msdn.microsoft.com/en-us/library/ms228154.aspx

Or use the configSource element attribute to uses the section in one of the assemblies config files:

http://msdn.microsoft.com/en-us/library/system.configuration.sectioninformation.configsource(VS.85).aspx

Hope this helps.

MarcLawrence
+2  A: 

The configSource element solution given by MarkLawrence is what I was looking for, and works nicely. The challenge in implementing this solution however, is to make the assembly configuration load when running tests from both an explicit NUnit project (as in my case), AND when running the unit tests for an assembly in isolation (no explicit project). To accomplish this, the following modifications to my file layout were required.

+ TestFolder
    testProject.nunit
    testProject.config
  + AssemblyAFolder
      assemblyA.dll
      assemblyA.dll.config
      assemblyA.dll.configfragment
  + AssemblyBFolder
      assemblyB.dll
      assemblyB.dll.config
      assemblyB.dll.configfragment

The configfragment files are created to contain the assembly configuration that was once in the corresponding config files. Afterwards, the config files are modified to contain only a configSource element with a relative path to the corresponding configfragment file. Note that the only time that this approach doesn't work is when assemblyA.dll and assemblyB.dll both require the same configuration section, as a conflict will arise when creating testproject.config.

After experimenting with this approach I decided to rely on generating the assembly configuration at runtime, and remove the static configuration files all together. Here is some code that demonstrates how to generate the configuration, and make it accessible regardless of how the assembly-under-test is loaded.

void WithConfigurationFile(Action method)
{
    // Create the assembly configuration.
    string settingsSection = "myConfigSectionName";
    Configuration config =
        ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
    config.Sections.Add(
        settingsSection,
        new ConfigSectionType(/*config element values*/);
    config.Save();

    try
    {
        // Invoke the method with the new configuration.
        ConfigurationManager.RefreshSection(settingsSection);
        method();
    }
    finally
    {
        // Revert the assembly configuration.
        File.Delete(config.FilePath);
        ConfigurationManager.RefreshSection(settingsSection);
    }
}

By using the ConfigurationManager.OpenExeConfiguration() overload that does not accept a path, we load the configuration from the host application's working directory, which changes depending on how you run your NUnit tests. Also, the try/finally block guarantees that your assembly configuration will not interfere with other tests, which may or may not require such configuration.

Finally, to use within a unit test:

[Test]
void VerifyFunctionality
{
    WithConfigurationFile(delegate
    {
        // implement unit test and assertions here
    });
}

I hope this helps others who may have encountered similar issues!

Steve Guidi
A: 
Daniel