I am running this portion of a test:
describe Dictionary do
before do
#d = Dictionary.new
end
it 'can check whether a given keyword exists' do
#d.include?('fish').should be_false
end
With this code:
class Dictionary
def initialize
#hash = {}
end
def add(new_entry)
new_entry.class == String ? #hash[new_entry] = nil : new_entry.each { |noun, definition| #hash[noun] = definition}
end
def entries
#hash
end
def keywords
#hash.keys
end
def include?(word)
if #hash.has_key?(word)
true
else
false
end
end
end
I don't know what I'm doing wrong, but my tests keep failing and saying this:
> 1) Dictionary can check whether a given keyword exists
> Failure/Error: #d.include?('fish').should be_false
> expected false to respond to `false?`
I am confused at the error since it seems to be giving the correct answer. I would really appreciate if someone could take a few minutes to tell me what's wrong with my code.
Thank you tons.
If you browse the RSpec Expectations 2.99 and RSpec Expectations 2.14 and search the section - Truthiness and existentialism, you will find
expect(actual).to be_true # passes if actual is truthy (not nil or false)
expect(actual).to be_false # passes if actual is falsy (nil or false)
# ...............
# ...
But of you browse RSpec Expectations 3.0 , the above method names got changed to -
expect(actual).to be_truthy # passes if actual is truthy (not nil or false)
expect(actual).to be true # passes if actual == true
expect(actual).to be_falsey # passes if actual is falsy (nil or false)
# ...........
#......
It seems you are in 3.0, and using the method which were exist prior to this version. Thus you were getting the error.
I put the code in my test.rb file as below :-
class Dictionary
def initialize
#hash = {}
end
def add(new_entry)
new_entry.class == String ? #hash[new_entry] = nil : new_entry.each { |noun, definition| #hash[noun] = definition}
end
def entries
#hash
end
def keywords
#hash.keys
end
def include?(word)
if #hash.has_key?(word)
true
else
false
end
end
end
And my spec/test_spec.rb file is -
require_relative "../test.rb"
describe Dictionary do
before do
#d = Dictionary.new
end
it 'can check whether a given keyword exists' do
#d.include?('fish').should be_false
end
end
Now I am running the code from my console, and it works :
arup#linux-wzza:~/Ruby> rspec -v
2.14.8
arup#linux-wzza:~/Ruby> rspec spec
.
Finished in 0.00169 seconds
1 example, 0 failures
Now I am changing the code in my spec/test_spec.rb file :-
require_relative "../test.rb"
describe Dictionary do
before do
#d = Dictionary.new
end
it 'can check whether a given keyword exists' do
#d.include?('fish').should be_falsey
end
end
and again run the test :-
arup#linux-wzza:~/Ruby> rspec -v
2.14.8
arup#linux-wzza:~/Ruby> rspec spec
F
Failures:
1) Dictionary can check whether a given keyword exists
Failure/Error: #d.include?('fish').should be_falsey
NoMethodError:
undefined method `falsey?' for false:FalseClass
# ./spec/test_spec.rb:9:in `block (2 levels) in <top (required)>'
Finished in 0.00179 seconds
1 example, 1 failure
Failed examples:
rspec ./spec/test_spec.rb:8 # Dictionary can check whether a given keyword exists
arup#linux-wzza:~/Ruby>
Now, they also mentioned in the 3.0.0.beta1 / 2013-11-07 changelog
Rename be_true and be_false to be_truthy and be_falsey. (Sam Phippen)
Related
The following Ruby code raises the confusing error "no id given" shown at the end. How do I avoid this problem?
class Asset; end
class Proxy < Asset
def initialize(asset)
#asset
end
def method_missing(property,*args)
property = property.to_s
property.sub!(/=$/,'') if property.end_with?('=')
if #asset.respond_to?(property)
# irrelevant code here
else
super
end
end
end
Proxy.new(42).foobar
#=> /Users/phrogz/test.rb:13:in `method_missing': no id given (ArgumentError)
#=> from /Users/phrogz/test.rb:13:in `method_missing'
#=> from /Users/phrogz/test.rb:19:in `<main>'
The core of this problem can be shown with this simple test:
def method_missing(a,*b)
a = 17
super
end
foobar #=> `method_missing': no id given (ArgumentError)
This error arises when you call super inside method_missing after changing the value of the first parameter to something other than a symbol. The fix? Don't do that. For example, the method from the original question can be rewritten as:
def method_missing(property,*args)
name = property.to_s
name.sub!(/=$/,'') if name.end_with?('=')
if #asset.respond_to?(name)
# irrelevant code here
else
super
end
end
Alternatively, be sure to explicitly pass a symbol as the first parameter to super:
def method_missing(property,*args)
property = property.to_s
# ...
if #asset.respond_to?(property)
# ...
else
super( property.to_sym, *args )
end
end
Here's my code:
class Dictionary
def entries
#entries ||= {}
end
def add(hash)
if hash.class == Hash
hash.each_pair do |k, v|
entries[k] = v
end
else
makehash = {hash => nil}
self.add(makehash)
end
#entries = entries
end
def keywords
#entries.keys
end
def include?(k)
if #entries == nil
false
elsif self.keywords.include?(k)
true
else
false
end
end
end
And here's the test I'm running it against:
require 'dictionary'
describe Dictionary do
before do
#d = Dictionary.new
end
it 'can check whether a given keyword exists' do
#d.include?('fish').should be_false
end
Now, that test will fail. However, if I change it to
it 'can check whether a given keyword exists' do
#d.include?('fish').should == false
end
then it passes.
How can I change my code so that should be_false passes instead of should == false? Thanks.
be_false matches falsey values (nil and false) and
be_true matches truthy values (other than nil or false)
From Rspec > 3.0,
be_false is renamed to be_falsey and
be_true is renamed to be_truthy
If you want to exactly match false, you should use
obj.should eq false
See the Documentation for more info about 'be' matchers
This question already has answers here:
expected true to respond to true?
(2 answers)
Closed 8 years ago.
Here's the spec:
it 'can check whether a given keyword exists' do
#d.include?('fish').should be_false
end
Here's my method:
def include?(k)
#entries.each_key do |n|
if (n==k)
return true
end
end
return false
end
If I do, say:
puts myd.include?('fish')
console prints false (correctly)
But the spec fails with:
Failure/Error: #d.include?('fish').should be_false
expected false to respond to `false?`
And I do not know why. Been working on this for a day now and completely stumped...
Here's the whole thing:
class Dictionary
attr_accessor :entries, :keywords
def initialize
#entries={}
end
def add(entry)
if entry.class==Hash
#entries.merge!(entry)
elsif entry.class==String
h={entry => nil}
#entries.merge!(h)
end
end
def keywords
#entries.keys.sort
end
def include?(k)
#entries.each_key do |n|
if (n==k)
return true
end
end
return false
end
def find(term)
results={}
#entries.each_key do |n|
if (n.include? term)
results.merge!(n => #entries[n])
end
end
if (results.length)
return results
end
return false
end
def printable
str=""
sortedentries = #entries.sort_by { |k, v| k}
str=sortedentries.map{ |k, v| "[#{k}] \"#{v}\"\n" }.join
return str
end
end
Rspec's magic method is biting you here. The hint is in the error:
expected false to respond to `false?
If the thing you are testing does not have a method false? you cannot run be_false. e.g. nil.nil? can be ran, so then nil.should be_nil is a valid test. But nil.admin? is not runnable, hence nil.should be_admin is not a valid test.
Instead of should be_false consider using should equal false.
So I'm trying to create a dictionary object in Ruby and get it to pass a bunch of RSPEC tests as part of a project. So far it's been good, but I'm stuck on one particular test. Here's the RSPEC up until that test:
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
end
Everything passes so far except for the last test "doesn't cheat when checking whether a given keyword exists". I'm trying to wrap my head around how I can get that to pass, but so far no success. Any help would be greatly appreciated. Here's what I have so far:
class Dictionary
attr_accessor :keywords, :entries
def initialize
#entries = {}
end
def add(defs)
defs.each do |word, definition|
#entries[word] = definition
end
end
def keywords
input = []
#entries.each do |key, value|
input << key
end
input.sort
end
def include?(key)
self.keywords.include?(keywords.to_s)
end
end
Thanks in advance!
There's a bug in:
self.keywords.include?(keywords.to_s)
keywords returns an array. You can't use keywords.to_s as a parameter for keywords.include? and expect it to find a match:
irb(main):002:0> keywords = %w[a b c]
=> ["a", "b", "c"]
irb(main):003:0> keywords.to_s
=> "[\"a\", \"b\", \"c\"]"
irb(main):004:0> keywords.include?(keywords.to_s)
=> false
irb(main):005:0> keywords.include?('a')
=> true
because you need to use an individual element in the keywords array if you want to find it. Notice that keywords.to_s is a String-ized version of the array, which could also be: '["a", "b", "c"]'. Hopefully that will help you recognize the problem the next time you encounter it.
From the documentation for include?:
a = [ "a", "b", "c" ]
a.include?("b") #=> true
a.include?("z") #=> false
So, change:
def include?(key)
self.keywords.include?(keywords.to_s)
end
to:
def include?(key)
self.keywords.include?(key)
end
What do you mean by "doesn't cheat"? How can code cheat? It only does what you told it. All the previous tests look like they'd rule out the conditions being tested in the "doesn't cheat" block which makes only:
#d.include?('bird').should be_false # confirms not always returning true after add
worth including in it. You could use:
#d.add('fish')
#d.include?('bird').should be_false # confirms not always returning true after add
if you really aren't sure how your code works.
Instead of building keywords using an array, which will get slower the larger your #entries list is, and results in include? running slower any time you call it,
take advantage of the fact that #entries is already a hash and use its methods:
def keywords
#entries.keys.sort
end
def include?(key)
!!#entries[key]
end
Or use this for include?:
def include?(key)
#entries.key?(key)
end
As totallymike mentions in the comment, most of the functions you want already exist in Hash. For the slightly different interfaces that you want, you should inherit Hash.
class Dictionary < Hash
def add(defs)
defs = {defs => nil} unless defs.kind_of?(Hash)
merge!(defs)
end
alias entries dup
def keywords; keys.sort end
end
Does this give you an idea how to get to pass "doesn't cheat when checking whether a given keyword exists"?
#h = Hash.new{|h,k,v| h[k] = nil}
#h["fish"]
p #h #=> {"fish"=>nil}
The {|h,k,v| h[k] = nil}part is run when a key is not present in the hash. It adds the key and gives it a nil value.
Consider the following test for rspec:
class RspecTest
def initialize
end
def to_s
"foo"
end
end
describe RspecTest do
it "should return foo (to_s)" do
RspecTest.new.should == "foo"
end
it "should return foo (inspect)" do
RspecTest.new.inspect == "foo"
end
end
And when tested through rspec:
%: rspec rspec_test.rb
F.
Failures:
1) RspecTest should return foo (to_s)
Failure/Error: RspecTest.new.should == "foo"
expected: "foo"
got: foo (using ==)
Diff:
# ./rspec_test.rb:13:in `block (2 levels) in <top (required)>'
Finished in 0.00059 seconds
2 examples, 1 failure
So the first test fails, whereas the second test passes. Why is that?
The second test passes, because it doesn't test anything. It doesn't contain any expectation (i.e. a call to should or should_not). It cannot fail, because there is nothing to fail.
The first test fails, because you are asserting that an instance of RspecTest is equal to the string 'foo'. This cannot possibly be true. How could those two objects possibly be equal if they aren't even the same kind of object?
Judging by the description of the test, you didn't actually mean to test whether the instance of RspecTest is equal to the string 'foo', but rather whether the return value of the instance method to_s is equal to the string 'foo'. However, you never call to_s anywhere.
Let's first fix the two obvious problems. Now, we have a test like this:
it 'should return foo (to_s)' do
RspecTest.new.to_s.should == 'foo'
end
it 'should return foo (inspect)' do
RspecTest.new.inspect.should == 'foo'
end
There is some unnecessary duplication there with the two RspecTest.new calls, so let's fix that by simply making RspecTest.new the default subject:
subject { RspecTest.new }
it 'should return foo (to_s)' do
subject.to_s.should == 'foo'
end
it 'should return foo (inspect)' do
subject.inspect.should == 'foo'
end
And actually, if you don't supply an explicit subject, then RSpec will walk up the chain of nested describe blocks until it finds a class, and will simply call that class's new method to provide the subject. So, we can just delete the subject declaration:
it 'should return foo (to_s)' do
subject.to_s.should == 'foo'
end
it 'should return foo (inspect)' do
subject.inspect.should == 'foo'
end
Personally, I prefer to let RSpec provide the example name by itself, so that the example name and the actual example code don't get out of sync, so I'd probably write that more like this:
describe RspecTest do
describe '#to_s' do
it { subject.to_s.should == 'foo' }
end
describe '#inspect' do
it { subject.inspect.should == "foo" }
end
end
Which yields:
RspecTest
#to_s
should == "foo"
#inspect
should == "foo"
Finished in 0.16023 seconds
2 examples, 0 failures
Last but not least, your initializer isn't actually doing anything, so you don't need it. All together, my version looks like this:
class RspecTest
def to_s; 'foo' end
end
describe RspecTest do
describe '#to_s' do
it { subject.to_s.should == 'foo' }
end
describe '#inspect' do
it { subject.inspect.should == "foo" }
end
end
I think your test should be the following (and they'll both pass). The first one is missing the actual to_s call, and the seocnd one is missing the .should:
describe RspecTest do
it "should return foo (to_s)" do
RspecTest.new.to_s.should == "foo"
end
it "should return foo (inspect)" do
RspecTest.new.inspect.should == "foo"
end
end