Stubby is a lightweight and fast stubbing framework that was
- designed to help with the repetitive work of setting up stub scenarios for specifying/testing and
- optimized for speed and ease of use.
Stubby does not track references to stubbed methods on real objects (like
stub! method does it which is used by
stub_model & co).
Instead it dynamically creates a Ruby class for every stub object once. These classes will then be instantiated for every specification (or test). This circumvents the overhead of adding, tracking and removing lots of methods and can - depending on your stubs and specs - result in significant speed improvements.
Stubby comes with a slick DSL to make it easy to describe stub setups. Basically there are three main statements:
define Model &block- defines a stub base class that camouflages as a model class
instance :key, methods- defines a concrete stub that inherits from a stub base class and will be instantiated on request
scenario :key &block- defines a code block that can be executed from your spec (e.g. in before :each blocks)
Things are pretty much readable and self-explaining:
define Site do methods :save => true, :destroy => true, :active? => true instance :homepage, :id => 1 instance :customer, :id => 2, :active? => false end
This creates a stub base class that camouflages as your
Site model (i.e. it
behaves as this model when methods like
=== etc. are sent to it).
For the Site stub base class there are a couple of methods defined (stubbed)
that you can call in your specs and that will return the given values (like
save will return
It also defines two stub instances for this class that can be accessed through
:customer. These both respond to the
id method. The
:customer instance additionally overwrites the
active? value that's present
in the base class.
You can access these stubs from
- the stub base class definition block (like the one above)
- scenario blocks (see below)
- your specs
To access them you can use the following method apis. With the Site stub base class above being defined:
stub_site # returns the first defined stub instance (i.e. :homepage) stub_site(:homepage) # returns the :homepage stub instance stub_sites # returns an array with all defined stub instances stub_sites(:homepage) # returns an array containing the :homepage stub instance
You can also use the
lookup(:site, :homepage) method in the same way in case
you need it.
Also, note that you can use these accessors from within the stub base class definition, too:
define Site do instance :homepage, :next => stub_site(:customer) instance :customer, :next => stub_site(:homepage) end
This creates a method
next on both instances which return the respective
For convenience you can pass arrays of method names as keys of method hashs:
define Site do methods [:save, :destroy, :active?] => true # same result as above end
To make stubbing of more complex ActiveRecord models easier there are also the following helpers:
define Site do has_many :sections, [:find, :build] => stub_section, :paginate => stub_sections end
This has the expected effects. With this definition in place the following lines will now all return the same section stub object:
stub_site.sections.first stub_site.sections.find stub_site.sections.build stub_site.sections.paginate.first
Watch the first of these lines! As you can see the sections
will automatically be populated with the array stub_sections if you don't
specify anything explicitely. I.e. that's the same as:
define Site do has_many :sections, stub_sections, [:find, :build] => stub_section, :paginate => stub_sections end
If you want the sections array to contain something else you can specify it:
define Site do has_many :sections, stub_sections(:root), [:find, :build] => stub_section, :paginate => stub_sections end
Finally there are also the
belongs_to statements which behave
in the expected manner:
define Site do has_one :api_key belongs_to :user end
belongs_to statement implicitely defines the
user_id method so that it
returns the id defined for the
A scenario is simply a code block that can be accessed and executed from your specs. You can define a scenario like this:
scenario :default do @site = stub_site Site.stub!(:find).and_return @site end
You can then invoke this scenario from your Spec like so:
before :each do scenario :default # pass as many scenario names as you like end
Because the scenario code block is evaluated in the scope of the before :each block the @site instance variable is then accessible from within your specs.
Install Stubby as a usual Rails plugin and add the following line somewhere
in your specs where it gets executed. The best place for this is probably
This requires the stubby code and loads the stub definition files once.
Stubby assumes that you have on directory where you store your stub definition files (and nothing else). A stub definition file is just a ruby file that contains all or some of your stub base class and scenario definitions like described above. You can structure your files as you like.
By default Stubby assumes that you
- use a directory named
- that is located beneath the directory of your
- that starts requires the stubby code
Stubby then tries to guess the correct directory location. If it gets things wrong or if you want to use a different setup you can specify the directory where it looks for your stub definitions files like this:
Stubby.directory = 'path/to/stubs'
Be aware that Stubby is a framework for plain stubs. That means that it doesn't make a single attemp of looking at your model classes and trying to mimick their behaviour. This is different from what other solutions do (which might instantiate full functional model objects, like fixtures, or at least mimick the model's attributes or similar).
Thus you need to define every single method that your specs call. Personally I feel that this is an advantage because it makes visible what portions of the code my specs actually run (and what they shouldn't). But your mileage may vary.
Also, with Stubby it is currently not possible:
- define class methods in stub base classes. You still need to stub class methods manually. The scenario block is a good place to stub common methods though.
- stub methods in a way so that they return different results depending on a certain parameter passed. You can still use RSpec
stub!(:method).with(:param).and_return(result)for that though.
If you want to help me with these, please let me know :)
Also, this code is brand new. Consider this an alpha release. I have written
this library to clean-up and speed-up the 1000+ specs of a project that I'm
currently working on. For me Stubby works quite well in this project's specs
and it runs these specs in less than 30 seconds (which is less then 50% of
what a solution with
That doesn't mean that there aren't any major bugs or problems though. Please send me your pull requests, patches or just bug requests if so.
There are a couple of similar solutions that aim at the same problem but all go down different routes. I highly recommend to have a look at:
- Fixture Scenarios by Tom Preston-Werner, extends Rails' native fixtures
- Model Stubbing by Rick Olson, creates in-memory versions of your models
- Exemplar by Piers Cawley
I was told that in Canada a "stubby" is a certain kind of beer bottle. The Wikipedia page lists a couple of advantages that perfectly match the reasons why I've written this library in the first place:
- easier to handle
- chills faster
- less breakage
- lighter in weight
- less storage space
- lower center of gravity
Now, given that I've thought of this name before I knew this page I think that's pretty funny and a perfect name for the library.
Also, I'm not a native English speaker but I've been informed on #rubyonrails that "Stubby" might also have some sexual connotations in some parts of the English speaking world.
Authors: Sven Fuchs