如何在 Smalltalk 中以编程方式替换消息正文?
作为某些测试的一部分,我希望暂时将消息内容替换为将返回一组可预测的测试值的消息内容。完成此类工作的标准 Smalltalk-y 方式是什么?有一些示例代码供我查看吗?
一些澄清:
- 不,这不是一个简单的单元测试。我正在测试一个大型、复杂的系统。
- 在运行时替换整个对象是不切实际的。除了更换方法之外,还必须通过测试来调整重要的累积状态。
- 子类化和替换一种方法并不能概括。我正在测试数十个类似的类和数千个对象。用微小的方法类填充类层次结构,每个测试用例一个方法类,这真的很糟糕 - 如果我更改测试用例,则必须更新它们会更糟糕。
这是我想要编写的一些伪代码:
replaceMessage: 'fibonacci'
onClass: 'funFunctions'
with: 'returnsPredictableNumbersWithoutCalculatingThem'.
self runTestSet1.
restoreMessage: 'fibonacci'
onClass: 'funFunctions'.
self runFollowUpSet1. "Depends on state set by Set1"
replaceMessage: 'fibonacci'
onClass: 'funFunctions'
with: 'returnsPredictableNumbersWithoutCalculatingThemPart2'.
self runFollowUpSet2. "Depends on state set by followupset1"
restoreMessage: 'fibonacci'
onClass: 'funFunctions'.
As part of some testing, I'm looking to, temporarily, replace the contents of a message with one that will return a predictable set of test values. What is the standard Smalltalk-y way to do this type of work? Is there some sample code for me to look at?
Some clarification:
- No, this is not a simple unit test. I'm testing a large, complex, system.
- Replacing the whole object at runtime is impractical. Significant accumulated state would have to be twiddled by the test, beyond replacing the method.
- Subclassing and replacing one method doesn't generalize. I'm testing dozens of similar classes and thousands of objects. Filling up the class hierarchy with tiny one method classes, one for each test-case, would really suck - and it would suck even more to have to update them if I change my test case.
Here's some pseudocode for what I'm looking to write:
replaceMessage: 'fibonacci'
onClass: 'funFunctions'
with: 'returnsPredictableNumbersWithoutCalculatingThem'.
self runTestSet1.
restoreMessage: 'fibonacci'
onClass: 'funFunctions'.
self runFollowUpSet1. "Depends on state set by Set1"
replaceMessage: 'fibonacci'
onClass: 'funFunctions'
with: 'returnsPredictableNumbersWithoutCalculatingThemPart2'.
self runFollowUpSet2. "Depends on state set by followupset1"
restoreMessage: 'fibonacci'
onClass: 'funFunctions'.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
要将一条消息替换为另一条消息,您可以:
a) 在相应的方法中,更改负责发送给定消息的选择器。
b) 使用代理来包装所有感兴趣的对象并拦截给定的消息,以便使用与原始方法不同的评估路径。
To replace one message with another you could:
a) in corresponding method(s), change the selector, which responsible for given message send.
b) use proxies, to wrap all objects of interest and intercept given message in order to use different evaluation path than in original method.
一种方法是使用 MethodWrappers 或类似的方法。有一种叫做“对象作为方法”的东西,它在一些 Smalltalk 方言中受到支持,例如 Pharo 和 Squeak(不确定其他方言)。这个想法是您可以使用任何对象作为方法。所以,你可以这样做:
MyClass methodDict at: #foo put: MyClassAsMethod new。
在 MyClassMethod 中,您必须使用:arguments in: aReceiver 来实现 #run: aSelector
因此,当您执行以下操作时: MyClass new foo.然后虚拟机将看到你所拥有的不是 CompiledMethod 而是另一个对象,然后它将向该对象发送消息 #run:with:in:
如果我是你,我会从这里下载 pharo: https://gforge.inria.fr/frs/download .php/27524/Pharo-1.1.1-OneClickCogVM.zip
并将研究包 MethodWrappers 及其所有类。
A way to do this is using MethodWrappers or similar. There is one thing called "Objects as Methods", which is supported in some Smalltalk dialects like Pharo and Squeak (not sure about others). The idea is that you can use any object as method. So, you can do this:
MyClass methodDict at: #foo put: MyClassAsMethod new.
And in MyClassMethod you have to implement #run: aSelector with: arguments in: aReceiver
So, when you do: MyClass new foo. Then the VM will see that what you have there is NOT a CompiledMethod but instead another object, and then it will send to such object the message #run:with:in:
If I were you, I would download pharo from here: https://gforge.inria.fr/frs/download.php/27524/Pharo-1.1.1-OneClickCogVM.zip
and would investigate the package MethodWrappers and all its classes.
也许MethodWrappers - ECOOP论文救援包装器
Maybe MethodWrappers - ECOOP paper Wrappers to the Rescue
我的第一个想法是子类化并仅覆盖您想要返回测试值的特定消息。但是,您可能需要阅读 要测试的子类 和 测试 Ward wiki 上反模式条目的子类。
要使其成为临时的,您需要将测试对象替换为子类的实例,然后在完成后将其更改回被测试的原始类。
My first thought would be to sub-class and override only the particular message you want to return test values. However, you'll probably want to read the subclass to test and the subclass to test anti-pattern entries on Ward's wiki.
To make this temporary, you would need to replace the test object with an instance of the sub-class, then change it back to the original class under test when you're done.