如何使用Moq模拟包含内部抽象方法的抽象类?

时间:2014-10-04 20:22:02

标签: c# unit-testing methods moq abstract

我有一个类A我正在单元测试一个方法,它将类B作为参数。类B是我想要模拟的,它是一个抽象类。该课程类似于以下内容。

public abstract class B
{
    internal abstract void DoSomething();
}

我的单元测试看起来像。

[TestMethod]
public void ClassA_Add_TestSomething()
{
    var classA = new A();
    var mock   = new Mock<B>();

    classA.Add(mock.Object);

    // Assertion
}

我收到以下例外情况。

  

测试方法TestSomething引发异常:
  System.ArgumentException:类型为mock必须是接口或抽象或非密封类。 ---&GT; System.TypeLoadException:程序集'DynamicProxyGenAssembly2,Version = 0.0.0.0,Culture = neutral,PublicKeyToken = null'类型'Castle.Proxies.BProxy'中的方法'DoSomething'没有实现。

我可以通过使方法虚拟而不是抽象来解决这个问题,但这不是我想要通过API设计实现的。我也试过通过mock.Setup(m => m.DoSomething())提供一个实现无济于事。这是可能的Moq,还是我将不得不创建一个派生自抽象类B的具体测试类?我想避免创建具体类型,这种类型会破坏使用模拟或存根框架的某些目的,或者我在这里误导?

编辑我发现如果我制作方法public abstract,则不会发生此问题。所以我想真正的问题是,当使用InternalsVisibleTo和Moq时,使用内部方法是否可行。

2 个答案:

答案 0 :(得分:6)

Moq依靠Castle Dynamic Proxy来实现其模拟。在运行时,Moq会创建一个新的程序集并将其加载到您的AppDomain中(异常消息中出现的名称为 DynamicProxyGenAssembly2 )。

问题是此程序集无法访问内部类和您自己代码的成员,因为它们在您声明它们的程序集之外不可见。

解决方法是使用InternalsVisibleToAttribute标记程序集并指定动态生成的程序集的名称:

[InternalsVisibleTo("DynamicProxyGenAssembly2")]

但请记住,此解决方案依赖于实现细节,并可能在将来的版本中停止工作。

答案 1 :(得分:3)

请仔细考虑为什么在公共类上使用internal abstract方法。其他人将无法实现抽象方法,因为它们不可见。也许这就是你想要的。有时,考虑到各种设计约束,这是一种有效的方法。如果您的库将被其他人使用,我建议至少将构造函数设置为内部,因此没有人会认为他们可以实现抽象类。