我有一个类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时,使用内部方法是否可行。
答案 0 :(得分:6)
Moq依靠Castle Dynamic Proxy来实现其模拟。在运行时,Moq会创建一个新的程序集并将其加载到您的AppDomain中(异常消息中出现的名称为 DynamicProxyGenAssembly2 )。
问题是此程序集无法访问内部类和您自己代码的成员,因为它们在您声明它们的程序集之外不可见。
解决方法是使用InternalsVisibleToAttribute标记程序集并指定动态生成的程序集的名称:
[InternalsVisibleTo("DynamicProxyGenAssembly2")]
但请记住,此解决方案依赖于实现细节,并可能在将来的版本中停止工作。
答案 1 :(得分:3)
请仔细考虑为什么在公共类上使用internal abstract
方法。其他人将无法实现抽象方法,因为它们不可见。也许这就是你想要的。有时,考虑到各种设计约束,这是一种有效的方法。如果您的库将被其他人使用,我建议至少将构造函数设置为内部,因此没有人会认为他们可以实现抽象类。