对于在RSpec中使用接收匹配器的示例,有没有办法设置公共代码块(即每个'之前的#39;)?

时间:2014-04-03 01:42:00

标签: rspec

以下是一个用例示例,我们正在测试以确保DinnerTable#arrange仅在安排来宾时调用另一个方法DinnerTable#moar_chairs

describe DinnerTable do
  subject(:table) { DinnerTable.new }

  describe "#arrange" do
    context "when arranging for family" do
      let(:group) { :family }
      it "doesn't get extra chairs" do
        expect(table).not_to receive(:moar_chairs)
        table.arrange(group)
      end
    end

    context "when arranging for guests" do
      let(:group) { :guests }
      it "gets extra chairs" do
        expect(table).to receive(:moar_chairs)
        table.arrange(group)
      end
    end
  end
end

注意上下文如何设置要为(家庭或客人)安排的组,但是我们仍然只是在每个示例中独立调用相关方法arrange(使上下文有点无意义)。

通常将该方法放在before块中是有效的,因为大多数匹配器都期望当前可测试的条件,但是因为我们正在使用receive匹配器,预计将来会发生某些事情,我们不能那样做,否则测试就会失败。将它放在after块中也会失败,因为期望在执行after块之前返回false。如果我们只有某个during块会在示例结束之前运行...

describe DinnerTable do
  subject(:table) { DinnerTable.new }

  describe "#arrange" do
    during { table.arrange(group) }

    context "when arranging for family" do
      let(:group) { :family }
      it { should_not receive(:moar_chairs) }
    end

    context "when arranging for guests" do
      let(:group) { :guests }
      it { should receive(:moar_chairs) }
    end
  end
end

(或者也许可以将actionpredicate称为subject?另一种选择是在让接收匹配器失败之前运行后块...但是我不知道会有什么样的其他后果。

使用receive匹配器为多个示例设置公共块是否还有其他好的模式?

2 个答案:

答案 0 :(得分:1)

在执行测试代码之前,需要设置receive期望值。以下内容将起作用并消除一些重复(DinnerTable类定义仅用于说明目的):

class DinnerTable

  def arrange(group)
    moar_chairs if group == :guests 
  end

  def moar_chairs
  end

end

describe DinnerTable do
  let(:table) { DinnerTable.new }
  let(:arrange) { table.arrange(group) }

  describe "#arrange" do
    context "when arranging for family" do
      let(:group) { :family }
      it "doesn't get extra chairs" do
        expect(table).not_to receive(:moar_chairs)
        arrange
      end
    end

    context "when arranging for guests" do
      let(:group) { :guests }
      it "gets extra chairs" do
        expect(table).to receive(:moar_chairs)
        arrange
      end
    end
  end
end

答案 1 :(得分:0)

哦,我错了!我一定是第一次做错了,因为把方法放在after可以工作:D Yay!

所以:

describe DinnerTable do
  subject(:table) { DinnerTable.new }

  describe "#arrange" do
    after { table.arrange(group) }

    context "when arranging for family" do
      let(:group) { :family }
      it { should_not receive(:moar_chairs) }
    end

    context "when arranging for guests" do
      let(:group) { :guests }
      it { should receive(:moar_chairs) }
    end
  end
end

<强>更新

或者,为了更好的语义,添加一个名为action的spec helper方法,如下所示:

def action(&block)
  shared_context "run action before", run_action: :first do
    before(&block)
  end
  shared_context "run action after", run_action: :last do
    after(&block)
  end
end

然后示例如下:

describe DinnerTable do
  subject(:table) { DinnerTable.new }

  describe "#arrange", run_action: :last do
    action { table.arrange(group) }

    context "when arranging for family" do
      let(:group) { :family }
      specify { table.should_not receive(:moar_chairs) }
    end

    context "when arranging for guests" do
      let(:group) { :guests }
      specify { table.should receive(:moar_chairs) }
    end
  end
end