【问题标题】:How to delete an entire array in Ruby and test with RSpec如何在 Ruby 中删除整个数组并使用 RSpec 进行测试
【发布时间】:2015-06-26 02:20:43
【问题描述】:

我对 Ruby 还很陌生,目前正在学习完整的堆栈课程。对于我的一个项目,我们正在构建一个地址簿。我已经设置了如何向地址簿添加条目,但是,我似乎无法弄清楚如何删除条目(我尝试使用下面 AddressBook 类中的 remove_entry 方法,但没有任何运气)。我们还应该先用 RSpec 进行测试,让测试失败,然后编写一些代码让它通过。如果我没有包含这个问题所需的所有信息,请告诉我(这里是新手)。无论如何,这是我目前所拥有的:

RSpec

context ".remove_entry" do
    it "removes only one entry from the address book" do
      book = AddressBook.new
      entry = book.add_entry('Ada Lovelace', '010.012.1815', 'augusta.king@lovelace.com')
      book.remove_entry(entry)

      expect(entry).to eq nil
    end
  end

通讯录类

require_relative "entry.rb"

class AddressBook
  attr_accessor :entries

  def initialize
    @entries = []
  end

  def add_entry(name, phone, email)
    index = 0
    @entries.each do |entry|
      if name < entry.name
        break
      end
      index += 1
    end

    @entries.insert(index, Entry.new(name, phone, email))
  end

  def remove_entry(entry)
    @entries.delete(entry)
  end
end

入门类

class Entry
  attr_accessor :name, :phone_number, :email

  def initialize(name, phone_number, email)
    @name = name
    @phone_number = phone_number
    @email = email
  end

  def to_s
    "Name: #{@name}\nPhone Number: #{@phone_number}\nEmail: #{@email}"
  end
end

使用 RSpec 测试我的代码时,我收到以下错误消息:

.....F

Failures:

  1) AddressBook.remove_entry removes only one entry from the address book
     Failure/Error: expect(entry).to eq nil

       expected: nil
            got: [#<Entry:0x00000101bc82f0 @name="Ada Lovelace", @phone_number="010.012.1815", @email="augusta.king@lovelace.com">]

       (compared using ==)
     # ./spec/address_book_spec.rb:49:in `block (3 levels) in <top (required)>'

Finished in 0.02075 seconds (files took 0.14221 seconds to load)
6 examples, 1 failure

Failed examples:

rspec ./spec/address_book_spec.rb:44 # AddressBook.remove_entry removes only one entry from the address book

【问题讨论】:

    标签: ruby-on-rails ruby rspec


    【解决方案1】:

    只需测试 book.entries 关联是否为空:

    expect(book.entries).to be_empty
    

    由于 book 是测试中的局部变量,因此如果您保持测试原子性,您将不会得到假阴性结果。 rspec 上的一些 best practices

    编辑: 您还可以检查该条目是否在集合中:

    expect(book.entries.index(entry)).to be_nil
    

    或测试数组长度的变化:

    expect { book.remove_entry(entry) }.to change{book.entries.count}.by(-1)
    

    如果您想知道be_xxx 语法糖,如果对象响应xxx?,那么您可以在测试中使用be_xxx (predicate matchers)

    【讨论】:

    • 我认为entrybook 在这种情况下没有reload 方法。但是 be_empty 期望很好。我唯一的缺点是,如果测试增长或被重复使用,如果之前添加了其他条目,则可能会出现误报。
    • 你是对的!我一直在搜索 ruby​​-on-rails 问题,但这是纯 ruby​​。我会更新答案。如果你让你的测试保持原子性,你就不会有问题。 book 是在it 内部创建的局部变量,所以它不会与其他测试冲突。
    • 谢谢!现在对我来说很有意义!
    • 为了清楚起见,be_empty 有效,但通常我希望在其上下文中尽可能明确地看到测试。您正在测试是否删除了 Ada Lovelace 条目,因此 expect 应该简单地说。当您准备好在您的 book 中包含 2 个或更多项目时,be_empty 将不起作用 - 在某些时候,您必须指定哪个条目被删除。
    • 我认为“Ada L”是偶然的,它可能是“假条目”或“条目#{n}”(由工厂生成),测试不应该改变。另外我觉得问book.entriesbe_empty是不是很简单,一看就懂。
    【解决方案2】:

    我认为您的期望有问题。 entry 变量未设置为nil,但book 中的entry 将是nil

    我认为这样的做法会更好:

    expect(book.entries.find { |e| e.name == "Ada Lovelace" }).to eq nil
    

    更好的是,您的AddressBook 可以有自己的find 方法,这将使expect 参数更好,例如book.find(:name =&gt; "Ada Lovelace")

    最后,我还会在remove_entry 调用之前一个expect 调用,以确保其结果等于entry

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-03-25
      • 1970-01-01
      • 1970-01-01
      • 2011-07-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-09
      相关资源
      最近更新 更多