Struggling for Competence

Unit Test Logging on a Custom MsBuild Task

One of the problems areas for testing a custom MsBuild tasks is logging. In a custom task you create a log message with code like "Log.LogMessage("hullo")" or "Log.LogError("It's gone wrong")". The "Log" in this code is in fact the TaskLoggingHelper, which gets created behind the scenes by task engine. The TaskLoggingHelper has no interface, which on the face of it means you can't test code which does logging without creating a task engine and testing the whole custom task end-to-end.

That was what I though at least until this week, when I had a cunning idea. Make a logging interface myself anyway, which my classes can use and I can mock during unit testing. Then make the custom task implement the logging interface, using it's (untestable) logging functionality.

So here's an example, first the ILog interface which just defines a LogMessage method, which is the only logging I want to do at the moment:


using Microsoft.Build.Utilities;
using Moq;
using NUnit.Framework;
namespace MsBuildLogDemo
{
    public interface ILog
    {
        void LogMessage(string message);
    }

Next the class, ApplyChanges, which I want to unit test. The ILog interface is passed to ApplyChanges by constrcutor injection:


public class ApplyChanges
{
    public ILog Log { get; set; }

    public ApplyChanges(ILog log)
    {
        Log = log;
    }

    public void Apply()
    {
        Log.LogMessage("Apply Called");
    }
}

Next a genuine unit test, which uses the mocking framework Moq, to test that the call to LogMessage is made:


    [TestFixture]
    public class ApplyChangesTest
    {
        [Test]
        public void ApplyTest()
        {
            var logMock = new Mock<ILog>();
            var applyChanges = new ApplyChanges(logMock.Object);

            applyChanges.Apply();

            logMock.Verify(l => l.LogMessage("Apply Called"), Times.Once());
        }
    }

Finally the custom task, which implements the ILog interface using it's own logging functionality. Note that when the custom task creates the ApplyChanges class, it passes "this" into the constructor, to give Apply changes "real" logging functionality:


    public class MyTask : Task, ILog
    {
        public override bool Execute()
        {
            new ApplyChanges(this).Apply();
            return true;
        }

        public void LogMessage(string message)
        {
            Log.LogMessage(message);
        }
    }

So that's unit testing with logging in a custom MsBuild task. I was quite pleased with the idea, though I'm sure it's not original. I can't however find any references for other folks doing the same thing, kinda hard to know what to search for.