我有以下logger logger类,并且我想知道最好的对它进行单元测试的内容。
一些观察:
我可以通过使用MemoryString实现IFileWrapper来对FileWrapper.WriteLog方法进行单元测试,但是如果我想测试该方法内部的预期行为(例如:抛出异常,错误的路径和文件名等),
/// <summary>
/// Creates an instance of type <see cref="FileLogger"/>
/// </summary>
/// <remarks>Implements the Singleton Pattern</remarks>
private FileLogger()
{
FileName = string.Format("\\{0: MMM dd, yy}.log", DateTime.Now);
Path = Environment.CurrentDirectory;
FileWrapper = ContainerBuilderFactory.Container.Resolve<IFileWrapper>();
}
/// <summary>
/// Log the <paramref name="Message"/> in the <paramref name="Path"/> specified.
/// The <paramref name="UserName"/>, <paramref name="Host"/> must be supplied
/// </summary>
/// <example>
/// <code>
/// var handler = new LoggerHandlerFactory();
/// var logger = handler.GetHandler<FileLogger>();
/// logger.Log("Hello CSharpLogger");
/// </code>
/// </example>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
/// <exception cref="NotSupportedException"></exception>
/// <exception cref="FileNotFoundException"></exception>
/// <exception cref="IOException"></exception>
/// <exception cref="SecurityException"></exception>
/// <exception cref="DirectoryNotFoundException"></exception>
/// <exception cref="UnauthorizedAccessException"></exception>
/// <exception cref="PathTooLongException"></exception>
/// <exception cref="ArgumentOutOfRangeException"></exception>
/// <exception cref="FormatException"></exception>
public void Log(string message, LogLevel level = LogLevel.INFO)
{
lock (_current)
{
var configLevel = CSharpLoggerConfiguration.Configuration.GetLogLevel();
if (configLevel != LogLevel.OFF & level != LogLevel.OFF && configLevel >= level)
{
try
{
FileWrapper.WriteLog(string.Concat(Path, FileName), message, level);
}
catch (CSharpLoggerException)
{
throw;
}
}
}
}
因此,我使用Moq创建了以下UnitTesting:
//arrange
CSharpLoggerConfiguration.Configuration.SetLogLevel(LogLevel.DEBUG);
var mock = new Mock<IFileWrapper>();
mock.Setup(x => x.WriteLog(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<LogLevel>()));
logger.FileWrapper = mock.Object;
//act
logger.Log("Hello CSharpLogger", LogLevel.DEBUG);
logger.Log("Hello CSharpLogger", LogLevel.WARN);
//assert
mock.Verify(x => x.WriteLog(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<LogLevel>()), Times.Exactly(2));
到现在为止还挺好。我不满意的是这一行:logger.FileWrapper = mock.Object; 我想保留FileWrapper的私有属性。
任何建议都欢迎。
如果您需要更多详细信息,我将发布代码http://csharplogger.codeplex.com/。
使用构造函数注入。简而言之; 而不是通过设置属性来提供服务(在本例中为文件包装器),而是使记录器具有一个带IFileWrapper
参数的公共构造函数。
public class Logger
{
public Logger(IFileWrapper fileWrapper)
{
FileWrapper = fileWrapper;
}
public IFileWrapper FileWrapper { get; }
}
// in your test:
var logger = new Logger(mock.Object);
为了更彻底地回答有关单例文件包装的问题,这是应用程序(非测试)代码的代码示例:
public static class FileWrapperFactory
{
private static IFileWrapper _fileWrapper;
public static IFileWrapper GetInstance()
{
return _fileWrapper ?? (_fileWrapper = CreateInstance());
}
private static IFileWrapper CreateInstance()
{
// do all the necessary setup here
return new FileWrapper();
}
}
public class StuffDoer
{
public void DoStuff()
{
var logger = new FileLogger(FileWrapperFactory.GetInstance());
logger.WriteLog("Starting to do stuff...");
// do stuff
logger.WriteLog("Stuff was done.");
}
}
由于FileWrapperFactory
维护文件包装器的静态实例,因此您永远不会拥有一个以上的实例。但是,您可以像这样创建多个记录器,而不必在意。如果将来您决定可以有许多文件包装器,则记录器代码不必更改。
建议您在实际应用中选择某种DI框架来为您处理所有记账工作。大多数都对单例实例提供了出色的支持,基本上可以完成FileWrapperFactory
上述操作(但是通常以更复杂,更健壮的方式进行。FileWrapperFactory
例如,不是线程安全的...)。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句