模拟静态方法

Kev*_*ith 66 c# unit-testing moq mocking

最近,我开始使用Moq进行单元测试.我使用Moq来模拟我不需要测试的类.

你通常如何处理静态方法?

public void foo(string filePath)
{
    File f = StaticClass.GetFile(filePath);
}
Run Code Online (Sandbox Code Playgroud)

怎么可能这个静态方法StaticClass.GetFile()被嘲笑?

PS我很欣赏你推荐的Moq和单元测试的任何阅读材料.

Jec*_*eco 43

@ Pure.Krome:反响很好,但我会补充一些细节

@Kevin:您必须根据可以为代码带来的更改来选择解决方案.
如果你可以改变它,一些依赖注入使代码更容易测试.如果你做不到,你需要一个良好的隔离.
使用免费的模拟框架(Moq,RhinoMocks,NMock ......),您只能模拟委托,接口和虚拟方法.因此,对于静态,密封和非虚拟方法,您有3种解决方案:

  • TypeMock隔离器(可以模拟一切,但它很昂贵)
  • Telerik的JustMock(新来的,更便宜,但仍然没有免费)
  • 微软的摩尔(唯一免费的隔离解决方案)

我推荐Moles,因为它是免费的,高效的并使用像Moq这样的lambda表达式.只有一个重要细节:Moles提供存根,而不是模拟.所以你仍然可以使用Moq作为接口和代理;)

Mock:一个实现接口的类,允许动态设置值以返回/异常从特定方法抛出,并提供检查是否已调用/未调用特定方法的功能.
存根:类似于模拟类,但它不提供验证方法是否已被调用/未调用的能力.

  • 作为对此的更新.Moles现在称为Fakes,内置于Visual Studio 2012中:http://msdn.microsoft.com/en-us/library/hh549175.aspx (8认同)
  • 您对模拟和存根的定义比必要的更复杂.模拟是一个伪造的对象,你断言某些行为已经发生了.存根是一个虚假对象,仅用于向测试提供"预制"数据.存根永远不会被断言.简而言之.嘲笑是关于行为和存根是关于状态. (3认同)
  • Microsoft.Fakes 不是“免费”的,因为它在 VS 社区版中不可用。 (3认同)
  • 在Moles中,您可以验证是否已调用方法只是添加布尔变量并在存根函数内设置为true也可以使用此workarround验证调用中的所有参数. (2认同)

Pur*_*ome 36

像Moq或Rhinomocks这样的模拟框架只能创建对象的模拟实例,这意味着无法模拟静态方法.

您还可以在Google上搜索更多信息.

此外,还有以前问StackOverflow上的一些问题在这里,这里这里.


pt1*_*lol 16

在.NET中有可能排除MOQ和任何其他模拟库.您必须右键单击包含要模拟的静态方法的程序集上的解决方案资源管理器,然后选择Add Fakes Assembly.接下来,您可以自由地模拟该程序集的静态方法.

假设您要模拟System.DateTime.Now静态方法.以这种方式执行此操作:

using (ShimsContext.Create())
{
    System.Fakes.ShimDateTime.NowGet = () => new DateTime(1837, 1, 1);
    Assert.AreEqual(DateTime.Now.Year, 1837);
}
Run Code Online (Sandbox Code Playgroud)

每个静态属性和方法都有类似的属性.

  • 警告人们发现这一点:MS Fakes仅在Visual Studio 2012+ Premium/Ultimate中内置.您可能无法将其集成到持续集成链中. (7认同)
  • 这应该是公认的答案.MS假装现在伪装使得模拟静态方法成为可能. (3认同)
  • 使用 MS Fakes 时要非常小心!Fakes 是一个重定向框架,它的使用很快就会导致糟糕的架构。仅在绝对必要时才使用 Fakes,以拦截对第 3 方或(非常)遗留库的调用。最好的解决方案是创建一个带有接口的类包装器,然后通过构造函数注入或通过注入框架传入接口。模拟接口,然后将其传递到被测试的代码中。如果可能的话,远离假货。 (2认同)

mr1*_*100 6

您可以使用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);
Run Code Online (Sandbox Code Playgroud)

有关更多信息,请参见此处https://medium.com/@tonerdo/unit-testing-datetime-now-in-c-without-using-interfaces-978d372478e8