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

时间:2013-12-05 13:01:36

标签: c# mocking tdd

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

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

8 个答案:

答案 0 :(得分:50)

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

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

的类型

答案 1 :(得分:18)

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

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

答案 2 :(得分:12)

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

var mocked = new Mock<MyConcreteClass>();

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

答案 3 :(得分:6)

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

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

答案 4 :(得分:5)

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

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

例如:

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);
}

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

希望它有效。

答案 5 :(得分:4)

如果您无法更改受测试的课程,那么我建议的唯一选择是使用MS Fakes https://msdn.microsoft.com/en-us/library/hh549175.aspx。 但是,MS Fakes仅适用于几个版本的Visual Studio。

答案 6 :(得分:1)

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

适配器实现接口,因此mock也可以实现接口。

它不仅仅是创建虚拟方法或仅添加接口,而且如果您无法访问具体类的源,它可以让您脱离绑定。

答案 7 :(得分:1)

我在其中的一个旧项目和遗留项目中遇到过类似的问题,其中包含任何接口或最佳实践,而且由于项目的成熟,实施它们或重构代码太难了业务,所以在我的UnitTest项目中,我曾经在我想要模拟的类上创建一个Wrapper,并且包装器实现接口包含我想要设置和使用的所有我需要的方法,现在我可以模拟包装器而不是真实的课程。

例如:

您要测试的服务不包含虚拟方法或实现接口

public class ServiceA{

public void A(){}

public String B(){}

}

包裹到moq

public class ServiceAWrapper : IServiceAWrapper{

public void A(){}

public String B(){}

}

包装器接口

public interface IServiceAWrapper{

void A();

String B();

}

在单元测试中,您现在可以模拟包装器:

    public void A_Run_ChangeStateOfX()
    {
    var moq = new Mock<IServiceAWrapper>();
    moq.Setup(...);
    }

这可能不是最佳做法,但如果您的项目规则以这种方式强迫您,请执行此操作。同时将所有Wrappers放入单元测试项目或仅为单元测试指定的Helper项目中,以免使用不需要的包装器或适配器使项目过载。

<强>更新 这个答案来自一年多,但在今年我遇到了很多类似的场景,有不同的解决方案。 例如,使用Microsoft Fake Framework轻松创建模拟,伪造和存根,甚至可以在没有任何接口的情况下测试私有和受保护的方法。 您可以阅读:https://docs.microsoft.com/en-us/visualstudio/test/isolating-code-under-test-with-microsoft-fakes?view=vs-2017