如何在没有界面的情况下模拟一个类?

Vin*_*edo 58 c# tdd mocking

我正在使用Windows 7中的C#开发.NET 4.0.

我想测试一些使用mock的方法之间的通信.唯一的问题是我想在没有实现接口的情况下这样做.那可能吗?我刚刚阅读了很多关于模拟对象的主题和一些教程,但是它们都用于模拟接口,而不是类.我尝试使用Rhino和Moq框架.

Jus*_*ony 56

只需将您需要伪造的任何方法标记为virtual(而不是私有).然后,您将能够创建一个可以覆盖该方法的假货.

如果您使用new Mock<Type>并且您没有无参数构造函数,那么您可以将参数作为上述调用的参数传递,因为它需要一种类型的param Objects

  • @orad事实上,我倾向于先创建一个类,只有在需要突破常用功能时才创建一个接口. (12认同)
  • 当没有无参数构造函数并且封闭的 Concrete 类中的参数化构造函数不是公共的时,就会出现主要问题。(这里,我所说的“封闭”是指在外部包中)。然后,无法实现接口,无法使该方法成为虚拟方法,也无法使用参数化构造函数。 (5认同)
  • 这让我想知道是否有必要为我想要模拟的每个类创建一个接口。如果具体的类没有接口,我们不能只使用它们来进行模拟吗? (4认同)
  • 我解决了这个问题,如果你将你的模拟定义为“var myMock = new Mock&lt;MyType&gt;()”,你必须将它传递给使用模拟作为 myMock.Object 的类。 (2认同)

mat*_*att 19

大多数模拟框架(包括Moq和RhinoMocks)生成代理类作为模拟类的替代,并使用您定义的行为覆盖虚拟方法.因此,您只能在具体或抽象类上模拟接口或虚拟方法.另外,如果你在嘲笑一个具体的类,你几乎总是需要提供一个无参数的构造函数,以便模拟框架知道如何实例化该类.

为什么厌恶在代码中创建接口?

  • 因为它通过大量的接口阻塞了代码库,如果没有测试框架的某些技术限制,那么完全没有必要吗? (76认同)
  • "唯一的目的是提供测试",但是没有创建对您很重要的可测试代码? (9认同)
  • "为什么厌恶在代码中创建接口?" 因为我没有写这个对象,而且我没有访问代码.我需要创建一个我不拥有的封闭对象的模拟,它不实现接口. (9认同)
  • 你已经听过"达到超出掌握"的表达.这就解释了为什么开发人员总是抱怨.C#语言的设计并未考虑单元测试.这就是为什么在没有无意义的接口或虚拟方法的巨大混乱的情况下注入模拟依赖项真的很难.也许有人会很快发明一种易于单元测试的语言.到那时我们还会抱怨别的东西. (7认同)
  • 我很高兴知道我不是唯一一个将接口和虚拟方法视为混乱的人,如果他们的唯一目的是为了服务测试. (7认同)
  • @ BlueRaja-DannyPflughoeft同样适用于标记方法`virtual`.= /哎呀,我正在看一个案例,如果我不考虑嘲笑它,我可以让整个班级成为问题`静态`.(它只是方法的集合.它没有状态,也永远不会.) (4认同)
  • @bouke太好了,实际上我已经做到了。但是,这只是将问题推到了一个层次。现在,我需要对代理类进行单元测试,该代理类又包含一个我需要模拟的对象,该对象未实现接口。回到最初的问题,我该如何对它进行单元测试? (2认同)

Roy*_*tus 14

使用MoQ,您可以模拟具体类:

var mocked = new Mock<MyConcreteClass>();
Run Code Online (Sandbox Code Playgroud)

但这允许您覆盖virtual代码(方法和属性).

  • 我尝试过,但是当我运行测试项目时,我的程序抛出异常:“无法实例化类的代理”“找不到无参数构造函数。” (3认同)
  • 为什么不直接为类创建一个接口呢? (3认同)
  • 只需将构造函数参数传递给 Mock&lt;&gt; 构造函数。例如`new Mock&lt;MyConcreteClass&gt;(param1, anotherParam,thirdParam, evenMoreParams);` (2认同)

Tho*_*ler 6

标准的模拟框架正在创建代理类.这就是他们在技术上限于接口和虚拟方法的原因.

如果您还想模拟"普通"方法,则需要一个可以使用检测而不是代理生成的工具.例如MS Moles和Typemock可以做到这一点.但前者有一个可怕的'API',而后者是商业的.


Ana*_*ria 6

我认为最好为该类创建一个接口。并使用界面创建单元测试。

如果您无权访问该类,则可以为该类创建适配器。

例如:

public class RealClass
{
    int DoSomething(string input)
    {
        // real implementation here
    }
}

public interface IRealClassAdapter
{
    int DoSomething(string input);
}

public class RealClassAdapter : IRealClassAdapter
{
    readonly RealClass _realClass;

    public RealClassAdapter() => _realClass = new RealClass();

    int DoSomething(string input) => _realClass.DoSomething(input);
}
Run Code Online (Sandbox Code Playgroud)

这样,您可以使用IRealClassAdapter轻松为类创建模拟。

希望它能工作。

  • @JohnHenckel我假设我们无权访问RealClass。因此,在我看来,添加“虚拟”方法不是一种选择。如果您可以访问真实的类,则最好直接实现与新类的接口,我认为这是最佳实践。 (11认同)
  • 这对于维护来说是可怕的。如果要向 RealClass 添加新方法,还必须将其添加到 IReadClassAdapter 和 RealClassAdapter。三重努力!更好的解决方案是为 RealClass 中的每个公共方法添加“virtual”关键字。 (8认同)

Rus*_*lin 5

如果您不能更改被测类,那么我建议的唯一选择是使用MS Fakes https://msdn.microsoft.com/en-us/library/hh549175.aspx。但是,MS Fakes仅在Visual Studio的几个版本中起作用。


Tim*_*ger 5

如果情况变得更糟,您可以创建一个接口和适配器对。您将更改 ConcreteClass 的所有用法以使用接口,并始终在生产代码中传递适配器而不是具体类。

适配器实现了接口,因此模拟也可以实现接口。

它比仅仅使方法虚拟或仅添加接口更像是脚手架,但如果您无权访问具体类的源代码,它可以让您摆脱束缚。