This guide will show you new ways of testing. By referring to this guide, you will be able to:
Slides - use arrow keys to navigate, esc to return to page view, f for fullscreen
Rspec is the second testing framework that is commonly used with Ruby on Rails projects. According to ruby-toolbox.com it is used more often than the (built in) Minitest.
Rspec replaces minitest in all aspects of rails, including in scaffolds.
The first things you hav to know to get started:
spec/*
(not test/*
)rspec <filename>
or rspec <filename>:<linenumber>
on the command linerake spec
(yes, sometimes you need the r
in rspec, and other times you leave it out.)
A file can contain multiple test. You use describe
and it
to
structure the file. The arguments for describe
and it
are
used to describe the test in case of failure:
describe Game do
describe "#score" do
it "returns 0 for all gutter games" do
game = Game.new
20.times { game.roll(0) }
game.score.should == 0
end
end
end
The message when this test fails reads:
Failures:
1) Game#score returns 0 for all gutter games
Failure/Error: game.score.should == 0
expected: 0
got: 1 (using ==)
# ./x_spec.rb:15:in `block (3 levels) in <top (required)>'
It is a convention to actually use the Class under test as
the argument of describe
.
Inside the test you can use ruby and rails. Instead of minitest's assertions you formulate expectations with "should" (outdated) or "expect" (current):
game.score.should == 0
expect(game.score).to eq(0)
There are two ways of writing matchers:
foo.should == bar
foo.should eq(bar) expect(foo).to eq(bar)
foo.should_not eq(bar) expect(foo).not_to eq(bar)
foo.should be < 10 expect(foo).to be < 10
"a string".should_not =~ /a regex/
expect("a string").not_to match(/a regex/)
lambda { do_something }.should raise_error(SomeError)
expect { something }.to raise_error(SomeError)
describe Post do
context "with 2 or more comments" do
it "orders them in reverse chronologically" do
post = Post.create!
comment1 = post.comments.create!(:body => "first comment")
comment2 = post.comments.create!(:body => "second comment")
post.reload.comments.should == [comment2, comment1]
end
end
end
feature "Widget management" do
scenario "User creates a new widget" do
visit "/widgets/new"
fill_in "Name", :with => "My Widget"
click_button "Create Widget"
page.should have_text("Widget was created.")
end
end
the magic behind cucumber:
Given /the following movies exist/ do |movies_table|
movies_table.hashes.each do |movie|
Movie.create( movie )
end
end
Then /^the director of "([^"]*)" should be "([^"]*)"$/ do |title, director|
m = Movie.find_by_title( title )
m.should_not be_nil
m.director.should == director
end
According to Meszaros(2007)
VCR
describe "sets done_at" do
t = Todoitem.create!( :text => "write" )
t.done = true
t.save!
t.reload
t.done_at.should == Time.now
end
# in your test:
t.done_at.should be_the_same_time_as( Time.zone.now )
# in spec_helper.rb:
RSpec::Matchers.define :be_the_same_time_as do |expected|
match do |actual|
expected.to_i == actual.to_i
end
failure_message_for_should do |actual|
"expected that #{actual} (#{actual.to_i} in seconds) would be a the same as #{expected} (#{expected.to_i} in seconds)"
end
end
it "sets done_at" do
t = Todoitem.create!( :text => "write" )
Timecop.freeze do
t.done = true
t.save!
t.reload
t.done_at.should == Time.now
end
end
var page;
page = require("webpage").create();
page.open("http://localhost:3000", function(status) {
var string;
string = page.evaluate(function() {
return $("h1").text();
});
console.log("Title: " + string);
return phantom.exit();
});
var page;
page = require("webpage").create();
page.open("http://localhost:3000", function(status) {
var string;
string = page.evaluate(function() {
return $("h1").text();
});
console.log("Title: " + string);
return phantom.exit();
});
with capybara and poltergeist
Clone this app and try out your new testing strategies!