【问题标题】:How should I mock this abstract class with Moq?我应该如何用 Moq 模拟这个抽象类?
【发布时间】:2011-07-28 21:34:25
【问题描述】:

这并不是真正特定于 Moq,而是更多的一般 Mocking 框架问题。我为“IAsset”类型的对象创建了一个模拟对象。我想模拟从 IAsset 的 getter “Info”返回的类型。

 var mock = new Mock<IAsset>();
        mock.SetupGet(i => i.Info).Returns(//want to pass back a mocked abstract);
        mock.SetupProperty(g => g.Id, Guid.NewGuid());

我遇到的问题是模拟这个返回的属性值。

 mock.SetupGet(i => i.Info).Returns(//this is the type I need to mock);

该属性包含一个抽象类型。这种类型扩展了 XDocument。

public abstract class SerializableNodeTree : XDocument, ISerializable{...}

所以..我想做的是:

 var nodeTreeMock = new Mock<SerializableNodeTree>();
        nodeTreeMock .SetupGet(d => d.Document).Returns(xdoc);

xdoc 是一个 XDocument 实例。这将不起作用,因为 XDocument.Document getter 不是虚拟的。这是有道理的。

我应该手动编写一个派生自 SerializableNodeTree 的模拟,还是有办法模拟这个对象?

【问题讨论】:

    标签: c# unit-testing mocking moq


    【解决方案1】:

    在这种情况下,我会将 XDocument 视为标准的、不可模拟的对象,例如 strings 以及大多数 POCO 和本机类型。也就是说,你应该创建一个真实的(非模拟的)SerializableNodeTree 从IAsset.Info 返回。

    另一种选择是让SerializableNodeTree 实现一个接口,该接口包含您要模拟的所有方法,并让IAsset.Info 返回该接口类型而不是直接返回SerializableNodeTree

    【讨论】:

    • 我决定只使用您的第一个建议并创建了 SerializableNodeTree 的测试实现
    【解决方案2】:

    在这种情况下,我将创建一个从您的抽象类派生的测试替身。这将为您提供测试所需的内容。

    public class SerializableNodeTreeDouble : SerializableNodeTree
    {
       public new XDocument Document
       {
         get;         
         set;
       }
    
       ...
    }
    
    public void TestMethod()
    {
       SerializableNodeTreeDouble testDouble = new SerializableNodeTreeDouble();
       testDouble.XDocument = xdoc; // your xdoc
    
       ...
    }
    

    希望这会有所帮助。

    【讨论】:

    • 如果Document 属性不是虚拟的,您可以隐藏原来的Document 属性,但不能覆盖它。因此,我希望模拟类不会调用您定义的属性 getter。
    • 新的修饰符将隐藏基类的实现,从而允许我们在这里模拟出依赖关系(虽然不理想)。这是新修改器的 MSDN。 msdn.microsoft.com/en-us/library/435f1dw2.aspx
    • 是的,new 修饰符是您用来隐藏原始Document 属性的。但是调用SerializableNodeTree.Document 的代码最终不会调用SerializableNodeTreeDouble.Document,即使底层对象是SerializableNodeTreeDouble。为了获得这种行为,您需要 override 方法,如果定义的方法不是抽象的或虚拟的,则该方法既不能静态完成,也不能通过 Moq 完成。
    • 对,如果代码直接通过命名空间调用,它不会调用双精度。那么这里的代码是做什么的呢?它是明确调用 SerializableNodeTree.Document 还是只是 Document 属性?如果是第二个新的应该没问题吧? (不是想打死马,就像讨论一样)
    • @BrianDishaw 因为它在模拟类中被键入为SerializableNodeTree,它还将调用该类型的属性,获得原始实现。使用new 隐藏某些内容并不能很好地处理多态性。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多