Sham - Lightweight Factories for Ruby on Rails Testing
In general test-driven development in Rails couldn’t be easier, but while writing your tests you’ll eventually run into the case where you need to create a valid model so that you can test some extended functionality. Take a look at this RSpec test.
The problem with this approach is that if you modify the validations on Item
,
Cart
or User
you’ll end up going back and adjusting your test. For example,
if you required that items had a quantity that was positive, you would have to
go back and add a quantity parameter every time you create an item - major pain
in the ass. Not to mention your tests start to become quite wordy.
Fortunately there are a couple of gems that are available for creating factories. Factories are “minimally valid models” that can be reused to create valid models. I have experimented with a couple of options, like factory girl or machinist. But I ran into issues with sequences (sequentially generated attributes that are used to avoid validation errors) or I disliked the way the gems and factories were setup and used in my tests. This led me to create my own lightweight factory gem called sham.
Here’s how you can get started using Sham.
1. Install the Sham Gem.
Or add it to your Gemfile:
And re-install your Bundle:
2a. If you are using RSpec or Test::Unit, enable Sham in your test.rb file.
2b. If you are using Cucumber, enable Sham in your features/support/env.rb file.
3. Create factories with your default attributes.
4. Write Your Tests.
Much Simpler! Here are a couple things to keep in mind:
You can override attributes.
You can override any attributes that have been defined in a sham. For example, either of the following is perfectly acceptable:
You can create random strings.
Sham.string!
will generates a random string that can be used as a filler or to
avoid validation issues. For example if there is a requirement that your
usernames are unique, you can use Sham.string! to avoid users having the same
username.
Normally, this would fail because the two users would have the same username:
But, this will not fail because each username will be globally unique:
You can nest shams.
Sham::Base.new(User)
tells Sham that when it’s creating a model it should
create a User sham if one is not specified. That means that either of these two
calls is valid:
Check out the documentation for additional examples and features. Feel free to file an issue if something is not working as you would expect. There’s much more you can do with this gem and while I do plan on continuing to develop features, they will remain lightweight and simple.