I am working through TestFirst.org's Ruby tutorial. I am currently on the "Dictionary" lesson. I am stuck on the keyword example. Here is the Ruby document as well as the RSpec file.
The reason I am confused is that I am not sure if the example is correct. If you follow the RSpec file, the examples before "can check whether a given keyword exists" uses the add method to add "fish" to the dictionary. Then, in the example that is given me an error,the include? method (which I need to implement) should return false.
Should it return false? Didn't we add "fish" in the examples above? Or am I missing something?
This is my Ruby program:
class Dictionary
def initialize
@entries = {}
end
def entries
@entries
end
def keywords
@entries.keys
end
def add(pairings)
if pairings.is_a?(String)
@entries[pairings] = nil
else
pairings.each do |word, definition|
@entries[word] = definition
end
end
end
def include?(keyword)
@entries.keys.include?(keyword)
end
end
The following is the RSpec file: # # Topics # # * Hash # * Array # * instance variables # * regular expressions #
require 'dictionary'
describe Dictionary do
before do
@d = Dictionary.new
end
it 'is empty when created' do
@d.entries.should == {}
end
it 'can add whole entries with keyword and definition' do
@d.add('fish' => 'aquatic animal')
@d.entries.should == {'fish' => 'aquatic animal'}
@d.keywords.should == ['fish']
end
it 'add keywords (without definition)' do
@d.add('fish')
@d.entries.should == {'fish' => nil}
@d.keywords.should == ['fish']
end
it 'can check whether a given keyword exists' do
@d.include?('fish').should be_false
end
it "doesn't cheat when checking whether a given keyword exists" do
@d.include?('fish').should be_false # if the method is empty, this test passes with nil returned
@d.add('fish')
@d.include?('fish').should be_true # confirms that it actually checks
@d.include?('bird').should be_false # confirms not always returning true after add
end
it "doesn't include a prefix that wasn't added as a word in and of itself" do
@d.add('fish')
@d.include?('fi').should be_false
end
it "doesn't find a word in empty dictionary" do
@d.find('fi').should be_empty # {}
end
it 'finds nothing if the prefix matches nothing' do
@d.add('fiend')
@d.add('great')
@d.find('nothing').should be_empty
end
it "finds an entry" do
@d.add('fish' => 'aquatic animal')
@d.find('fish').should == {'fish' => 'aquatic animal'}
end
it 'finds multiple matches from a prefix and returns the entire entry (keyword + definition)' do
@d.add('fish' => 'aquatic animal')
@d.add('fiend' => 'wicked person')
@d.add('great' => 'remarkable')
@d.find('fi').should == {'fish' => 'aquatic animal', 'fiend' => 'wicked person'}
end
it 'lists keywords alphabetically' do
@d.add('zebra' => 'African land animal with stripes')
@d.add('fish' => 'aquatic animal')
@d.add('apple' => 'fruit')
@d.keywords.should == %w(apple fish zebra)
end
it 'can produce printable output like so: [keyword] "definition"' do
@d.add('zebra' => 'African land animal with stripes')
@d.add('fish' => 'aquatic animal')
@d.add('apple' => 'fruit')
@d.printable.should == %Q{[apple] "fruit"\n[fish] "aquatic animal"\n[zebra] "African land animal with stripes"}
end
end
The test is correct. What you're missing is how
before
works.In RSpec,
before
hooks without arguments (or with the argument:each
) specify steps to be taken before each test is run, not (as one might think) steps that are run before the entire set of tests.In other words, the
before
hook runs before the first test, then before the second test, then before the third test, and so on.The execution order looks like this:
before
test1
before
test2
before
test3
In your case, while it's true that in
it 'add keywords (without definition)' do
you add'fish'
to@d
, that doesn't affect the next test, because thebefore
hook runs between the two of them, and replaces@d
with a new, empty,Dictionary
object. As a result, when testing#include?
, you're checking if an emptyDictionary
contains'fish'
, which it doesn't.If, instead, you wanted take some steps once, before any of the tests were run, you would replace your
before
block with:In that case, you'd be right; the test would be working with the same
Dictionary
as all the previous tests, and, consequently, should expecttrue
.