siderio2
siderio2

Reputation: 111

Why is ruby returning `true` for method call?

So this is a brief tutorial I found abouot Rspec: w3ii.com: rspec on it's explanation about rspec helpers. This is the code for the example:

class Dog
   attr_reader :good_dog, :has_been_walked

   def initialize(good_or_not)
      @good_dog = good_or_not
      @has_been_walked = false
   end

   def walk_dog
      @has_been_walked = true
   end
end

describe Dog do
   def create_and_walk_dog(good_or_bad)
      Dog.new(good_or_bad).walk_dog
   end

   it 'should be able to create and walk a good dog' do
      dog = create_and_walk_dog(true)

      expect(dog.good_dog).to be true
      expect(dog.has_been_walked).to be true
   end
end

When I run this, I get this error:

NoMethodError: undefined method 'good_dog' for true:TrueClass
# ./dor.rb:22:in 'block <2 levels> in >'

I can't understand how a call to Dog.new() returns a true:TrueClass object instead of a simple dog.

Upvotes: 4

Views: 259

Answers (2)

Michael Deering
Michael Deering

Reputation: 444

I think for clarity you could/should be using the RSpec let method here not a full method definition included in your RSpec file. Having your _walk_dog_ method return self like suggested on another answer I see here fixes your current RSpec implementation but does not help your flow anywhere else in your potential application. Hypothetical case where bad dogs return false for _dog.walk_dog_ randomly 50% of the time or just not cooperate at all.

class Dog
  attr_reader :good_dog, :has_been_walked

  def initialize(good_or_not)
    @good_dog = good_or_not
    @has_been_walked = false
  end

  # Command to attempt walking a dog.
  # 
  # @api public
  # 
  # @example
  #   good_dog = Dog.new(true)
  #   good_dog.walk_dog # => Will always return true for good dogs.
  #   bad_dog = Dog.new(false)
  #   bad_dog.wal_dog # => 50/50 chance of true or false
  def walk_dog
    return @has_been_walked = true if @good_dog
    [true, false].sample
  end
end

describe Dog do

  let(:good_dog) { Dog.new(true) }

  it 'should always be able to walk a good dog' do
    expect(good_dog.walk_dog).to be true
  end

  it 'should track if the dog has been walked' do
    expect {
      good_dog.walk_dog
    }.to change(dog, :has_been_walked).from(false).to true
  end

end

Up for debate also but you should only be asserting a single thing per spec also unless you are hitting the db or doing something relatively time consuming.

P.S. They are all good dogs Brent

Upvotes: 0

Igor Drozdov
Igor Drozdov

Reputation: 15045

That's because the create_and_walk_dog returns that the walk_dog method returns

To return a dog from the create_and_walk_dog method you'll need something like

describe Dog do
   def create_and_walk_dog(good_or_bad)
      dog = Dog.new(good_or_bad)
      dog.walk_dog
      dog
   end

   it 'should be able to create and walk a good dog' do
      dog = create_and_walk_dog(true)

      expect(dog.good_dog).to be true
      expect(dog.has_been_walked).to be true
   end
end

Or extend the expectation block:

describe Dog do
   it 'should be able to create and walk a good dog' do
      dog = Dog.new(true)

      expect(dog.good_dog).to be true
      expect {
        dog.walk_dog
      }.to change(dog, :has_been_walked).from(false).to true
   end
end

Upvotes: 3

Related Questions