我正在将我最喜欢的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).
有什么建议吗?
答案 0 :(得分:3)
你让你的“间谍”成为一个“包装”对象,它只实现doesNotUnderstand:
,然后使用become:
将其交换为真实对象。
将为所有邮件调用间谍的doesNotUnderstand:
方法,然后您可以记录其参数(它是一个Message对象)并将其发送到原始对象。
如果您在Smalltalk图片中浏览doesNotUnderstand:
的实施者,您可以找到几个示例(例如,在Squeak中有MessageCatcher
和ObjectViewer
)。
答案 1 :(得分:3)
您可以使用ObjectsAsMethodsWrapper library直接打包RealObjectUnderTest
的{{1}}。这提供了一个方便的API来安装和删除包装器,以及一些方便的预定义包装器。
这些将拦截自发送,因为包装器安装在真实对象的方法字典中,因此可以在将消息传递给基础CompiledMethod
之前对消息执行任意更改。
虽然my example显示了如何在不触及源代码的情况下记住方法调用,但它应该为您提供模拟方法调用所需的基本知识。
这种特殊技术确实有一个限制:它拦截自身发送到类本身定义的消息。因此,如果CompiledMethod
子类Foo
并且您在Bar
上安装包装器,则不会拦截构成Foo
协议一部分的消息(当然,除非您将其包装起来) )。
你 不能拦截骚扰或Pharo图片中的Bar
,ifTrue:ifFalse:
或类似消息(也可能在GNU Smalltalk中),因为它们是不是消息发送:编译时转换内联这些消息发送到跳转字节码。 (消息发送的错觉相对有说服力,因为timesRepeat
知道如何将字节码重新转换回Decompiler
或其他任何内容。)
答案 2 :(得分:2)
Smalltalk没有用于拦截发送给自己的内置机制,因此您将不得不诉诸异国情调的措施。
最简单的方法可能是从原始对象中动态窃取方法。 Bert建议,你的间谍将使用#become:替换目标。然后,当间谍收到一条消息,而不是将消息转发给原始对象时,你将在其类中查找选择器并以间谍作为接收者执行它。对任意接收者执行编译方法的机制将因方言而异。在Squeak中,它是CompiledMethod类>> receiver:withArguments:executeMethod:。
正如我所说,这是非常奇特的 - 你需要在创建它时为间谍生成一个自定义类,并确保它具有与目标对象相同的结构,以便编译后的方法能够正常工作。新的接收器。您还必须将目标的状态复制到间谍中,并在您进行间谍活动时将其复制回原始对象。最后,你必须找到一个存放目标的地方,因为间谍的状态必须与目标的状态相匹配。这一切都可行,但并不简单。您将动态生成类和方法,这需要对系统的低级理解。