模拟密封课程可能会很痛苦。 我目前更喜欢使用Adapter模式来处理此问题,但是大约有些感觉很奇怪。
那么,模拟密封类的最佳方法是什么?
非常欢迎Java的答案。 实际上,我希望Java社区已经对此进行了较长的处理,并且可以提供很多帮助。
但是,这里有一些.NET意见:
-
为什么鸭子为C#打字很重要
开发人员
-
创建包装器
用于密封和其他类型的
嘲笑
-
WCF(和Moq)的单元测试
对于.NET,您可以使用TypeMock之类的东西,它使用概要分析API,并允许您挂接对几乎所有内容的调用。
我相信Microsoft Research的Moles可以帮助您做到这一点。从"痣"页面:
Moles may be used to detour any .NET
method, including non-virtual/static
methods in sealed types.
更新:即将发布的VS 11版本中有一个名为" Fakes"的新框架,旨在取代Moles:
The Fakes Framework in Visual Studio 11 is the next generation of Moles & Stubs, and will eventually replace it. Fakes is different from Moles, however, so moving from Moles to Fakes will require some modifications to your code. A guide for this migration will be available at a later date.
Requirements: Visual Studio 11 Ultimate, .NET 4.5
我的一般经验法则是,我需要模拟的对象也应该有一个公共接口。我认为这是正确的设计方式,并且使测试容易得多(通常,如果执行TDD,通常会得到此结果)。有关此问题的更多信息,请参阅Google Testing Blog最新文章(请参阅第9点)。
另外,在过去的四年中,我主要从事Java工作,我可以说一方面可以指望我创建最终(密封)类的次数。这里的另一个规则是,我应该总是有一个很好的理由来密封一个类,而不是默认情况下密封它。
TypeMock的问题在于它会为不良设计辩解。现在,我知道它通常隐藏着其他人的不良设计,但是允许它进入您的开发过程可以很容易地导致允许您自己的不良设计。
我认为,如果要使用模拟框架,则应使用传统框架(如Moq),并在不可模拟的事物周围创建隔离层,然后模拟隔离层。
我几乎总是避免在代码中深入依赖外部类。相反,我宁愿使用适配器/网桥与他们交谈。这样,我就在处理我的语义,翻译的痛苦被隔离在一个类中。
从长远来看,这也使切换我的依赖关系变得更加容易。
我最近遇到了这个问题,在阅读/搜索网络之后,似乎没有简单的方法可以使用上面提到的其他工具。
或者像我那样粗暴地处理事情:
尽管它目前仅在beta版本中可用,但我认为值得牢记新的Fakes框架(Visual Studio 11 Beta版本的一部分)的填充功能。
Shim types provide a mechanism to detour any .NET method to a user defined delegate. Shim types are code-generated by the Fakes generator, and they use delegates, which we call shim types, to specify the new method implementations. Under the hood, shim types use callbacks that were injected at runtime in the method MSIL bodies.
我个人当时正在使用它来模拟密封框架类(例如DrawingContext)上的方法。
模拟密封类是完全合理的,因为许多框架类都是密封的。
就我而言,我试图模拟.Net的MessageQueue类,以便可以TDD我优美的异常处理逻辑。
如果有人对如何克服Moq关于"不可覆盖成员上的无效设置"的错误有任何想法,请告诉我。
码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| [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;
} |
我通常采用创建接口和适配器/代理类的途径来简化密封类型的模拟。但是,我还尝试过跳过接口的创建,并使代理类型不与虚拟方法密封。当代理实际上是封装并使用密封类的用户部分的自然基类时,此方法很好用。
在处理需要这种适应的代码时,我厌倦了执行相同的操作来创建接口和代理类型,因此我实现了一个库来自动执行任务。
该代码比您所引用的文章中的示例要复杂得多,因为它可以生成程序集(而不是源代码),可以在任何类型上执行代码生成,并且不需要太多配置。
有关更多信息,请参阅此页面。
有没有一种方法可以从接口实现密封类并模拟接口呢?
我内心有些感觉,一开始就有密封的类是错误的,但这就是我自己:)