为什么 Ruby 返回方法调用时的“true”?

4

这是我找到的一个关于Rspec的简短教程:w3ii.com: rspec,它讲解了rspec helpers。以下是示例代码:

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

当我运行这个程序时,出现了以下错误:
``` NoMethodError: undefined method 'good_dog' for true:TrueClass # ./dor.rb:22:in 'block <2 levels> in >' ```
我无法理解为什么调用 Dog.new() 方法会返回一个 true:TrueClass 对象而不是一个普通的狗。

1
我会非常小心地使用w3ii作为参考网站,因为它看起来质量相当低。有更好的开始学习编程的地方 - tadman
2个回答

3

这是因为create_and_walk_dog返回了walk_dog方法的返回值。

要从create_and_walk_dog方法中返回一只狗,您需要使用类似于以下代码:

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

或者扩展预期块:

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

2
你也可以通过在 walk_dog 方法的最后一行添加 self 来使其成为可链式调用的方法。 - tadman
2
这是使用 tap(&:block) 方法的一个好例子,它会执行给定的 block 并返回对象本身。在这种情况下:Dog.new(good_or_bad).tap(&:walk_dog) - MrYoshiji

0

我认为为了清晰起见,您应该在这里使用RSpec let方法而不是在RSpec文件中包含完整的方法定义。虽然像另一个答案中建议的那样让您的_walk_dog_方法返回self可以修复您当前的RSpec实现,但它并不能帮助您在潜在的应用程序中解决其他问题。假设有一种情况,坏狗随机50%的时间返回false或根本不合作。

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

这也可以进行辩论,但是除非你正在访问数据库或执行相对耗时的操作,否则每个规范应该只断言一件事情。

附言:它们都是好狗,Brent。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接