testing participants

testing participant implementations.

There are two ways to test participants: in isolation and in flow.

The “in isolation” way is to simply setup the participant and exercise it. In the “in flow” way, the participant is exercised via a transient (in-memory HashStorage) ruote engine and a minimal flow.


testing in isolation

This piece of documentation was contributed by Hartog C. de Mik

Introduction

Testing a participant is gory-detail-stuff. You must know a lot about the internals of Ruote.
Because in order to ‘test’ a participant, we have to mimic the environment the participant lives and is executed in.

Setup

Since we are isolating the participant in our experiment^W… tests we have to prevent the participant from communicating with Ruote. In order to do so we have to replace the #reply function with an own singleton.

            participant = Participant.new
            def participant.reply; end
            

The singleton-method reply just has to do … nothing (certainly not reply to the the engine!)

Create a workitem

Loading a workitem is pretty simple, just instantiate a Ruote::Workitem and load it with the proper values for your tests.

Fields
            wi = Ruote::Workitem.new( 'fields' => { 'my_field' => 'my value' } )
            
Params

To get the parameters (as given in your process definition), you will have to place them within the #fields hash.

            wi = Ruote::Workitem.new(
              'fields' => {
                'my_field' => 'my value'
                'params' => {
                  'passed_from_pdef' => 'not really, no...'
                },
              })
            

Injecting a workitem

Since we want to emulate, or mimic, the environment Ruote builds around our participants, we cannot simply call #on_workitem, we have to do what ruote does.

            it "should receive the workitem" do
              wi = Ruote::Workitem.new( 'fields' => { 'my_field' => 'my value' } )
              subject._on_workitem(wi)
              subject.workitem.should == wi
            end
            

Or, for old(er) ruote installments

            it "should consume the workitem" do
              wi = Ruote::Workitem.new( 'fields' => { 'my_field' => 'my value' } )
              subject.expects(:consume).with(wi)
              subject._consume(wi)
            end
            

Capturing results

Workitem updates

Since you have overridden reply (with nothing) you can just test participant.workitem and expect it to have the latest-greatest.

Side-effects

How you test your side-effects is up to you…

The StorageParticipant

If you have subclassed the storage participant, the rules change a little

Setup

an order for the storage participant to function properly, an instance of the ruote engine should be supplied

            engine      = Ruote::Dashboard.new(Ruote::HashStorage.new())
            participant = MyStorageParticipant(engine)
            
Load a workitem

First thing the the StorageParticipant sub-class does (or should do) in #on_workitem is call #super – this will wreak havoc, unless you make sure that you have a workitem with a (valid) fei

            workitem = RuoteWorkitem.new(
              'fei' => {
                'expid' => '0',
                'subid' => 'abc123',
                'wfid' => 'some-wfid-could-be-anything',
                'engine_id' => 'engine' },
              'fields' => {
                'age_of_the_captain' => 32 }
            )
            

Disclaimer

  • The code examples provided are not runnable out-off-the-box and are meant has a hook for you to start your work on.
  • The code examples below are RSpec specific and assume the use of Mocha as a mocking framework.

Code samples

participant implementation:

            require 'open-uri'
            
            class MyParticipant < Ruote::Participant
            
              def on_workitem
                begin
                  OpenURI.open_uri(workitem.fields['url'])
                rescue SocketError => error
                  workitem.fields['error'] = "#{error.class}: #{error.message}"
                else
                  workitem.fields['visited'] = true
                end
              end
            end
            

spec/participants/my_participant.rb

            
            require 'spec_helper'
            
            describe MyParticipant do
            
              subject { setup_participant MyParticipant }
            
              it "visits successfully the target URL" do
            
                subject._on_workitem new_workitem(
                  fields: { 'url' => 'http://ruote.rubyforge.org/' })
            
                subject.workitem.fields['visited'].should be_true
              end
            end
            

spec/support/ruote_helpers.rb

            module RuoteHelpers
            
              def engine
                @engine ||= RuoteKit.engine # (or some other setup)
              end
            
              def setup_storage_participant(klass)
            
                participant = klass.new(engine)
            
                def participant.update(workitem)
                  self.workitem = workitem
                end
            
                participant
              end
            
              def setup_participant(klass)
            
                participant = klass.new()
            
                def participant.reply
                  # catching the reply - it should not do anything
                end
            
                participant
              end
            
              def new_workitem(opts)
            
                opts = Ruote.keys_to_s(opts)
            
                if opts['params']
                  opts['fields']['params'] ||= {}
                  opts['fields']['params'].merge!(opts.delete('params'))
                end
            
                Ruote::Workitem.new(opts.merge(
                 'fei' => {
                    'expid'     => '0',
                    'subid'     => 'abc123',
                    'wfid'      => engine.context.wfidgen.generate,
                    'engine_id' => 'engine',
                  }
                ))
              end
            end
            
            RSpec.configure do |config|
              config.include RuoteHelpers
            end
            

testing in flow

TODO