Testing in Rails is both hard and easy. Hard because it takes as much time as coding does, but easy because there are many tools that make the most complicated things straightforward. There are so many tools though, that sometimes it is hard to choose. Neat paradox.
When setting up testing for my app, I did not find in one place a comprehensive explanation of the Rails testing process as a whole. This is an attempt to fix the situation.
Why testing?
The issue is more what and when to test. Michael Hartl has an introduction to testing in his Ruby on Rails tutorial, which describes why testing and the kinds of tests he is likely to do. In summary,
- first test the controllers and the models, in other words
unit testing
;
- second test the functionality across models, views and controllers, the
integration tests
;
- and third, the views, but if they are likely to change, they can be skipped.
He also mentions the importance of writing regression tests
on bugs found and having tests in place before any refactoring.
RSpec testing infrastructure
Set up
When a new Rails project is created with default settings, a test
directory is created, coupled to work with minitest
. From the word GO, I started using RSpec
(http://rspec.info/, since I used the Ruby on Rails tutorial mentioned above which used RSpec
as testing framework. I believed it has changed since.
I did not want that default directory, as I wanted the RSpec set up.
By issuing the command
rails g controller StaticPages about --no-test-framework
the test files related to the StaticPages controller
will not be created. To create the right files, RSpec must be installed. That is done by including its gem in the Gemfile. RSpec
takes advantage of a series of helpers to run tests automatically. The gems are specified following:
group :development, :test do
gem 'rspec-rails'
gem 'factory_girl_rails'
end
group :test do
gem 'selenium-webdriver', '2.35.1'
gem 'faker'
gem 'capybara', '>= 2.2.0'
gem 'guard-rspec'
gem 'launchy'
end
and then run bundle.
In my opinion, the most interesting about Rails testing, is the interaction with databases and RSpec
plus helpers makes this easy.
By default, every Rails application has three environments: development, test, and production. The database for each one of them is configured in config/database.yml
.
http://guides.rubyonrails.org/testing.html
That fact is simply brilliant, as the development database is NOT the same as the test database. One can create automatically hundreds of records to test for specific features in isolation.
To run any testing, the databases need to be created, so run
rake db:create:all
which will create the databases which do not exist and inform you of the ones already created. The information on the databases description is taken from the file config/database.yml
, which should include the information about test, development and production databases. Make sure that the three are name differently!
Then run
rails generate rspec:install
which generates the following configuration files:
.rspec
spec/spec_helper.rb
spec/rails_helper.rb
All tests and helpers will reside in the spec directory
. This is directory where RSpec
searches for the tests to run.
NOTE: After making changes to any of the models in development, you have to migrate the changes to the test database as well by running
rake db:migrate RAILS_ENV=test
With this, the testing infrastructure is set up.
Syntax
RSpec uses mainly the words “describe” and “it” so we can express concepts like a conversation:
“Describe an order.”
“It sums the prices of its line items.”
The describe
method creates an ExampleGroup
. Within the block passed to describe
you can declare examples using the it
method. Under the hood, an example group is a class in which the block passed to describe
is evaluated.
The broad syntax of the test is as follows:
describe Object do
it "Descriptive message of the test" do
code with expectations
end
end
Each “it” line only expects one example. Best practice is to test one thing at a time to make it simple to find errors. Although the descriptive message is technically optional, omitting defeat the purpose of individual testing. Previous RSpec
examples had the “should” beginning the message, however that just clutters the output. A direct verb suffices.
Actual testing
Now to writing the tests. But where to start? With how to run a test.
To run a test, use the command
rspec
from the root directory of the app. If used alone, it will run all the tests found in the spec
directory. You can also specify a directory or a filename including its path with respect to root. RSpec will run all tests found in that directory in the first case, or just the file specified in the second.
The testing framework automatically creates directories to sort out the tests. My spec
directory looks like this:
controllers/ factories/ models/ requests/ support/
helpers/ rails_helper.rb spec_helper.rb views/
Next is what to test: unit testing, integrations testing, views, regression testing.
Unit testing: Models
Models are the building blocks of the application. They are also easier to test since their behaviour should be well defined in any application. I considered them to be first priority to test.
Everyday Rails
The blog Everyday Rails considers the following to be essential model tests:
- the
factory
should generate a valid object
- data validation
- class and instance method
Full text deployed in a post called Testing with RSpec in Rails, Part 2, Models.
Unit testing: Controllers
Post in development.
Integration tests
Post in conception.
This should test the functionality across models, views and controllers.
View’s tests?
Will I create a post?
Code in the views tend to change often, so there are different schools of thought on whether to test or not.
Regression tests
Automatic test runs with Guard
Guard
runs the test suite upon the detection of a modification of file in the spec directory or as specified in the Guardfile
. It also sets the testing environment just once, speeding up the running of subsequent tests. To set up (gem already included in the Gemfile) run:
bundle exec guard init
which creates the Guardfile
describing how and when Guard is to run. In his tutorial, Guard: Michael Hartl’s Rails tutorial, he explains the set up in more detail, although using minitest
instead of RSpec
.
To start guard just type in a terminal
$ guard
It will create a shell and guard
will start listening to any changes in the spec directory or any other file specified in the Guardfile
. The Guarfile
created in the set up is a very good starting point.
Type enter in the shell to run all the tests in the spec directory. Ctl-D
to exit.
(Explanation on Guard
needs expansion.)