70

模拟密封类可能会很痛苦。我目前喜欢使用适配器模式来处理这个问题,但有些东西总是让人感觉很奇怪。

那么,模拟密封类的最佳方式是什么?

Java 答案非常受欢迎。事实上,我预计 Java 社区处理这个问题的时间会更长,并且可以提供很多东西。

但这里有一些 .NET 意见:

4

10 回答 10

21

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

于 2008-08-28T06:44:22.923 回答
14

我的一般经验法则是,我需要模拟的对象也应该有一个通用接口。我认为这在设计上是正确的,并且使测试更容易(如果你做 TDD,通常是你得到的)。有关这方面的更多信息,请参阅 Google 测试博客的最新帖子(请参阅第 9 点)。

另外,在过去的 4 年里,我主要在 Java 领域工作,我可以说我一方面可以数出我创建最终(密封)类的次数。这里的另一条规则是我应该始终有充分的理由来密封一个课程,而不是默认密封它。

于 2008-08-09T09:40:37.727 回答
14

我相信来自 Microsoft Research 的Moles可以让您做到这一点。从痣页面:

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

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

Visual Studio 11中的Fakes Framework是 Moles & Stubs 的下一代,最终将取代它。然而,Fakes 与 Moles 不同,因此从 Moles 转移到 Fakes 将需要对您的代码进行一些修改。稍后将提供此迁移的指南。

要求:Visual Studio 11 Ultimate,.NET 4.5

于 2010-04-06T20:12:03.190 回答
6

TypeMock 的问题在于它为糟糕的设计找借口。现在,我知道它隐藏的往往是别人的糟糕设计,但是允许它进入你的开发过程很容易导致你自己的糟糕设计被允许。

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

于 2008-08-28T07:04:19.113 回答
4

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

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

于 2009-12-11T04:08:01.970 回答
3

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

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

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

  • YourObject.GetType().GetProperty("PropertyName").SetValue(dto, newValue, null);
  • YourObject.GetType().GetField("FieldName").SetValue(dto, newValue);
于 2015-05-12T19:42:34.163 回答
1

我通常采用创建接口和适配器/代理类的方法来促进密封类型的模拟。但是,我也尝试过跳过创建接口并使用虚拟方法使代理类型不密封。当代理真的是一个自然的基类,它封装了密封类的一部分和用户部分时,这很有效。

在处理需要这种调整的代码时,我厌倦了执行相同的操作来创建接口和代理类型,因此我实现了一个库来自动化任务。

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

欲了解更多信息,请参阅此页面

于 2009-12-08T05:14:09.100 回答
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;
    }
于 2010-04-06T20:05:01.763 回答
1

尽管它目前仅在 beta 版本中可用,但我认为值得牢记新Fakes 框架Visual Studio 11 Beta 版本的一部分)的shim功能。

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

就我个人而言,我正在考虑使用它来模拟密封框架类(如 DrawingContext)上的方法。

于 2012-04-04T01:24:41.890 回答
0

有没有办法从接口实现密封类......并模拟接口?

我觉得一开始就有密封的课程是错误的,但这就是我:)

于 2008-08-09T02:21:32.853 回答