编写 rspec 测试以将项目添加到哈希

writing a rspec test to add an item to a hash

我收到一个 NoMethodError 对于我的代码,但我已经定义了它说缺少的添加方法。 我正在尝试将一个项目添加到已经存在的散列中。 散列是菜肴,我正在尝试使用 add 方法。

测试:

require 'menu'

describe Menu do

    it 'has a menu' do
        expect(subject.respond_to?(:dishes)).to be true
    end

    it 'displays dishes and prices' do
        expect(subject.dishes).to eq [
            { name: 'Burger', price: 10.95 },
            { name: 'Pizza', price: 14.00 },
            { name: 'Salad', price: 7.60 },
            { name: 'fries', price: 2.90 }
        ]
    end

    it 'can add dishes to it' do
        menu = Menu.new 
        menu.add_dish("Icecream", 4.80)
        expect(subject.dishes).to eq [
            { name: 'Burger', price: 10.95 },
            { name: 'Pizza', price: 14.00 },
            { name: 'Salad', price: 7.60 },
            { name: 'fries', price: 2.90 },
            { name: 'icecream', price: 4.80 }
        ]
    end
end

方法

class Menu
   def initialize
     @dishes = []
   end

   def dishes
     @dishes = [
        { name: 'Burger', price: 10.95  },
        { name: 'Pizza', price: 14.00 },
        { name: 'Salad', price: 7.60 },
        { name: 'fries', price: 2.90 }
     ]
   end

   def add_dish(name, price)
     @dishes << { name: name, price: price }
   end
end

谢谢

啊,我明白你的问题了。您需要初始化菜单。 Add 不是静态方法。所以你需要类似的东西,

Menu.new.add(blah, blah)

查看:

Menu.add("Icecream", 4.80)

这个方法是错误的。必须是:

Menu.new.add("Icecream", 4.80)

或者您需要类似的东西:

menu = Menu.new
menu.add("Icecream", 4.80)

如果没有你的代码,要让它工作有点困难,但问题很简单。

试试下面编辑过的代码。请注意对规范的更改初始化菜单并将添加方法添加到实例变量@dishes.

require 'menu'

describe Menu do
   it 'displays dishes and prices' do
    expect(Menu.new.dishes).to eq [
      { name: 'Burger', price: 10.95 },
      { name: 'Pizza', price: 14.00 },
      { name: 'Salad', price: 7.60 },
      { name: 'fries', price: 2.90 }
    ]
  end
  it 'can add dishes to it' do
   menu = Menu.new.add("Icecream", 4.80)
   expect(menu.dishes).to eq [
    { name: 'Burger', price: 10.95 },
    { name: 'Pizza', price: 14.00 },
    { name: 'Salad', price: 7.60 },
    { name: 'fries', price: 2.90 },
    { name: 'icecream', price: 4.80 }
  ]
  end
end

class Menu
   def initialize
     @dishes = []
   end

   def dishes
    @dishes ||=
    [
      { name: 'Burger', price: 10.95  },
      { name: 'Pizza', price: 14.00 },
      { name: 'Salad', price: 7.60 },
      { name: 'fries', price: 2.90 }
    ]
  end

  def add(name, price)
    @dishes << { name: name, price: price }
  end
end

希望对您有所帮助

看起来您在使用这段代码时遇到了一些问题。首先,因为 add 方法没有声明为 class 方法(即 def self.add),所以不能将其作为 class 方法调用(如您所见, Menu.add 表示 NoMethodError)。相反,您需要在测试中创建菜单 class 的实例,也许使用 let:

describe Menu do
  let(:menu) { Menu.new }

  it 'can add dishes to it' do
    menu.add("Icecream", 4.80)
    # test your expectations...
  end
end

最后,由于 add 方法是当前定义的,它不会修改 @dishes 而只是 returns 一个新的散列,所以你的期望会失败。您需要使 add 方法附加值,可能像这样:

def add(name, , price)
  @dishes << {name: name, price: price}
end

的回答解决了NoMethodError,但是你的代码还有很多其他的问题

  1. 重复你自己,你应该让你的代码变干(Don't Repeat Yourself原则)

  2. 当您想要将散列添加到本身就是散列列表的菜肴列表时,您强制需要调用 add 方法的对象以特定顺序提供参数, 而不是该方法构造哈希,因此每次需要调用它时,您都需要 return 以查看参数的顺序。

  3. dishes方法是错误的,因为每次调用它时,它都会将初始数组赋值给@dishes变量。在这种情况下,add_dishes 方法将无效,因为添加的菜肴将在您下次调用 dishes 方法时被删除。
  4. 你的例子没有表现力,所以如果测试没有通过,你无法从打印的消息中知道问题出在哪里。 OK,在这个小例子中这没什么大不了的,但是在一个大的应用程序中,specs expressiveness 具有更高的价值。

这里是测试例子

require 'menu'

describe Menu do
  # every time you call the function dishes in an example
  # it will be declared and it will return this array
  let :dishes do
    [
      { name: 'Burger', price: 10.95 },
      { name: 'Pizza', price: 14.00 },
      { name: 'Salad', price: 7.60 },
      { name: 'fries', price: 2.90 }
    ]
  end

  # explicit definition of the subject
  subject { Menu.new }

  # a shorter yet more expressive version of
  # expect(subject.respond_to?(:dishes)).to be true
  it { is_expected.to respond_to(:dishes) }

  # You should always group the examples that test 
  # the same method
  describe '#dishes' do
    # it 'displays dishes and prices' do
    it 'returns the list of dishes' do
      expect(subject.dishes).to eq dishes
    end
  end

  describe "#add_dish" do
    # it 'can add dishes to it' do
    it "adds the given dish to the list of dishes" do
      new_dish = {name: 'salad', price: 4.0 }
      expect {
        subject.add_dish(new_dish)
      }.to change(subject.dishes, :count).by(1)
      expect(subject.dishes).to include new_dish
    end
  end
end

所以这是 class 定义

class Menu
  # you don't need to declare the method dishes
  # since this is what attr_reader will do
  attr_reader :dishes

   def initialize
     # this will set the @dishes only once
     # but you code @dishes = [...] will return
     # the same list every time you call it and
     # all the dishes you add through the #add method
     # will be deleted.
     @dishes = [
        { name: 'Burger', price: 10.95  },
        { name: 'Pizza', price: 14.00 },
        { name: 'Salad', price: 7.60 },
        { name: 'fries', price: 2.90 }
     ]
   end

   # dish is a hash {name: '', price: ''} 
   def add_dish(dish)
     @dishes << dish
   end
end

所以现在 运行 rspec --format doc --color 看看谁表达了这些信息。