Create a gist now

Instantly share code, notes, and snippets.

Testing ruote participants with rspec

Testing participants

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 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 propper 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

In order for the storage participant to function propperly, 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, 'wfid' => 'some-wfid-could-be-anything', 'engine_id' => 'engine' },
  'fields' => { ... }
)

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.
  • These code examples are RSpec specific and assume the use of Mocha as a mocking framework.
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 "should taste well with the chicken" 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.stringify_keys! # if you have Rails &| ActiveSupport
if opts['params']
opts['fields']['params'] ||= {}
opts['fields']['params'].merge(opts.delete('params'))
end
Ruote::Workitem.new(opts.merge(
'fei' => {
'expid' => 0,
'wfid' => engine.context.wfidgen.generate,
'engine_id' => 'engine',
}
))
end
end
RSpec.configure do |config|
config.include RuoteHelpers
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment