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

Dav*_*son 6 c# methods unit-testing moq abstract

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

public abstract class B
{
    internal abstract void DoSomething();
}
Run Code Online (Sandbox Code Playgroud)

我的单元测试看起来像.

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

    classA.Add(mock.Object);

    // Assertion
}
Run Code Online (Sandbox Code Playgroud)

我收到以下异常.

测试方法TestSomething引发异常:
System.ArgumentException:类型为mock必须是接口或抽象或非密封类.---> 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时,使用内部方法是否可行.

Gab*_*oya 6

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

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

一个解决办法是与标记你的装配InternalsVisibleToAttribute和动态指定生成的程序集的名称:

[InternalsVisibleTo("DynamicProxyGenAssembly2")]
Run Code Online (Sandbox Code Playgroud)

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

  • 这通常很有用,但在我的情况下,InternalsVisibleTo已经被适当地应用了.问题在于Moq处理抽象类型的内部方法,具体而言.最终答案是MikeZ建议的,这是重新考虑设计. (3认同)

Mik*_*ray 3

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