Log4net helper class for unit tests The class helps to determine the exact output log message, taking in account the conversionPattern that log4net uses for layout. Current usage is you have to attach the listener, then call your BL or a wrapper method which your code uses to write to log, then call the GetRenderedMessage() method. The purpose is to make sure that even if I modify my wrapper method (I have a wrapper method which adds some properties to log4net's ThreadContext.Properties and also formats the message in a certain way) the rendered log would remain the same. Are there any improvements or suggestions to make it more "friendly" so someone would know how to use it without reading this intro?

using log4net;
using log4net.Appender;
using log4net.Config;
using System;
using System.IO;
using System.Linq;

namespace TestUtils
{
    public class Log4netListener
    {
        MemoryAppender memAppender;

        /// &lt;summary&gt;
        /// Attaches the listener so it would capture any log messages written.
        /// &lt;/summary&gt;
        /// &lt;param name="appenderName"&gt;&lt;/param&gt;
        public void AttachLogListener(string appenderName)
        {
            var appender = LogManager.GetRepository().GetAppenders().Where(a =&gt; a.Name == appenderName);
            var patternLayout = ((log4net.Layout.PatternLayout)((AppenderSkeleton)(appender.First())).Layout);
            memAppender = new MemoryAppender();
            memAppender.Layout = patternLayout;
            BasicConfigurator.Configure(memAppender);
        }

        /// &lt;summary&gt;
        /// Gets the message written to log. Use this method only after AttachLogListener() was called
        /// and a message was written to log4net afterwards.
        /// &lt;/summary&gt;       
        public string GetRenderedMessage()
        {
            if (memAppender == null)
            {
                throw new InvalidOperationException("The listener was not attached. Make sure you call AttachLogListener() before writing to log.");
            }
            var events = memAppender.GetEvents();
            string logMessage = "";
            using (StringWriter writer = new StringWriter())
            {
                foreach (var ev in events)
                {
                    memAppender.Layout.Format(writer, ev);
                }
                logMessage = writer.ToString();
            }
            return logMessage;
        }
    }
}


Usage:

[TestClass]
public class LoggingTests
{
    [AssemblyInitialize]
    public static void Configure(TestContext tc)
    {
        XmlConfigurator.Configure(); // must use this line to make log4net work from test  if "configSource" is used in app.config
    }

    [TestMethod]
    public void LogLayout()
    {
        var listener = new Log4netListener();
        listener.AttachLogListener("MyLogFileAppender");

        // Call business logic here that writes to log...

        string logMessage = listener.GetRenderedMessage();
        // Assert...
    }
}
Are there any improvements or suggestions to make it more "friendly" so someone would know how to use it without reading this intro?


The most obvious thing is high-quality XML docs. You've already started, but you need to fill in the param name where it's blank, and the return value for the method where it's missing.

On top of that, adding a summary tag to the class itself containing most of the verbiage above would be helpful.

Reading a little further into your implementation - is it possible to move the code of the AttachLogListener method into a constructor? That would guarantee your order of execution/initialization and make usage more sane and obvious, I think.