0

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
Steven L.
  • 2,045
  • 3
  • 18
  • 23
  • This question has been asked before here: http://stackoverflow.com/questions/23937758/rspec-failing-error-expected-false-to-respond-to-false – Steven L. Jan 04 '15 at 16:00

1 Answers1

2

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:

  1. before
  2. test1
  3. before
  4. test2
  5. before
  6. 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 the before 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 empty Dictionary 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:

before(:all) do
  @d = Dictionary.new
end

In that case, you'd be right; the test would be working with the same Dictionary as all the previous tests, and, consequently, should expect true.

Tutleman
  • 740
  • 8
  • 15
  • Thanks for your help. I did not know how before worked in RSpec. The reason I am getting a problem at all is because I am using RSpec 3.0 when the tests were wrote with another version. – Steven L. Jan 04 '15 at 16:01
  • @user245185 You're absolutely right. I didn't mention it, because I didn't know what version you were using, and because it didn't seem important to your question, but, in RSpec 3.0, the tests should be written as `be_truthy` and `be_falsey`, respectively. – Tutleman Jan 05 '15 at 01:44