Developing Gems and Testing

When should I use development vs testing group in gemfile for testing gems?

Gems that you run from the development environment should be present in both the development and test groups. You run things like rspec cucumber and guard from development and they run in the test environment, you need them in development to run the rake tasks and the executables.

Gems that only run while in test mode, such as capybara email_spec and launchy can exist only in the test group and still function correctly.

I hope this helps clear things up.

As a general rule, gems that are executable need to be in both. Also, if you are unsure, put it in both groups too.

Edit

If the gem you are using has generators (rails generate), it needs to be present in both test and development.

How to use 'debugger' and 'pry' when developing a gem? (Ruby)

You can add gems to your gemspec as a development dependency, like this:

Gem::Specification.new do |s|
# ...
s.add_development_dependency 'pry'
s.add_development_dependency 'debugger'
s.add_development_dependency 'rake'
end

These will only be installed when working on the gem and not when installing the gem itself.

How to run unit tests before building a ruby gem?

If you have a build process that consists of multiple tasks (e.g. testing, creating a gem, publishing a gem), and has dependencies between those tasks (e.g. the gem should only be created if the tests are successful, in order to publish the gem, it needs to be created first), you can use a build tool to automate that.

Actually, build tools can be used for much more than just building, which is why some people prefer the term task-oriented programming or (my personal favority) dependency-oriented programming for that.

The most famous of such dependency-oriented programming tools is probably make. If you are familiar with the Java ecosystem, you probably know Apache Ant and Gradle. In the Microsoft world, there is the Microsoft Build Engine (MSBuild). From the ECMAScript ecosystem, you may know Grunt or Gulp. The hot new kid on the block is Google's Bazel.

Ruby also has its own share of such tools, the most widely-used one is Rake.

A lot of Ruby libraries and tools come with their own ready-made Rake Tasks, so that you don't have to write them yourself. For your particular use case, for example, there are ready-made tasks for building gems and for running RSpec tests.

Should one include tests in a packaged gem?

There is no need to include tests in your gem files. However, if the test files are tiny (which they probably are), then it's no big deal.

For a long time, when creating a new gem with the command bundle gem mygemname, the following lines were included in the generated gemspec:

Gem::Specification.new do |s|
# ...
s.files = `git ls-files -z`.split("\x0")
s.test_files = s.files.grep(/^(test|spec|features)\//)
# ...
end

Since your tests were being included already, in the test_files, it literally didn't matter that they were included in the files, too.

However, if you run the same command bundle gem mygemname today, then instead you'll see this line in the generated gemspec file:

Gem::Specification.new do |s|
# ...
s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
# No mention of s.test_files !
# ...
end

What changed?

Well, s.test_files is now almost deprecated. There is an open issue in RubyGems to not install test_files, which has been deferred to a post-2.x RubyGems release due to 'backward compatibility'. It also used to be possible to run tests for a Gem whilst installing it, with gem install -t gemname; this feature has now been removed (see bug #25707).

This is, I believe, why you still see lots of gems with test files included in the source code. But there's no longer any point including them.

Set up RSpec to test a gem (not Rails)

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

How to test a gem that depends on Rails and uses Rails commands

A technique we use for WickedPDF is in the default rake task, before we run the tests, is to delete & generate a full Rails application in a gitignored subdirectory of the gem.

As a high-level simplified example of this Rakefile, it looks something like this:

Rakefile

require 'rake'
require 'rake/testtask'

# This gets run when you run `bin/rake` or `bundle exec rake` without specifying a task.
task :default => [:generate_dummy_rails_app, :test]

desc 'generate a rails app inside the test directory to get access to it'
task :generate_dummy_rails_app do
if File.exist?('test/dummy/config/environment.rb')
FileUtils.rm_r Dir.glob('test/dummy/')
end
system('rails new test/dummy --database=sqlite3')
system('touch test/dummy/db/schema.rb')
FileUtils.cp 'test/fixtures/database.yml', 'test/dummy/config/'
FileUtils.rm_r Dir.glob('test/dummy/test/*') # clobber existing tests
end

desc 'run tests in the test directory, which includes the generated rails app'
Rake::TestTask.new(:test) do |t|
t.libs << 'lib'
t.libs << 'test'
t.pattern = 'test/**/*_test.rb'
t.verbose = true
end

Then, in test/test_helper.rb, we require the generated Rails app, which loads Rails itself and it's environment:

test/test_helper.rb

ENV['RAILS_ENV'] = 'test'

require File.expand_path('../dummy/config/environment.rb', __FILE__)
require 'test/unit' # or possibly rspec/minispec

# Tests can go here, or other test files can require this file to have the Rails environment available to them.
# Some tests may need to copy assets/fixtures/controllers into the dummy app before being run. That can happen here, or in your test setup.

You could skip parts of Rails that aren't needed by customizing the command that generates the app. For example, your gem may not need a database at all or a lot of things by default, so you command could be customized for a simpler app. Something like this maybe:

system("rails new test/dummy --skip-active-record \
--skip-active-storage --skip-action-cable --skip-webpack-install \
--skip-git --skip-sprockets --skip-javascript --skip-turbolinks")

In the WickedPDF project, we wanted to test across a wide range of "default" Rails installs, so we don't customize the command much, but that may generate much more than what you need to test some generator tasks.

WickedPDF also tests against multiple versions of Rails with TravisCI and multiple Gemfiles, but this could also be accomplished with the Appraisal gem that Luke suggested in this thread.

Gem testing with Rspec

Here's a simple project where you can see how you'd go by doing it: multiplier

First and foremost, if you're doing the gem management by yourself, please don't, use helper tools like jeweler to do it for you. Install the jeweler gem (gem install jeweler) and once you have it installed, create your gem projet:

jeweler --rspec your_gem_name

With this, it's going to setup a skeleton gem that's going to have a single main file (where you would require your necessary gem files) and the spec folder.

At the spec folder there's spec_helper.rb, that's where our configuration lives, what I did was:

$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
$LOAD_PATH.unshift(File.dirname(__FILE__))
require 'rspec'
require 'multiplier'

# Requires supporting files with custom matchers and macros, etc,
# in ./support/ and its subdirectories.
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}

RSpec.configure do |config|

end

Multiplier.configure do |config| #these are the only lines I added myself
config.multiplier 4
end

So, here lives the config for our gem, but you could even do it on each spec, if you'd need it. But if you want to use a single config for all specs this is where you should place it.



Related Topics



Leave a reply



Submit