使用模拟框架记录拐点上的交互。 起订量
问题的更新版本
嗨。
我的公司有一些遗留代码库,我希望在它们迁移到 .NET 3.5 后立即对其进行测试。 我选择 Moq 作为我的 Mocking 框架(我立即爱上了清晰的语法)。
我预计将来会经常看到的一种常见场景是,我看到一个对象与其他一些对象进行交互。
我了解 Michael Feathers 的作品,而且我越来越擅长识别拐点和隔离合适尺寸的组件。 提取和覆盖才是王道。
然而,有一个功能可以让我的生活变得更加轻松。
想象一下 Component1 与 Component2 交互。 Component2 是一些奇怪的串行线路接口,连接到消防中心或类似的设备,具有大量字节检查、转换和指针操作。 我不想理解 Component2 及其由 Component1 使用的遗留接口带来的很多包袱。
我想做的是提取 Component1 消耗的 Component2 的接口,然后执行如下操作:
component1.FireCentral = new Mock<IComponent2> (component2);
我正在创建一个普通的模拟,但我将真正的 Component2 的实例作为构造函数参数传递到模拟中目的。 看起来我正在根据 Component2 进行测试,但我不打算保留此代码。 这是“放置测试对象”仪式的一部分。
现在,我将启动真实的系统(连接了物理火中心),然后与我的对象进行交互。
然后我希望检查模拟以查看组件 1 如何与组件 2 交互的日志(使用调试器检查模拟上的一些字符串集合)。 而且,更好的是,模拟可以提供一个期望列表(在 C# 中),这将在不依赖于 Component2 的模拟中创建此行为,然后我将在测试代码中使用它。
简而言之。 使用模拟框架记录交互,以便我可以在测试代码中回放它。
问题的旧版本
嗨。
在使用遗留代码和大量实用程序类的过程中,我有时发现自己想知道在许多场景中,特定的类是如何被其周围环境所影响的。 今天早上我正在处理的一个案例涉及对常规 MemoryStream 进行子类化,以便在达到一定大小时将其内容转储到文件中。
// TODO: Remove
private class MyLimitedMemoryStream : MemoryStream
{
public override void Write(byte[] buffer, int offset, int count)
{
if (GetBuffer().Length > 10000000)
{
System.IO.FileStream file = new FileStream("c:\\foobar.html",FileMode.Create);
var internalbuffer = GetBuffer();
file.Write(internalbuffer,0,internalbuffer.Length);
}
base.Write(buffer, offset, count);
}
}
(我在这里使用断点在文件写入后退出程序)。 这有效,我发现哪个 Web 表单(Web 部件 -> Web 部件 -> Web 部件)控件渲染不正确。 然而,内存流有一堆写入和写入行。
我可以使用模拟框架来快速了解特定实例的操作方式吗? 有什么巧妙的技巧吗? 我们使用 Rhino Mocks。
我认为这是处理遗留代码的一个重要资产。 特别是如果可以轻松地将场景中记录的操作设置为在单元测试中复制的同一场景的新期望/接受标准。
每一个输入都受到赞赏。 感谢您的阅读。
Updated version of question
Hi.
My company has a few legacy code bases which I hope to get under test as soon as they migrate to .NET 3.5. I have selected Moq as my Mocking framework (I fell in love with the crisp syntax immediately).
One common scenario, which I expect to see a lot of in the future, is where I see an object which interacts with some other objects.
I know the works of Michael Feathers and I am getting good at identifying inflection points and isolating decent sized components. Extract and Override is king.
However, there is one feature which would make my life a whole lot easier.
Imagine Component1 interacting with Component2. Component2 is some weird serial line interface to a fire central or somesuch with a lot of byte inspection, casting and pointer manipulation. I do not wish to understand component2 and its legacy interface consumed by Component1 carries with it a lot of baggage.
What I would like to do, is to extract the interface of Component2 consumed by Component1 and then do something like this:
component1.FireCentral = new Mock<IComponent2> (component2);
I am creating a normal mock, but I am pasing in an instance of the real Component2 in as a constructor argument into the Mock object. It may seem like I'm making my test depending on Component2, but I am not planning on keeping this code. This is part of the "place object under test" ritual.
Now, I would fire up the real system (with a physical fire central connected) and then interact with my object.
What I then would wish for, is to inspect the mock to see a log of how component1 interacted with component2 (using the debugger to inspect some collection of strings on the mock). And, even better, the mock could provide a list of expectations (in C#) that would create this behavior in a mock that did not depend on Component2, which I would then use in my test code.
In short. Using the mocking framework to record the interaction so that I can play it back in my test code.
Old version of question
Hi.
Working with legacy code and with a lot of utility classes, I sometimes find myself wondering how a particular class is acted upon by its surroundings in a number of scenarios.
One case that I was working on this morning involved subclassing a regular MemoryStream so that it would dump its contents to file when reaching a certain size.
// TODO: Remove
private class MyLimitedMemoryStream : MemoryStream
{
public override void Write(byte[] buffer, int offset, int count)
{
if (GetBuffer().Length > 10000000)
{
System.IO.FileStream file = new FileStream("c:\\foobar.html",FileMode.Create);
var internalbuffer = GetBuffer();
file.Write(internalbuffer,0,internalbuffer.Length);
}
base.Write(buffer, offset, count);
}
}
(and I used a breakpoint in here to exit the program after the file was written).
This worked, and I found which webform (web part->web part->web part) control rendered incorrectly. However, memorystream has a bunch of write's and writeline's.
Can I use a mocking framework to quickly get an overview on how a particular instance is acted upon? Any clever tricks there? We use Rhino Mocks.
I see this as a great assett in working with legacy code. Especially if recorded actions in a scenario easily can be set up as new expectations/acceptance criteria for that same scenario replicated in a unit test.
Every input is appreciated. Thank you for reading.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
欢迎来到了解此要求的“远见者”小俱乐部:)
不幸的是,我会告诉您,我认为 .NET 尚不存在这种情况。 我也很确定 Java 也不存在...因为我已经定期搜索了几年,甚至在按工付费网站上为此提供了现金奖励,但什么也没有出现(一些俄罗斯开发商提出从头开始实施它,但这超出了我的预算)。
但是,我在 PHP 中创建了一个概念验证来演示这个想法,也许其他人会对用其他语言开发它感兴趣(.NET 对你来说,Java 对我来说)。
以下是 PHP 概念验证:
http://code.google.com /p/php-mock-recorder/
Welcome to the small club of "visionaries" who understand this requirement :)
Unfortunately I'll tell you, I don't think this exists yet for .NET. I'm also pretty sure it doesn't exist for Java either... as I've been searching periodically for a few years, and even offered a cash bounty for this on a pay-for-work website, and nothing turned up (some Russian developer offered to implement it from scratch, but it was outside my budget).
But, I have created a Proof Of Concept in PHP to demonstrate the idea and perhaps other get people interested in developing this for other languages (.NET for you, Java for me).
Here is the PHP proof-of-concept:
http://code.google.com/p/php-mock-recorder/
我认为您无法使用模拟框架来轻松了解特定实例的操作方式。
但是,您可以使用模拟框架来定义应如何对其进行操作,以验证是否以这种方式对其进行操作。 在遗留代码中,这通常需要使代码可测试,例如引入接口等。
一种可以与遗留代码一起使用而不需要对代码进行大量重构的技术是使用记录接缝。 您可以在这篇 InfoQ 文章中阅读更多相关内容:使用日志接缝进行遗留代码单元测试。
如果您想了解有关如何测试遗留代码的更多提示,我推荐这本书有效地处理遗留代码代码,作者:Michael Feathers。
希望这可以帮助!
I don't think you can use a mocking framework to easily get an overview of how an particular instance is acted upon.
You can however use the mocking framework to define how it should be acted upon in order to verify that it is acted upon in this way. In legacy code this often requires making the code testable, e.g. introducing interfaces etc.
One technique that can be used with legacy code without needing to much restructuring of the code is using logging seams. You can read more about this in this InfoQ article: Using Logging Seams for Legacy Code Unit Testing.
If you want more tips on how to test legacy code, I recommend the book Working Effectively with Legacy Code by Michael Feathers.
Hope this helps!
是的,这是可能的。 如果您使用严格的模拟并运行一个执行该模拟的单元测试,则测试将失败,并告诉您调用了哪个意外方法。
这是您要找的吗?
Yes, this is possible. If you use a strict mock and run a unit test that exercises the mock, the test will fail, telling you which unexpected method was called.
Is this what you're looking for?
模拟框架不是为这个问题而设计的。 我不明白如何使用 Moq 或 RhinoMocks 来完成这项工作。 即使功能强大的 TypeMock 也可能无法满足您的要求。 模拟框架不是为此构建的。
相反,使用面向方面的编程 (AOP) 工具来编织方法调用前和方法调用后的调用。 这将完全满足您的要求:查看特定类型的所有交互。 例如,在 PostSharp AOP 框架中,您只需指定要在方法之前和之后调用的方法调用其他对象:
这将记录组件 2 上调用的所有方法。
Mock frameworks weren't designed for this problem. I don't see how you can make this work with either Moq or RhinoMocks. Even the powerful TypeMock may not be able to do what you're asking. Mock frameworks weren't built for this.
Instead, use an aspect-oriented programming (AOP) tool to weave pre- and post- method invocation calls. This will do exactly what you want: see all interactions for a particular type. For example, in the PostSharp AOP framework, you simply specify methods you'd like called before and after a method call on some other object:
That will log all the methods that are called on component 2.