Monday, February 16, 2009

Configuring NUnit tests to work with log4net

The problem

I would like my NUnit tests to produce log4net logs.

Setting (my context)

I have all my unit tests in separate assembly and I use NUnit GUI to execute the tests.

The Solution

Existing solutions

Use assembly attribute to declare log4net configuration
Load log4net configuration for each namespace using NUnit's SetUpFixture attribute

Solution I use
  1. Create app.config file containing log4net configuration.
  2. Create custom TestCase class which loads log4net configuration before running tests.
  3. Inherit each test class from above TestCase class.
Potential problems
  • wrong config file can be used (other than "our" app.config with log4net configuration),
  • log4net configuration can be not loaded before the tests,
  • wrong log4net configuration can be loaded.
Tests for above problems
  1. Test for config file

    First add "special" key to appSettings in app.config. Value from this key will be used to check if expected config file was loaded.


    <appSettings>
    <add key="config.type" value="unit.test"/>
    </appSettings>
    Second - add test which will check for existence of this "special" key-value.

    [Test]
    public void AppConfigForTestIsUsed()
    {
    Assert.AreEqual("unit.test", ConfigurationManager.AppSettings["config.type"]);
    }
    REMARK: Please use ConfigurationManager instead of ConfigurationSettings, the latter is deprecated.

  2. Test for log4net config file

    Check if at least one of defined appenders was loaded.


    [Test]
    public void Log4NetConfigurationLoaded()
    {
    IAppender[] appenders = LogManager.GetRepository().GetAppenders();
    ICollection appenderNames = appenders.Select(appender => appender.Name).ToArray();
    Assert.Contains("NUnitFileAppender", appenderNames);
    }
Implementation
  1. app.config file

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
    <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
    </configSections>
    <appSettings>
    <add key="config.type" value="unit.test"/>
    </appSettings>
    <log4net>
    <appender name="NUnitFileAppender" type="log4net.Appender.FileAppender">
    <file value="log4net.log" />
    <appendToFile value="false" />
    <layout type="log4net.Layout.PatternLayout">
    <conversionPattern value="%date %thread %-5level %logger{1} - %message%newline" />
    </layout>
    </appender>
    <appender name="NUnitConsoleAppender" type="log4net.Appender.ConsoleAppender">
    <layout type="log4net.Layout.PatternLayout">
    <conversionPattern value="%date %thread %-5level %logger{1} - %message%newline" />
    </layout>
    </appender>
    <root>
    <level value="DEBUG" />
    <appender-ref ref="NUnitFileAppender" />
    <appender-ref ref="NUnitConsoleAppender" />
    </root>
    </log4net>
    </configuration>
  2. Custom TestCase class

    public class TestCaseWithLog4NetSupport : Mockery
    {
    [TestFixtureSetUp]
    public void ConfigureLog4Net()
    {
    XmlConfigurator.Configure();
    }
    }
    Above solution causes that log4net configuration is loaded before each TestFixture class.

    REMARK: Becuase I want to use NMock2 my TestCase class extends Mockery class.

  3. Actual test

    [TestFixture]
    public class BusinessLogicTest : TestCaseWithLog4NetSupport
    {

That's it :-)