The other day I was working on a new implementation in our product to re do logging. I’m taking us from a custom File Writer to using Log4Net wrapped in a Facade.
To make this transition a bit smoother, and allow us to roll back to the old style if something breaks, I also implemented a Factory Pattern to provide the correct logger based upon the current App.Config settings.
To clarify, we are using Ninject for DI, and usually I would use the DI container to inject the correct implementation. However, we are also using the NinjectModule interface to setup bindings at runtime, based upon a compiled assembly. So instead, I’m using DI to inject the factory and it can provide the correct implementation.
I’m sure there will be countless opinions both ways here, but its convenient and makes sense in our project
I had sketched up my interfaces and got ready to write unit tests, when I discovered that I wasnt sure how to get NUnit to read from my App.Config, so I jumped on DuckDuckGo and ended up finding this article on CoryFoy.com
This definitely game me the direction I needed, but all of a sudden I had a revelation. I’ve been wanting to learn to use Moq and I thought this was a great opportunity to try it out.
So here is the interface for my LogFactory
public interface ILogFactory
{
ILog GetLogger();
ILog GetLogger(string name);
string GetServiceName { get; }
string GetLoggerType { get; }
}
And its impl.
public class LogFactory : ILogFactory
{
public virtual ILog GetLogger()
{
if (GetLoggerType != null && GetLoggerType.Equals("Log4Net", StringComparison.InvariantCultureIgnoreCase))
{
return new Log4NetLogger(GetServiceName);
}
else
{
return new LogHelper(GetServiceName);
}
}
public virtual ILog GetLogger(string name)
{
if (GetLoggerType != null && GetLoggerType.Equals("Log4Net", StringComparison.InvariantCultureIgnoreCase))
{
return new Log4NetLogger(name);
}
else
{
return new LogHelper(name);
}
}
public virtual string GetServiceName
{
get
{
return ConfigurationManager.AppSettings["ServiceName"] ?? "Service";
}
}
public virtual string GetLoggerType
{
get
{
return ConfigurationManager.AppSettings["Logger"] ?? "LogHelper";
}
}
}
As you can see we are currently dipping into the App.Config to grab the settings and return the correct logger.
To test the implementation I needed to override the methods (ok they are properties but still):
GetLoggerType ()
//and
GetServiceName()
To accomplish this I setup my unit tests (I am using NUnit) and used Moq to override the properties:
var mock = new Mock<LogFactory>();
mock.Setup(f => f.GetServiceName).Returns("My Test");
mock.Setup(f => f.GetLoggerType).Returns("Log4Net");
mock.Setup(f => f.GetLogger()).CallBase();
LogFactoryLog4Net = mock.Object;
Then in the test you just call the GetLogger() method and Moq takes care of the rest for you:
[Test()]
public void LogFactoryLog4NetGetLogger()
{
var factory = LogFactoryLog4Net.GetLogger();
var type = factory.GetType();
Assert.AreEqual("Log4NetLogger", type.Name);
}
Here is the full unit test for reference:
[TestFixture()]
public class LogFactoryTests
{
public ILogFactory LogFactoryLog4Net { get; set; }
public ILogFactory LogFactoryLogHelper { get; set; }
public ILogFactory LogFactoryDefault { get; set; }
[SetUp]
public void Init()
{
var mock = new Mock<LogFactory>();
mock.Setup(f => f.GetServiceName).Returns("Service Test");
mock.Setup(f => f.GetLoggerType).Returns("Log4Net");
mock.Setup(f => f.GetLogger()).CallBase();
LogFactoryLog4Net = mock.Object;
var mock2 = new Mock<LogFactory>();
mock2.Setup(f => f.GetServiceName).Returns("Service Test");
mock2.Setup(f => f.GetLoggerType).Returns("LogHelper");
mock2.Setup(f => f.GetLogger()).CallBase();
LogFactoryLogHelper = mock2.Object;
var mock3 = new Mock<LogFactory>();
mock3.Setup(f => f.GetServiceName).Returns("Service Test");
mock3.Setup(f => f.GetLoggerType).Returns("INVALID");
mock3.Setup(f => f.GetLogger()).CallBase();
LogFactoryDefault = mock3.Object;
}
[Test()]
public void LogFactoryLog4NetGetLogger()
{
var factory = LogFactoryLog4Net.GetLogger();
var type = factory.GetType();
Assert.AreEqual("Log4NetLogger", type.Name);
}
[Test()]
public void LogFactoryLogHelperGetLogger()
{
var factory = LogFactoryLogHelper.GetLogger();
var type = factory.GetType();
Assert.AreEqual("LogHelper", type.Name);
}
[Test()]
public void LogFactoryDefaultGetLogger()
{
var factory = LogFactoryDefault.GetLogger();
var type = factory.GetType();
Assert.AreEqual("LogHelper", type.Name);
}
}
Hope that helps someone, and thanks for reading!