2012-07-06 4 views
35

Estoy usando RSpec (2.10.1) para probar validaciones en un modelo y he extraído algún código para compartir con otras validaciones de modelo. Las validaciones fueron escritas por primera vez en la mesa de Empresas, por lo que el código es el siguiente:Cómo pasar una variable de instancia a un ejemplo compartido RSpec

# support/shared_examples.rb 
shared_examples "a text field" do |field, fill, length| 
    it "it should be long enough" do 
    @company.send("#{field}=", fill * length) 
    @company.should be_valid 
    end 

    etc... 
end 

y el uso es:

# company_spec.rb 
describe Company do 
    before { @company = Company.new(init stuff here) } 

    describe "when address2" do 
    it_behaves_like "a text field", "address2", "a", Company.address2.limit 
    end 

    etc... 
end 

me gustaría pasar el @company como parámetro a la compartida ejemplo, por lo que puede volver a utilizar el código para diferentes modelos, algo como esto:

# support/shared_examples.rb 
shared_examples "a text field" do |model, field, fill, length| 
    it "it should be long enough" do 
    model.send("#{field}=", fill * length) 
    model.should be_valid 
    end 

    etc... 
end 

y el uso es:

# company_spec.rb 
describe Company do 
    before { @company = Company.new(init stuff here) } 

    describe "when address2" do 
    it_behaves_like "a text field", @company, "address2", "a", Company.address2.limit 
    end 

    etc... 
end 

Sin embargo, cuando hago esto obtengo undefined method 'address2' for nil:NilClass. Parece que @company no se está aprobando (¿no en el alcance?) ¿Cómo puedo hacer que algo así funcione?

Respuesta

49

El problema es que self dentro del grupo de ejemplo es diferente de self dentro de un gancho before, por lo que no es la misma variable de instancia aunque tenga el mismo nombre.

le recomiendo que utilice let para casos como estos:

# support/shared_examples.rb 
shared_examples "a text field" do |field, fill, length| 
    it "it should be long enough" do 
    model.send("#{field}=", fill * length) 
    model.should be_valid 
    end 
end 

# company_spec.rb 
describe Company do 
    describe "when address2" do 
    it_behaves_like "a text field", "address2", "a", Company.address2.limit do 
     let(:model) { Company.new(init stuff here) } 
    end 
    end 
end 
+1

Algunas piezas no tienen sentido para mí. Si es que el "yo" es diferente, ¿dónde es diferente? ¿Por qué '@ company.send()' y '@ company.should' funcionan en' shared_examples'? En su sugerencia, puedo reemplazar 'Company.new()' con '@ company' (manteniendo el bloque' before') y eso funciona. A mi comprensión le falta algo sobre qué está pasando exactamente aquí. Parece que 'self 'solo es diferente en un lugar en particular (la línea' it_behaves_like' hasta 'do'). –

+6

Hay dos valores básicos que 'self' toma en RSpec, y es análogo a los dos valores de self en una definición de clase ruby. Entre un 'describe' /' context'/'shared_examples_for' y su correspondiente' end' (pero no en los bloques 'it'),' self' es el grupo de ejemplo - al igual que 'self' en un cuerpo de clase (pero no en una definición de método) es la clase misma. 'self' en un bloque' it'/'let' o un hook' before'/'after' /' around' o es el ejemplo - al igual que 'self' en la definición de un método de instancia de una clase es la instancia de la clase . –

+3

Otra forma de pensarlo: hay un proceso de dos pasos que realiza RSpec. En primer lugar, evalúa todos los bloques anidados 'describe' /' context'/'shared_examples_for' para definir todos los ejemplos; en este punto, 'self' en estos contextos es el grupo de ejemplo correspondiente. En segundo lugar, RSpec ejecuta todos los ejemplos definidos (los bloques 'it'). Cada ejemplo definido se evalúa en una instancia del grupo de ejemplo correspondiente, y 'self' es el ejemplo. –

Cuestiones relacionadas