[rspec-users] [Q] testing WIN32OLE_EVENT callbacks

Matt Wynne
Thu, 27 Aug 2009 17:22:42 -0000

On 27 Aug 2009, at 17:02, Chuck Remes wrote:

>
> On Aug 27, 2009, at 10:12 AM, Matt Wynne wrote:
>
>>
>> On 26 Aug 2009, at 19:57, Chuck Remes wrote:
>>
>>> I''m trying to setup some specs (really just assertions)
that
>>> verify some callbacks are executed in response to COM events. In
>>> the WIN32OLE_EVENT class you may subscribe to a COM event and have
>>> it delivered to you for processing. Syntax looks like:
>>>
>>>            event_handler.on_event(''StartEvent'')
do |*args|
>>>              do_start_event args
>>>            end
>>>
>>> The #do_start_event method is a member of the containing class.
>>> The only way I can confirm that it is being called is to set an
>>> expectation on the class under test. I''ve always noted
that
>>> mocking/stubbing the class under test is rather bad form.
>>> Regardless, I don''t know how to "mock" the
delivery of an event
>>> anyway so this may all be moot.
>>>
>>> I searched the list archives for ''win32ole'' back
to early 2007 and
>>> came up with 0 hits. Any suggestions on how to tackle this?
>>
>> This doesn''t really have much to do with the specific
technology
>> you''re dealing with, it''s an issue about separating
the layers so
>> you can introduce fakes where you need to in order to test your own
>> code.
>>
>> Can you use dependency injection (or some other trick) to swap in a
>> fake event_handler into the code you''ve quoted above? If so,
you
>> can then get your fake to simulate the behaviour of the
>> event_handler and exercise the code you want to actually test.
>>
>> There are a couple of good books on these sort of techniques:
>> http://xunitpatterns.com/
>>
http://www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052
>>
>> does that help?
>
> It does somewhat. I will definitely be looking at that XUnit
> patterns book.
>
> To answer an earlier question, I *can* pass in a mock for
> event_handler. However, I fail to see how I can use that in the
> example above to test that the statements in the block are executed.
>
> def bar event_handler
>  event_handler.on_event(''StartEvent'') do |baz, qux|
>    @obj1.method1 baz, qux
>  end
> end
>
> Let''s assume that method1 and method2 are acting upon other
objects
> that I can also mock via DI. How can I generate the
''StartEvent'' so
> that the block above is executed? BTW, the layers are as separated
> as they are going to get. In the WIN32OLE_EVENT class you *must*
> pass a block as shown above. I don''t know how to get that block to
> execute in my specs without actually connecting to the COM service
> and getting it to generate that ''StartEvent''. Do you know
of another
> way?
>
> it "should execute the block upon receipt of event
''StartEvent''" do
>  handler = mock("event handler")
>  handler.should_receive(:on_event).with(''StartEvent'') #
necessary?
>
>  obj1_mock = mock("obj1")
>  obj1.should_receive(:method1)
>
>  foo = Foo.new
>  foo.obj1 = obj1
>
>  foo.bar
>  # callback has been set; how do I generate the
''StartEvent''
>  # to run that block?
>  foo.???
> end

So I''m not sure if you can simulate a block being yeilded with
RSpec''s
mocks, it''s probably possible but I think you''d be simpler
using a
hand-made fake:

class FakeEventHandler
   def initialize(*args_to_yield)
     @args_to_yield = *args_to_yield
   end

   def on_event(event_name)
     yield *@args_to_yield
   end
end

Something like that.

Point being, a test-double (fake) doesn''t always have to be a mock
object.