Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit tests logged (or run) multiple times

I have this simple test:

protected readonly ILog logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().ReflectedType);
private static int count = 0;
[Test]
public void TestConfiguredSuccessfully()
{
    logger.Debug("in test method" + count++);
}

log4net is set up like this:

[TestFixtureSetUp]
public void SetUp()
{
    log4net.Config.BasicConfigurator.Configure();
}

The problem is, that if I run this test in nUnit once, I get the output (as expected):

1742 [TestRunnerThread] DEBUG Tests.TestSomthing (null) - in test method0

But if I press RUN in nUnit.exe again (or more) I get the following:

1742 [TestRunnerThread] DEBUG Tests.TestSomthing (null) - in test method1
1742 [TestRunnerThread] DEBUG Tests.TestSomthing (null) - in test method1

And so on (if I run it 5 times, I'll get 5 repeating lines). Now, if I run the same test alone from reSharper the output is fine and does not repeat. However, if I run this test along side 2 other tests in the same class, the output is repeated three times.

I am totally confused. What the hell is going on here?

like image 582
Egor Pavlikhin Avatar asked Jun 15 '10 05:06

Egor Pavlikhin


1 Answers

Log4net is being re-initialized on each test run, and appender(s) added each time. I suspect that ReSharper does not exhibit the behaviour as it starts a new process each time (the ReSharper test runner) whereas the NUnit GUI does not.

I have had various flavours of this in the past, but for quite a while now I have used a "SetupFixture" to initialize log4net (amongst other things).

[SetUpFixture]
public class UnitTestSuiteSetupTeardown
{
    [SetUp]
    public void Setup()
    {
        log4net.Config.BasicConfigurator.Configure();
    }

    [TearDown]
    public void Teardown()
    {
        //Teardown stuff...
    }
}

Add one of these per test assembly, and ensure the class has no namespace. It will be run once for all of your tests i.e. all tests in the assembly. I personally have one of these at solution level and then add it in as a link to each test project.

Update

The example above follows the question and sets up basic configuration. In my actual SetUpFixture I initialize log4net from a log4net configuration file (which I again store at solution level and then add as a link to all test projects), e.g.

[SetUpFixture]
public class UnitTestSuiteSetupTeardown
{
    [SetUp]
    public void Setup()
    {
        LogFactory.Configure();
    }

    [TearDown]
    public void Teardown()
    {
        //Teardown stuff...
    }
}

And a sample LogFactory type class.

public static class LogFactory
{
    public const string DefaultConfigFileName = "log4net.config";

    static ILog GetLogger(Type callingType)
    {
        return new Log4NetLogger(LogManager.GetLogger(callingType));
    }

    public static void Configure()
    {
        Type type = typeof(LogFactory);
        FileInfo assemblyDirectory = AssemblyInfo.GetCodeBaseDirectory(type);
        FileInfo configFile = new FileInfo(Path.Combine(assemblyDirectory.FullName,
            DefaultConfigFileName));
        XmlConfigurator.ConfigureAndWatch(configFile);
        log4net.ILog log = LogManager.GetLogger(type);
        log.ToString();
    }
}
like image 106
Tim Lloyd Avatar answered Nov 15 '22 20:11

Tim Lloyd