我有一个我用于记录的静态类:
public static class myLogger
{
public static ErrorLogging(string input)
{
//dostuff
}
}
我使用它的方式是:
public class myClassthatDoesStuff
{
...
myLogger.ErrorLogging("some error ocurred");
...
}
如何对 myLogger 类进行moq以便对其进行单元测试并确保执行 ErrorLogging 方法? 是否可以在构造函数中没有设置任何参数(构造函数注入)的情况下执行此操作? myClassthatDoesStuff 要求构造函数中没有参数。
答案 0 :(得分:6)
This blog post描述了完全相同的情况 - 您有一个旧的静态日志记录方法,并希望在可测试的代码中使用它。
无论您在哪里依赖静态类,都要依赖于接口。例如,如果类DoesSomething
需要静态类中的函数,请执行以下操作:
public interface ILogger
{
void ErrorLogging(string input);
}
public class MyClassthatDoesStuff
{
private readonly ILogger _logger;
public MyClassthatDoesStuff(ILogger logger)
{
_logger = logger;
}
}
这给你带来两个好处:
您可以对将使用该静态类的代码进行单元测试(通过删除对该静态类的直接依赖性。)您可以将ILogger
替换为模拟类,例如将错误消息添加到一个清单。
class StringLogger : List<string>, ILogger
{
public void ErrorLogging(string input)
{
Add(input);
}
}
var testSubject = new MyClassthatDoesStuff(new StringLogger());
答案 1 :(得分:4)
如果您无法将其从静态类更改为非静态类,请使用非静态类包装它...
void Test()
{
string testString = "Added log";
var logStore = new List<string>();
ILogger logger = new MyTestableLogger(logStore);
logger.ErrorLogging(testString);
Assert.That(logStore.Any(log => log==testString));
}
public interface ILogger
{
void ErrorLogging(string input);
}
public class MyTestableLogger : ILogger
{
public MyTestableLogger(ICollection<string> logStore)
{
this.logStore = logStore;
}
private ICollection<string> logStore;
public void ErrorLogging(string input)
{
logStore.Add(input);
MyLogger.ErrorLogging(input);
}
}
public static class MyLogger
{
public static void ErrorLogging(string input)
{
// Persist input string somewhere
}
}
答案 2 :(得分:1)
您可以使用Microsoft的Shims
来完成此操作假设您的项目被称为ConsoleApplication1
首先转到单元测试项目引用,右键单击包含myClassthatDoesStuff
类的程序集,然后选择“Add Fakes Assembly”。
使用填充程序进行单元测试将如下所示:
[TestClass()]
public class MyClassthatDoesStuffTests
{
[TestMethod()]
public void ImportansStuffTest()
{
using (ShimsContext.Create())
{
bool logCalled = false;
ConsoleApplication1.Fakes.ShimmyLogger.ErrorLoggingString =
(message) => logCalled = true;
new myClassthatDoesStuff().ImportansStuff();
Assert.IsTrue(logCalled);
}
}
}
答案 3 :(得分:1)
您可以使用Typemock Isolator完成此操作。
它允许您避免所有这些数量的包装器和接口,并且这样做很简单:
[TestMethod]
public void TestLogging()
{
//Arrange
Isolate.WhenCalled(() => myLogger.ErrorLogging("")).CallOriginal();
//Act
var foo = new myClassthatDoesStuff();
foo.DoStuff();
//Assert
Isolate.Verify.WasCalledWithAnyArguments(() => myLogger.ErrorLogging(""));
}
答案 4 :(得分:0)
一种选择是将静态类包装在一个实例中(非静态类)。另一个是依赖,不是依赖于静态方法本身,而是依赖于委托。运行时实现是静态方法,但为了测试,您可以注入匿名或本地方法。
首先,声明一个与您的静态方法具有相同签名的委托:
public delegate void LogErrorMethod(string errorMessage);
然后修改依赖静态方法的类注入委托,改为依赖。这看起来就像注入一个接口或类:
public class MyClassthatDoesStuff
{
private readonly LogErrorMethod _logErrorMethod;
public MyClassthatDoesStuff(LogErrorMethod logErrorMethod)
{
_logErrorMethod = logErrorMethod;
}
public void DoSomethingAndLogError()
{
// do something
_logErrorMethod("An error has occurred");
}
}
如果您使用的是像 IServiceCollection
/IServiceProvider
这样的 IoC 容器,那么您可以告诉它使用静态方法作为委托的实现。
services.AddSingleton<LogErrorMethod>(MyLogger.ErrorLogging);
但是为了测试,您可以提供匿名方法。如果你想要一个什么都不做的日志记录方法,你可以这样做:
LogErrorMethod fakeLoggingMethod = message => { };
或者,如果您想捕获消息以便验证它,您可以这样做:
string loggedMessage = null;
LogErrorMethod fakeLoggingMethod = message => { loggedMessage = message; };