你怎么模仿密封课程?

时间:2008-08-09 00:14:22

标签: unit-testing language-agnostic tdd mocking

Mocking sealed classes可能会非常痛苦。我目前赞成使用Adapter pattern来处理这个问题,但有些事情让我觉得很奇怪。

那么,你嘲笑密封班的最佳方式是什么?

Java答案非常受欢迎。事实上,我预计Java社区已经处理这个问题的时间越来越长,并且提供了大量的服务。

但这里有一些.NET意见:

11 个答案:

答案 0 :(得分:18)

对于.NET,你可以使用类似TypeMock的东西,它使用分析API并允许你挂钩几乎任何东西的调用。

答案 1 :(得分:13)

我相信来自Microsoft Research的{​​{3}}允许您这样做。从Moles页面:

  

Moles可用于绕道任何.NET   方法,包括非虚拟/静态   密封类型的方法。

UPDATE:在即将推出的VS 11版本中有一个名为“Fakes”的新框架,旨在取代Moles:

  

Moles是下一代Moles&存根,并最终将取代它。假货与Moles不同,因此从Moles转向Fakes需要对代码进行一些修改。此迁移指南将在以后提供。

     

要求:Visual Studio 11 Ultimate,.NET 4.5

答案 2 :(得分:7)

我的一般经验法则是我需要模拟的对象也应该有一个共同的接口。我认为这是正确的设计,使测试更容易(通常是你做TDD时得到的)。有关这方面的更多信息,请参阅Google测试博客latest post(参见第9点)。

另外,在过去的4年里,我一直在Java工作,我可以说我可以一手掌握我创建最终(密封)课程的次数。这里的另一个规则是我应该总是有充分的理由来密封一个类,而不是默认密封它。

答案 3 :(得分:4)

TypeMock的问题在于它可以为糟糕的设计辩解。现在,我知道它隐藏了其他人糟糕的设计,但允许它进入你的开发过程可能很容易导致你自己的糟糕设计。

我认为如果你要使用一个模拟框架,你应该使用一个传统的框架(比如Moq)并围绕不可迁移的东西创建一个隔离层,而不是模拟隔离层。

答案 4 :(得分:4)

我几乎总是避免在我的代码深处依赖外部类。相反,我宁愿使用适配器/桥接器与他们交谈。这样,我正在处理我的语义,翻译的痛苦在一个类中被隔离。

从长远来看,它还可以更轻松地切换我的依赖项。

答案 5 :(得分:2)

我最近遇到了这个问题,在阅读/搜索网页后,似乎没有简单的方法,除了使用上面提到的其他工具。 或者像我一样粗暴地处理事情:

  • 创建密封类的实例,而不需要调用构造函数。
  • System.Runtime.Serialization.FormatterServices.GetUninitializedObject(instanceType);

  • 通过反射为您的属性/字段指定值

  • YourObject.GetType()。GetProperty(" PropertyName")。SetValue(dto,newValue,null);
  • YourObject.GetType()。GetField(" FieldName")。SetValue(dto,newValue);

答案 6 :(得分:1)

我通常采用创建接口和适配器/代理类的路线来促进密封类型的模拟。但是,我还尝试跳过界面的创建,并使用虚拟方法使代理类型不被密封。当代理实际上是封装和用户密封类的一部分的自然基类时,这很有效。

在处理需要这种改编的代码时,我厌倦了执行相同的操作来创建接口和代理类型,所以我实现了一个库来自动完成任务。

代码比您引用的文章中给出的示例稍微复杂一些,因为它生成一个程序集(而不是源代码),允许在任何类型上执行代码生成,并且不需要那么多配置

有关详细信息,请参阅this page

答案 7 :(得分:1)

模拟密封类是完全合理的,因为许多框架类都是密封的。

在我的情况下,我正在尝试模拟.Net的MessageQueue类,以便我可以TDD我优雅的异常处理逻辑。

如果有人对如何克服Moq关于“无法覆盖的成员的无效设置”的错误有任何想法,请告诉我。

代码:

    [TestMethod]
    public void Test()
    {
        Queue<Message> messages = new Queue<Message>();
        Action<Message> sendDelegate = msg => messages.Enqueue(msg);
        Func<TimeSpan, MessageQueueTransaction, Message> receiveDelegate =
            (v1, v2) =>
            {
                throw new Exception("Test Exception to simulate a failed queue read.");
            };

        MessageQueue mockQueue = QueueMonitorHelper.MockQueue(sendDelegate, receiveDelegate).Object;
    }
    public static Mock<MessageQueue> MockQueue
                (Action<Message> sendDelegate, Func<TimeSpan, MessageQueueTransaction, Message> receiveDelegate)
    {
        Mock<MessageQueue> mockQueue = new Mock<MessageQueue>(MockBehavior.Strict);

        Expression<Action<MessageQueue>> sendMock = (msmq) => msmq.Send(It.IsAny<Message>()); //message => messages.Enqueue(message);
        mockQueue.Setup(sendMock).Callback<Message>(sendDelegate);

        Expression<Func<MessageQueue, Message>> receiveMock = (msmq) => msmq.Receive(It.IsAny<TimeSpan>(), It.IsAny<MessageQueueTransaction>());
        mockQueue.Setup(receiveMock).Returns<TimeSpan, MessageQueueTransaction>(receiveDelegate);

        return mockQueue;
    }

答案 8 :(得分:1)

虽然目前仅在测试版中提供,但我认为值得记住新shimFakes framework功能(Visual Studio 11 Beta版本的一部分)。

  

Shim类型提供了一种将任何.NET方法绕道到用户定义的委托的机制。 Shim类型是由Fakes生成器代码生成的,它们使用委托(我们称之为shim类型)来指定新的方法实现。在引擎盖下,填充类型使用在运行时在方法MSIL主体中注入的回调。

就我个人而言,我正在使用它来模拟密封框架类(如DrawingContext。

)上的方法

答案 9 :(得分:0)

有没有办法从接口实现密封类...而是模拟界面?

我身上有些东西觉得密封课程首先是错误的,但那只是我:)

答案 10 :(得分:0)

您可以使用免费且获得 MIT 许可的 Mocksanity