Skip to content
Simple cross process stubbing
Find file
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.
lib Use Rails.root instead of RAILS_ROOT.
spec Fixed incompatibility with 1.9.2.
tmp Add logging capability & relocated development working cache file.
.gitignore Add logging capability & relocated development working cache file.
HISTORY.txt Version bump to 0.2.4
VERSION Version bump to 0.2.4
cross-stub.gemspec Version bump to 0.2.4


CROSS-STUB makes cross process stubbing possible !!


Existing mocking/stubbing frameworks support only stubbing in the current process. This is OK most of the time. However, when running cucumber integration test suite in another process, these in-process stubbing frameworks simply doesn't help. Eg. I want to always return a timing that should be a Sunday, how do I do that when running cucumber using selenium, culerity, steam, blah, blah driver? It doesn't seem straight-forward me.

(Let's not argue whether stubbing should be encouraged. It is an itch, the poor itch needs to be scratched.)

Getting Started

It's hosted on

$ gem install cross-stub

Setting Up

1. Rails-3.*

$ rails g cucumber:install
$ rails g cross_stub

2. Rails-2.*

$ ./script/generate cucumber
$ ./script/generate cross_stub

3. Others (back to basics)

# In the test setup method:
CrossStub.setup :file => <CACHE_FILE>

# In the test teardown method:

# Find an entry point in your target application, eg. in a server, the
# point where all request handling starts:
CrossStub.refresh :file => <CACHE_FILE>

For a full list of available cache stores, scroll down to take a look at the 'Cache Stores' section.

Using It

Cross-stubbing is simple:

1. Simple returning of nil or non-nil value:

1.1. Class method:

class Someone
  def self.say

Someone.say # yields: nil

Someone.xstub(:say => 'HELLO')
Someone.say # yields: 'HELLO'

1.2. Instance method:

Someone.xstub(:say, :instance => true) # yields: nil

Someone.xstub({:say => 'HELLO'}, :instance => true) # yields: 'HELLO'

2. If a stubbed method requires argument, pass xstub a proc:

2.1. Class method:

Someone.xstub do
  def say(something)
    'saying "%s"' % something

Someone.say('HELLO') # yields: 'saying "HELLO"'

2.2. Instance method:

Someone.xstub(:instance => true) do
  def say(something)
    'saying "%s"' % something
end'HELLO') # yields: 'saying "HELLO"'

3. Something more complicated:

something = 'hello'
Someone.xstub do
  def say
    'saying "%s"' % something

Someone.say # failure !!

The above fails as a result of undefined variable/method 'something', to workaround we can have:

Someone.xstub(:something => 'HELLO') do
  def say
    'saying "%s"' % something

Someone.say # yields: 'saying "HELLO"'

Cache Stores

Cache stores are needed to allow stubs to be made available for different processes. The following describes all cache stores available:

1. File

# Setting up (current process)
CrossStub.setup :file => '<CACHE_FILE>'

# Refreshing (other process)
CrossStub.refresh :file => '<CACHE_FILE>'

2. Memcache (requires memcache-client gem)

# Setting up (current process)
CrossStub.setup :memcache => 'localhost:11211/<CACHE_ID>'

# Refreshing (other process)
CrossStub.refresh :memcache => 'localhost:11211/<CACHE_ID>'

3. Redis (requires redis gem)

# Setting up (current process)
CrossStub.setup :redis => 'localhost:6379/<CACHE_ID>'

# Refreshing (other process)
CrossStub.refresh :redis => 'localhost:6379/<CACHE_ID>'

Adding new store is super easy (w.r.t testing & actual implementation), let me know if u need more :]


  1. CrossStub uses ruby's Marshal class to dump & load the stubs, thus it has the

same limitations as Marshal (pls note abt a 1.9.1 specific marshal bug at

  1. Under the hood, CrossStub uses Sourcify ( for

extracting the methods defined within the proc, u may wish to read Sourcify's gotchas to avoid unnecessary headaches.


  1. Is there any better serialization strategy instead of the current Marshal load/dump?


Written since 2009 by:

  1. NgTzeYang, contact ngty77[at]gmail[dot]com or

  2. WongLiangZan, contact liangzan[at]gmail[dot]com or

Released under the MIT license

Something went wrong with that request. Please try again.