最近我开始使用Moq进行单元测试。 我使用Moq来模拟不需要测试的类。
你通常如何处理静态方法?
public void foo(string filePath)
{
File f = StaticClass.GetFile(filePath);
}
这个静态方法 StaticClass.GetFile()
怎么被模拟?
附言:如果您有关于Moq和单元测试的阅读材料推荐,我会很感激。
最近我开始使用Moq进行单元测试。 我使用Moq来模拟不需要测试的类。
你通常如何处理静态方法?
public void foo(string filePath)
{
File f = StaticClass.GetFile(filePath);
}
这个静态方法 StaticClass.GetFile()
怎么被模拟?
附言:如果您有关于Moq和单元测试的阅读材料推荐,我会很感激。
@Pure.Krome: 回答得很好,但我会补充一些细节。
@Kevin:你必须根据你可以对代码进行的更改来选择解决方案。
如果你可以更改它,某些依赖注入可以使代码更易于测试。如果你不能更改它,你需要一个良好的隔离。
借助免费的模拟框架(Moq、RhinoMocks、NMock...),你只能模拟委托、接口和虚方法。因此,对于静态、密封和非虚方法,你有三种解决方案:
我推荐使用 Moles,因为它是免费的、高效的,并像 Moq 一样使用 lambda 表达式。只有一个重要的细节:Moles 提供的是 Stub 而不是 Mock。因此,你仍然可以使用 Moq 来处理接口和委托;)
Mock:一个实现接口并允许动态设置返回值/异常并提供检查特定方法是否被调用/未被调用的能力的类。
Stub:像 Mock 类一样,但它不提供验证方法是否已调用/未调用的能力。
在.NET中有一种可能性,可以排除MOQ和任何其他的mocking库。你需要在包含你想要mock的静态方法的程序集上,在解决方案资源管理器上右键单击,然后选择添加Fakes程序集。接下来,您可以自由地mock这个程序集的静态方法。
假设您想要mock System.DateTime.Now
静态方法。可以像这样进行:
using (ShimsContext.Create())
{
System.Fakes.ShimDateTime.NowGet = () => new DateTime(1837, 1, 1);
Assert.AreEqual(DateTime.Now.Year, 1837);
}
每个静态属性和方法都具有类似的属性。
您可以使用从nuget中获取的Pose库来实现此目标。它允许您模拟静态方法等内容。在测试方法中编写以下内容:
Shim shim = Shim.Replace(() => StaticClass.GetFile(Is.A<string>()))
.With((string name) => /*Here return your mocked value for test*/);
var sut = new Service();
PoseContext.Isolate(() =>
result = sut.foo("filename") /*Here the foo will take your mocked implementation of GetFile*/, shim);
更多阅读请参考此处:https://medium.com/@tonerdo/unit-testing-datetime-now-in-c-without-using-interfaces-978d372478e8
public static class LegacyStaticClass
{
// A static constructor sets up all the delegates so production keeps working as usual
static LegacyStaticClass()
{
ResetDelegates();
}
public static void ResetDelegates()
{
// All the logic that used to be in the body of the static method goes into the delegates instead.
ThrowMeDelegate = input => throw input;
SumDelegate = (a, b) => a + b;
}
public static Action<Exception> ThrowMeDelegate;
public static Func<int, int, int> SumDelegate;
public static void ThrowMe<TException>() where TException : Exception, new()
=> ThrowMeDelegate(new TException());
public static int Sum(int a, int b)
=> SumDelegate(a, b);
}
public class Class1Tests : IDisposable
{
[Fact]
public void ThrowMe_NoMocking_Throws()
{
Should.Throw<Exception>(() => LegacyStaticClass.ThrowMe<Exception>());
}
[Fact]
public void ThrowMe_EmptyMocking_DoesNotThrow()
{
LegacyStaticClass.ThrowMeDelegate = input => { };
LegacyStaticClass.ThrowMe<Exception>();
true.ShouldBeTrue();
}
[Fact]
public void Sum_NoMocking_AddsValues()
{
LegacyStaticClass.Sum(5, 6).ShouldBe(11);
}
[Fact]
public void Sum_MockingReturnValue_ReturnsMockedValue()
{
LegacyStaticClass.SumDelegate = (a, b) => 6;
LegacyStaticClass.Sum(5, 6).ShouldBe(6);
}
public void Dispose()
{
LegacyStaticClass.ResetDelegates();
}
}
使用Microsoft Fakes:
添加模拟程序集,然后如果您有这个静态方法...
//code under test
public static class MyClass {
public static int MyMethod() {
...
}
}
…
// unit test code
using (ShimsContext.Create())
{
ShimMyClass.MyMethod = () => 5;
}
using YT.IIGen.Attributes;
[IIFor(typeof(StaticClass), "StaticClassWrapper")]
internal partial interface IStaticClass
{
}