2011-04-09 10 views
5

Estoy usando Sinatra (1.2) y RSpec (2.5) y me gustaría crear un nuevo objeto con un atributo de estilo TDD. Así es como el resultado final debe ser similar:Spec RSpec model attribute setter

class User 
    def initialize(name) 
    @name = name 
    end 
end 

sé que tengo que escribir el ejemplo antes de la aplicación, pero estoy tratando de explicar mi pregunta aquí. :) Aquí está la especificación de trabajo no tengo hasta ahora:

describe User 
    it "creates a new user object" do 
    name = mock("A name") 
    user = mock(User) # shouldn't do this, see the reply's 
    user.should_receive(:name=).with(name) 
    User.new(name) 
    end 
end 

Cuando corro RSpec recibo el "esperado: 1 vez, recibido 0 veces" error. ¿Alguna idea de cómo puedo explicar RSpec? Me gustaría asignar el atributo de nombre.

Nota: No estoy usando Rails, no estoy usando ActiveRecord ni nada, solo Ruby.

+1

Para referencia futura: burlarse del objeto de usuario no es una buena idea, mi mal. Gracias a todos por los comentarios. – Cimm

Respuesta

12

En primer lugar, permítanme explicar por qué la especificación que ha escrito no funciona:

configura la expectativa de que el objeto devuelto por simulacro mock(User) debe recibir name=. Hay dos problemas con esto. En primer lugar, el simulacro no recibirá nada, porque nunca se llama. mock(User) devuelve un objeto de simulacro y no se puede usar para establecer las expectativas de lo que recibirá el objeto de clase User (simplemente hazlo User.should_receive(...)). En segundo lugar, incluso si hubiera establecido la expectativa en el objeto de clase User, ese objeto nunca recibirá name=. También hay dos razones para esto: en primer lugar, porque name= (si hubiera existido) sería un método de instancia y, como tal, no se invoca en el objeto de clase y, en segundo lugar, declara no name= método de instancia. Lo que su código hace es que establece una variable de instancia.

Ahora, ¿cómo se debe escribir una prueba para esto? No deberias. Las pruebas son para definir y afirmar el comportamiento, no la implementación. Establecer una variable de instancia es pura implementación. En su código de ejemplo no hay manera de obtener el valor de la variable de instancia @name fuera de la clase, por lo tanto, no hay razón para escribir una prueba para ello.

Obviamente su código es solo un ejemplo, cualquier cosa útil haría algo con la variable @name, y eso es lo que debe probar. Comience por escribir una prueba para qué se usará un objeto User, luego escriba toda la implementación necesaria para completar esa prueba (pero no más). Escriba una prueba que refleje cómo se usará el objeto en el código de producción real.

+0

Gracias Theo. El simulacro de usuario está muy mal y no debería haberlo hecho. Usted dice lo mismo que David, no debería probar la implementación, sino mantenerme en un nivel superior y probar el comportamiento. – Cimm

1

¿Realmente quieres burlarte del mismo objeto que estás desarrollando?

require 'rspec' 

class User 
    attr_accessor :name 

    def initialize(name) 
    @name = name 
    end 
end 

describe User do 
    subject {User.new "other name"} 

    it "creates a new user object" do 
    subject.should respond_to :name= 
    end 
end 
+0

Incluso eso me parece una especificación demasiado invasiva. Ver mi otro comentario. –

+1

Gracias Wes, esta sería la respuesta literal a mi pregunta. Al leer la respuesta de David, creo que veo mi error y debería abordarlo de manera diferente. Por cierto, tienes razón acerca de la clase de usuario burlada, no debería estar haciendo eso. – Cimm

5

yo realmente recomiendo que no se acercan a esto utilizando burla. No es para lo que son. De hecho, especificar getters/setters como este no es realmente para lo que TDD es. La idea es dejar que un requerimiento impulse a los setters/getters a existir. Por ejemplo, podría haber un requisito de que el nombre del usuario aparece en un mensaje de bienvenida cuando él/ella abre una sesión entonces se podría hacer algo como esto:.

describe 'login process' do 
    it "displays user's name after successful login" do 
    user = User.new("Cimm", "[email protected]", "secret") 
    post "/login", :email => "[email protected]", :password => "secret" 
    last_response.body.should =~ /Welcome Cimm/m 
    end 
end 

Esto especifica el comportamiento, y te obliga a poner en práctica un medio de establecer el atributo del nombre (a través del constructor, en este caso) y un medio para acceder a él. No es necesario especificar el constructor directamente.

+0

Gracias David, no puedo discutir contigo sobre la mejor manera de abordar esto. ;) Puedo ver lo que quieres decir y sí, tienes razón, salté unos pocos pasos aquí y debería volver a un nivel más alto primero. Todavía hay algo que no me sorprende ... en algún momento volveré a estar al nivel de modelo y tendré que crear el método User.new, ¿omito las asignaciones de atributos en ese punto porque está probado en la vista de especificaciones? – Cimm