Smalltalk:如何修改自我行为

时间:2012-10-31 07:56:17

标签: mocking metaprogramming smalltalk

我正在将我最喜欢的Java / JavaScript Mocktito库移植到Smalltalk。我目前正在实施Spy来存储真实对象。当一个spyed对象调用它自己的stubed方法时,我的问题出现了。而不是:

self aMethod.

我宁愿将调用委托给间谍对象:

spyObject aMethod.

以下是预期行为的方案测试:

realObject := RealObjectForTesting new.
spyedObject := Spy new: realObject.
spyedObject when: #accesorWhichReturnsValue thenReturn: 'stubbed value'.

spyedObject accesorWhichCallsSelf.

self assert: (spyedObject verify: #accesorWhichReturnsValue).

有什么建议吗?

3 个答案:

答案 0 :(得分:3)

你让你的“间谍”成为一个“包装”对象,它只实现doesNotUnderstand:,然后使用become:将其交换为真实对象。

将为所有邮件调用间谍的doesNotUnderstand:方法,然后您可以记录其参数(它是一个Message对象)并将其发送到原始对象。

如果您在Smalltalk图片中浏览doesNotUnderstand:的实施者,您可以找到几个示例(例如,在Squeak中有MessageCatcherObjectViewer)。

答案 1 :(得分:3)

您可以使用ObjectsAsMethodsWrapper library直接打包RealObjectUnderTest的{​​{1}}。这提供了一个方便的API来安装和删除包装器,以及一些方便的预定义包装器。

这些将拦截自发送,因为包装器安装在真实对象的方法字典中,因此可以在将消息传递给基础CompiledMethod之前对消息执行任意更改。

虽然my example显示了如何在不触及源代码的情况下记住方法调用,但它应该为您提供模拟方法调用所需的基本知识。

这种特殊技术确实有一个限制:它拦截自身发送到类本身定义的消息。因此,如果CompiledMethod子类Foo并且您在Bar上安装包装器,则不会拦截构成Foo协议一部分的消息(当然,除非您将其包装起来) )。

不能拦截骚扰或Pharo图片中的BarifTrue:ifFalse:或类似消息(也可能在GNU Smalltalk中),因为它们是不是消息发送:编译时转换内联这些消息发送到跳转字节码。 (消息发送的错觉相对有说服力,因为timesRepeat知道如何将字节码重新转换回Decompiler或其他任何内容。)

答案 2 :(得分:2)

Smalltalk没有用于拦截发送给自己的内置机制,因此您将不得不诉诸异国情调的措施。

最简单的方法可能是从原始对象中动态窃取方法。 Bert建议,你的间谍将使用#become:替换目标。然后,当间谍收到一条消息,而不是将消息转发给原始对象时,你将在其类中查找选择器并以间谍作为接收者执行它。对任意接收者执行编译方法的机制将因方言而异。在Squeak中,它是CompiledMethod类>> receiver:withArguments:executeMethod:。

正如我所说,这是非常奇特的 - 你需要在创建它时为间谍生成一个自定义类,并确保它具有与目标对象相同的结构,以便编译后的方法能够正常工作。新的接收器。您还必须将目标的状态复制到间谍中,并在您进行间谍活动时将其复制回原始对象。最后,你必须找到一个存放目标的地方,因为间谍的状态必须与目标的状态相匹配。这一切都可行,但并不简单。您将动态生成类和方法,这需要对系统的低级理解。