2011-04-05 11 views
32

Creo que tengo un problema con rspec let y scoping. Puedo usar los métodos definidos con let in examples (los bloques "it"), pero no afuera (el bloque describe donde hice el let).Rspec deje scoping

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

Cuando trato de ejecutar esta especificación, me sale el error:

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

¿Cómo puedo pasar el parámetro de conexión a la it_behaves_like?

Respuesta

3

me encontré con lo que funciona para mí:

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 
+0

¿RSpec monkeypatch 'new' hace esto? –

19

Redacted - completamente apestoso en mi primera solución. Ho-Sheng Hsiao dio una gran explicación de por qué.

Usted puede dar it_behaves_like un bloque de este modo:

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

yo "shared_examples_for" ninguna conexión "do | conexión |". El error está en la línea 10, donde lo intenta primero encuentra "conexión", antes de que llegue a los ejemplos compartidos. La segunda solución duplicaría el código, porque necesito el código por let (: conexión) en los ejemplos en connection_spec también. – Costi

+4

Esta es, de lejos, la mejor respuesta. – robomc

+0

Esta es en realidad la respuesta correcta – Dorian

27

Let() se supone que estar en el ámbito de los bloques de ejemplo y en otras partes inutilizables. En realidad, no usa let() como parámetros. La razón por la que no funciona con it_behaves_like como parámetro tiene que ver con cómo se define let(). Cada grupo de ejemplo en Rspec define una clase personalizada. let() define un método de instancia en esa clase. Sin embargo, cuando llamas it_behaves_like en esa clase personalizada, está llamando al nivel de clase en lugar de desde una instancia.

He usado Sea() así:

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 

que he hecho algo similar a la de bcobb respuesta, aunque rara vez usar 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 

La definición de esos ejemplos compartidos son más detallados que usar ejemplos compartidos. Creo que encuentro que "it_behave_like" es más incómodo que extender Rspec directamente.

Obviamente, se puede añadir argumentos a .expects_valid_connections

escribí esto para ayudar a la clase rspec de un amigo: http://ruby-lambda.blogspot.com/2011/02/agile-rspec-with-let.html ...

1

Este funciona para mí:

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 
6

He descubierto que si no pasa explícitamente el parámetro declarado por let, estará disponible en el ejemplo compartido.

Así:

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

    it_behaves_like "any connection" 
end 

estará disponible en las especificaciones ejemplo compartidos

+0

Esto funciona pero no es bueno. Crea una dependencia entre shared_context y la variable con el nombre "conexión". Entonces, si tiene una let (: new_connection) en otra especificación, no funcionará. –