Set up RSpec to test a gem (not Rails)

49,277

Solution 1

I've updated this answer to match current best practices:

Bundler supports gem development perfectly. If you are creating a gem, the only thing you need to have in your Gemfile is the following:

source "https://rubygems.org"
gemspec

This tells Bundler to look inside your gemspec file for the dependencies when you run bundle install.

Next up, make sure that RSpec is a development dependency of your gem. Edit the gemspec so it reads:

spec.add_development_dependency "rspec"

Next, create spec/spec_helper.rb and add something like:

require 'bundler/setup'
Bundler.setup

require 'your_gem_name' # and any other gems you need

RSpec.configure do |config|
  # some (optional) config here
end

The first two lines tell Bundler to load only the gems inside your gemspec. When you install your own gem on your own machine, this will force your specs to use your current code, not the version you have installed separately.

Create a spec, for example spec/foobar_spec.rb:

require 'spec_helper'
describe Foobar do
  pending "write it"
end

Optional: add a .rspec file for default options and put it in your gem's root path:

--color
--format documentation

Finally: run the specs:

$ rspec spec/foobar_spec.rb

Solution 2

Iain's solution above works great!

If you also want a Rakefile, this is all you need:

require 'rspec/core/rake_task'

RSpec::Core::RakeTask.new(:spec)

# If you want to make this the default task
task default: :spec

Check the RDoc for RakeTask for various options that you can optionally pass into the task definition.

Solution 3

You can generate your new gem with rspec by running bundler gem --test=rspec my_gem. No additional Setup!

I always forget this. It's implemented here: https://github.com/bundler/bundler/blob/33d2f67d56fe8bf00b0189c26125d27527ef1516/lib/bundler/cli/gem.rb#L36

Solution 4

Here's a cheap and easy (though not officially recommended) way:

Make a dir in your gem's root called spec, put your specs in there. You probably already have rspec installed, but if you don't, just do a gem install rspec and forget Gemfiles and bundler.

Next, you'll make a spec, and you need to tell it where your app is, where your files are, and include the file you want to test (along with any dependencies it has):

# spec/awesome_gem/awesome.rb
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
$: << File.join(APP_ROOT, 'lib/awesome_gem') # so rspec knows where your file could be
require 'some_file_in_the_above_dir' # this loads the class you want to test

describe AwesomeGem::Awesome do
  before do
    @dog = AwesomeGem::Awesome.new(name: 'woofer!')
  end
  it 'should have a name' do
    @dog.name.should eq 'woofer!'
  end
  context '#lick_things' do
    it 'should return the dog\'s name in a string' do
      @dog.lick_things.should include 'woofer!:'
    end
  end
end

Open up Terminal and run rspec:

~/awesome_gem $ rspec
..

Finished in 0.56 seconds
2 examples, 0 failures

If you want some .rspec options love, go make a .rspec file and put it in your gem's root path. Mine looks like this:

# .rspec
--format documentation --color --debug --fail-fast

Easy, fast, neat!

I like this because you don't have to add any dependencies to your project at all, and the whole thing remains very fast. bundle exec slows things down a little, which is what you'd have to do to make sure you're using the same version of rspec all the time. That 0.56 seconds it took to run two tests was 99% taken up by the time it took my computer to load up rspec. Running hundreds of specs should be extremely fast. The only issue you could run into that I'm aware of is if you change versions of rspec and the new version isn't backwards compatible with some function you used in your test, you might have to re-write some tests.

This is nice if you are doing one-off specs or have some good reason to NOT include rspec in your gemspec, however it's not very good for enabling sharing or enforcing compatibility.

Share:
49,277

Related videos on Youtube

medihack
Author by

medihack

Updated on January 25, 2021

Comments

  • medihack
    medihack over 3 years

    It is pretty easy with the added generator of rspec-rails to set up RSpec for testing a Rails application. But how about adding RSpec for testing a gem in development? I am not using jeweler or such tools. I just used Bundler (bundle gem my_gem) to setup the structure for the new gem and edit the *.gemspec manually. I also added s.add_development_dependency "rspec", ">= 2.0.0" to gemspec and did a bundle install.

    Is there some nice tutorial what to do next to get RSpec working?

    • medihack
      medihack over 13 years
      I guess I have to write one :-) ... At least there are two gems that already integrate it nicely: acts-as-taggable-on and acts_as_geocodable.
  • Attila Györffy
    Attila Györffy about 12 years
    To be fair, you should instead invoke RSpec's init command to generate the spec skeleton files rather than having to manually type them in. This would ensure compatibility with the version of RSpec that you are using: rspec --init
  • iain
    iain about 12 years
    rspec --init wasn't available when I wrote this, but good point!
  • mkon
    mkon over 10 years
    Actually I found the best way to do the requires in the spec helper is this: require 'rubygems' require 'bundler/setup' Bundler.require(:default, :development)
  • Malte
    Malte about 9 years
    Neat! However, I think your gem name should be specified with underscores instead of camel case. Otherwise Bundler creates files with upper case letters (Bundler 1.7.4)
  • Nicolas Mattia
    Nicolas Mattia about 9 years
    Bundler complained about --test=rspec, but it still asked me if I wanted to use Rspec when I ran bundler gem my_gem.
  • Misha Slyusarev
    Misha Slyusarev almost 8 years
    Is there a way to not put AwesomeGem:: before class names any time you refer to a testing object? Or when you create a new test like in your example.
  • wulftone
    wulftone almost 8 years
    Sure, you can either set your class name equal to something shorter, like Thing = AwesomeGem::Awesome or you can do the test inside a module, like module AwesomeGem; it 'stuff' do; Awesome.new ... end; end
  • Nakilon
    Nakilon over 7 years
    How exactly do @mkon's three lines of code work differently from iain's three lines of code?
  • iain
    iain over 7 years
    The lines from @mkon will require all gems in the development and test groups, while my approach is to require every gem manually. Since you need to require every gem yourself when making gems, I think it's the better/clearer approach even though it might be a bit more work.
  • Nakilon
    Nakilon over 7 years
    So both are with Bundler.setup, but one is with require and another one is with Bundler.require. I guess I get it now. Probably Bundler.require :development is exactly what I need in spec_helper.rb. Please, next time mention me by username or I'll not get the notification. Thanks.