Rspec中的let作用域问题

34

我认为我遇到了rspec let和作用域的问题。我可以在示例中("it"块)使用let定义的方法,但无法在外部(describe块中的let所在位置)使用。

5   describe Connection do
8     let(:connection) { described_class.new(connection_settings) }
9 
10    it_behaves_like "any connection", connection
24  end

运行这个测试用例时,我遇到了以下错误:

connection_spec.rb:10: undefined local variable or method `connection' for Class:0xae8e5b8 (NameError)

如何将connection参数传递给it_behaves_like呢?

5个回答

28

let() 应该被限定在示例块中,不能在其他地方使用。实际上,您不会将 let() 用作参数。它无法与 it_behaves_like 一起使用的原因与 let() 的定义方式有关。 Rspec 中的每个示例组都定义了一个自定义类。let() 定义了该类中的实例方法。但是,当您在该自定义类中调用 it_behaves_like 时,它是在类级别而不是在实例内部调用。

我已经像这样使用过 let():

shared_examples_for 'any connection' do
  it 'should have valid connection' do
    connection.valid?
  end
end

describe Connection do
  let(:connection) { Connection.new(settings) }
  let(:settings) { { :blah => :foo } }
  it_behaves_like 'any connection'
end

我做过与bcobb的答案类似的事情,不过我很少使用shared_examples:

module SpecHelpers
  module Connection
    extend ActiveSupport::Concern

    included do
      let(:connection) { raise "You must override 'connection'" }
    end

    module ClassMethods
      def expects_valid_connection
        it "should be a valid connection" do
          connection.should be_valid
        end
      end
    end
  end
end

describe Connection do
  include SpecHelpers::Connection

  let(:connection) { Connection.new }

  expects_valid_connection
end

使用共享示例的定义比直接扩展 Rspec 更冗长。我认为 "it_behave_like" 比直接扩展 Rspec 更糟糕。

显然,您可以向 .expects_valid_connections 添加参数

我写了这篇文章来帮助一位朋友的 rspec 课程:http://ruby-lambda.blogspot.com/2011/02/agile-rspec-with-let.html ...


20

删减 -- 在我的第一个解决方案中完全失败了。Ho-Sheng Hsiao 给出了一个很好的解释。

你可以像下面这样给 it_behaves_like 传递一个块:

describe Connection do
  it_behaves_like "any connection" do
    let(:connection) { described_class.new(connection_settings) }
  end
end

我使用 "shared_examples_for" 来测试任何的连接,代码中出错的地方位于第10行,在该行之前就尝试着使用“connection”变量,导致在共享示例之前就发生错误。第二个解决方案将会重复代码,因为我需要在 connection_spec 的示例中使用 let(:connection) 代码。 - Costi
5
这绝对是更好的答案。 - robomc
2
这个回答非常棒! - Daniel Morris
同意robomc的观点。对我来说,Ho-Sheng Hsiao更好。 - Sergio Belevskij

6

我发现如果您没有显式地传递由let声明的参数,它将在共享示例中可用。

因此:

describe Connection do
  let(:connection) { described_class.new(connection_settings) }

  it_behaves_like "any connection"
end

连接将在共享示例规范中可用


这个可以运行,但不太好。你在 shared_context 和名为“connection”的变量之间创建了一个依赖关系。因此,如果在另一个 spec 中有 let(:new_connection),它将无法工作。 - ascherman

3
我找到了适合我的方法:
   describe Connection do
     it_behaves_like "any connection", new.connection
     # new.connection: because we're in the class context 
     # and let creates method in the instance context, 
     # instantiate a instance of whatever we're in
   end

RSpec是否使用猴子补丁来更改new方法以实现这一点? - Andrew Grimm

1
这对我有效:
  describe "numbers" do

    shared_examples "a number" do |a_number|
      let(:another_number) {
        10
      }

      it "can be added to" do
        (a_number + another_number).should be > a_number
      end

      it "can be subtracted from" do
        (a_number - another_number).should be < a_number
      end
    end

    describe "77" do
      it_should_behave_like "a number", 77
    end

    describe "2" do
      it_should_behave_like "a number", 2
    end
  end

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