Why a gem needs its own tests
A lot of Rails plugins and gems lean on the host app’s test suite. That makes them fragile.
With no tests of its own, a gem is hard to contribute to, hard to trust, and tied to one app’s lifecycle. Update the parent app and the gem can quietly break.
A gem should prove it works on its own. Here are four ways to test one, from lightest to heaviest.
1. Plain Ruby, no Rails
If the gem barely touches Rails, test it as a standalone Ruby module.
# lib/lorem_ipsum.rb
module LoremIpsum
TEXT = "Lorem ipsum dolor sit amet, consectetur " +
"adipisicing elit, sed do eiusmod tempor " +
"incididunt ut labore et dolore magna aliqua. " +
"Ut enim ad minim veniam, quis nostrud " +
"exercitation ullamco laboris nisi ut aliquip " +
"ex ea commodo consequat. Duis aute irure " +
"dolor in reprehenderit in voluptate velit " +
"esse cillum dolore eu fugiat nulla pariatur. " +
"Excepteur sint occaecat cupidatat non proident, " +
"sunt in culpa qui officia deserunt mollit anim " +
"id est laborum."
def lorem_ipsum(words = nil)
if words
TEXT.split[0..words - 1].join(' ') + "."
else
TEXT
end
end
end
It tests in isolation because it never touches Rails:
# test/test_helper.rb
require 'rubygems'
require 'test/unit'
require 'shoulda'
require 'lorem_ipsum'
No Rails dependencies, a simple setup, easy for others to contribute to.
2. Load only the Rails parts you need
If the gem uses specific Rails components, load just those instead of the full stack:
# test/test_helper.rb
require 'rubygems'
require 'active_record'
require 'shoulda'
require 'slugalicious'
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
Here is a Slugalicious test that only needs ActiveRecord:
# test/slugalicious_test.rb
require 'test_helper'
class SlugaliciousTest < ActiveSupport::TestCase
should validate_presence_of(:slug)
should allow_value("example-slug").for(:slug)
end
Lighter than booting all of Rails, and it keeps the test focused on the one component you depend on.
3. Embed a full Rails app (only if you must)
Some plugins touch the entire stack: models, controllers, routes, and views. Then you embed a small Rails app inside the gem’s test suite.
Avoid this unless you truly need it.
The grunt work:
-
Generate a base skeleton and a test app:
cd blawg/test rails new rails_root -
Point it at SQLite:
# blawg/test/rails_root/config/database.yml development: adapter: sqlite3 database: db/development.sqlite3 test: adapter: sqlite3 database: db/test.sqlite3 -
Link the plugin to the test app:
# blawg/test/rails_root/config/environment.rb Rails::Initializer.run do |config| config.plugin_paths = ["#{Rails.root}/../../../"] config.plugins = [:blawg] end
Now the test app can load and exercise the plugin.
Running tests
Write normal unit and functional tests in that app. Testing a Post model might look like this:
# blawg/test/rails_root/test/unit/post_test.rb
require 'test_helper'
class PostTest < ActiveSupport::TestCase
should have_db_column(:title)
should have_db_column(:body)
should validate_presence_of(:title)
should validate_presence_of(:body)
end
The reality
If I had to bet, 90% of Rails developers have never tried to code a gem. They know how to use Rails, but have never really written Ruby.
And maintaining a gem is maintaining Ruby code. That is where a lot of devs struggle.
Have you shipped your own gem or plugin yet?