2011-08-12 13 views
12

Trabajando con RSpec & Capybara, estoy obteniendo un modo de falla de prueba interesante que se va con algunos arreglos sutiles de líneas en el caso de prueba ... cosas que no deberían importar.Factory Girl/Capybara borrando registros de la base de datos mid-test?

Estoy desarrollando mi propio sistema de autenticación. Actualmente está funcionando y puedo iniciar sesión/salir con el navegador y la sesión funciona, etc. etc. Sin embargo, tratar de probar esto está fallando. Está sucediendo algo que no entiendo del todo, que parece depender del orden de las llamadas (aparentemente) no relacionadas.

require 'spec_helper' 

describe "Sessions" do 
    it 'allows user to login' do 
    #line one 
    user = Factory(:user) 
    #For SO, this method hashes the input password and saves the record 
    user.password! '2468' 

    #line two 
    visit '/sessions/index' 


    fill_in 'Email', :with => user.email 
    fill_in 'Password', :with => '2468' 
    click_button 'Sign in' 

    page.should have_content('Logged in') 
    end 
end 

Como es, esa prueba falla ... el inicio de sesión falla. Después de insertar 'depurador' pone en tanto la especificación y el controlador Puedo ver por qué: el usuario no está siendo insertado en la base de datos por lo que el controlador está preocupado:

Editar añadiendo en ApplicationController

class ApplicationController < ActionController::Base 
    helper :all 
    protect_from_forgery 

    helper_method :user_signed_in?, :guest_user?, :current_user 

    def user_signed_in? 
    !(session[:user_id].nil? || current_user.new_record?) 
    end 

    def guest_user? 
    current_user.new_record? 
    end 

    def current_user 
    @current_user ||= session[:user_id].nil? ? User.new : User.find(session[:user_id]) 
    rescue ActiveRecord::RecordNotFound 
    @current_user = User.new 
    flash[:notice] = 'You\'ve been logged out.' 
    end 
end 


class SessionsController < ApplicationController 
    def login 
    user = User.where(:email=>params[:user][:email]).first 

    debugger ### 

    if !user.nil? && user.valid_password?(params[:user][:password]) 
     #engage session 
    else 
     #run away 
    end 
    end 

    def logout 
    reset_session 
    redirect_to root_path, :notice => 'Logget Out.' 
    end 
end 

en la consola, en el punto de ruptura por encima de:

1.9.2 [email protected]:~/Sites/website$ rspec spec/controllers/sessions_controller_spec.rb 
/Users/vox/Sites/website/app/controllers/sessions_controller.rb:7 
if !user.nil? && user.valid_password?(params[:user][:password]) 
(rdb:1) irb 
ruby-1.9.2-p180 :001 > User.all.count 
=> 0 
ruby-1.9.2-p180 :002 > 

Sin embargo, si yo reordenar unas pocas líneas en mi prueba, poniendo la línea 'dos' por encima de la línea 'uno':

describe "Sessions" do 
    it 'allows user to login' do 
    #line two 
    visit '/sessions/index' 

    #line one 
    user = Factory(:user) 
    #For SO, this method hashes the input password and saves the record 
    user.password! '2468' 


    fill_in 'Email', :with => user.email 
    fill_in 'Password', :with => '2468' 
    click_button 'Sign in' 

    page.should have_content('Logged in') 
    end 
end 

me sale esto en la consola (el mismo que el anterior punto de ruptura):

1.9.2 [email protected]:~/Sites/website$ rspec spec/controllers/sessions_controller_spec.rb 
/Users/vox/Sites/website/app/controllers/sessions_controller.rb:7 
if !user.nil? && user.valid_password?(params[:user][:password]) 
(rdb:1) irb 
ruby-1.9.2-p180 :001 > User.all.count 
=> 1 

En aras de la brevedad he omitido el volcado completo de los contenidos del objeto de usuario, pero puedo asegurarles que la prueba se completa como se esperaba

Este comportamiento de líneas de intercambio para pasar la prueba realmente no encaja bien con mi idea de lo que debería estar pasando con estos comandos y ha demostrado ser bastante compatible con mis pruebas en otras áreas.

¿Alguna pista sobre lo que está pasando aquí?

He recorrido google y SO para obtener ideas que presentan este problema, y ​​no hay escasez de preguntas sobre RSpec/Capybara y Sessions. Sin embargo, nada parecía encajar del todo bien.

Gracias por mirar.

actualización

He añadido un punto de interrupción (justo antes de una llamada visita) y algunos de depuración para la prueba y volver con esto:

(rdb:1) user 
#<User id: 1, login_count: 1, email: "[email protected]", encrypted_password: "11f40764d011926eccd5a102c532a2b469d8e71249f3c6e2f8b...", salt: "1313613794"> 
(rdb:1) User.all 
[#<User id: 1, login_count: 1, email: "[email protected]", encrypted_password: "11f40764d011926eccd5a102c532a2b469d8e71249f3c6e2f8b...", salt: "1313613794">] 
(rdb:1) next 
/Users/vox/Sites/website/spec/controllers/sessions_controller_spec.rb:19 
fill_in 'Email', :with => user.email 
(rdb:1) User.all 
[] 

algo tan claramente a lo largo de la manera en que ¿Hacer visitas le dice a Factory Girl que está hecho con el objeto de usuario y lo elimina?

Editar Después de inspeccionar test.log con cuidado, no se emite ninguna eliminación. Así que estoy más o menos de vuelta al punto de partida.

Respuesta

21

Con la ayuda de la lista de correo de Factory Girl he encontrado el problema.

De forma predeterminada, RSpec usa transacciones para mantener la base de datos en un estado limpio y cada transacción está vinculada a un hilo. En algún lugar a lo largo de la tubería, el comando visit_page se divide y la transacción vinculada al hilo actual muere.

La solución es simple: deshabilite las transacciones.

describe "Sessions" do 
    self.use_transactional_fixtures = false 

    it 'no longer uses transactions' do 
    #whatever you want 
    end 
end 

Actualización para Rails 5.1

A partir de Rails 5.1, use_transactional_fixtures es obsoleto y debe ser reemplazado con use_transactional_tests.

self.use_transactional_tests = false 
+3

Lo mejor es configurarlo en el spec_helper.rb. – iltempo

+0

Esto me dejó perplejo todo el día ya que tenía muchas distracciones que me llevaron por el camino equivocado. ¡Gracias! – Rimian

2

Creo que la variable de usuario en RSpec ha sobrescrito la que está en el controlador por lo que no funcionó? (no se pudo obtener el usuario correcto.e-mail en la prueba)

Antes:

user = Factory(:user) 
user.password! '2468' 

visit '/sessions/index' # user gets overwritten 

fill_in 'Email', :with => user.email # can't get user.email 

Después:

visit '/sessions/index' # Execute action 

user = Factory(:user) # user gets overwritten 
user.password! '2468' 

fill_in 'Email', :with => user.email # user.email works 
+0

no creo que su un problema con el usuario * * variable de sobreescritos, pero voy a mirar en él. Gracias. – voxobscuro

1

Esto no es técnicamente una respuesta, más de un comentario, pero para aclarar el código que es el mecanismo más fácil.

Se puede intentar hacer lo siguiente para ayudar a reducir donde el ser del usuario destruido

describe "Sessions" do 
    it 'allows user to login' do 
    #line one 
    user = Factory(:user) 
    #For SO, this method hashes the input password and saves the record 
    user.password! '2468' 


# check the user's definitely there before page load 
puts User.first 

    #line two 
    visit '/sessions/index' 

# check the user's still there after page load 
puts User.first.reload 


    fill_in 'Email', :with => user.email 
    fill_in 'Password', :with => '2468' 
    click_button 'Sign in' 

# check the user's still there on submission (though evidently not) 
puts User.first.reload 

    page.should have_content('Logged in') 
    end 
end 

EDITAR

El hecho de que funciona para usted bien en la vida real, pero no en Carpincho sugiere que es puede ser un producto de la información de sesión existente. Cuando estás probando en el navegador, por lo general estás yendo por detrás del trabajo anterior, pero Capibara siempre está comenzando desde una sesión limpia.

Puedes ver fácilmente si puedes reproducir el error de Capiba en el navegador borrando todas tus cookies (como estoy seguro que sabes) o simplemente cambiando a una nueva ventana de incógnito en Chrome/FF, que es una buena aplicación forma de obtener una sesión limpia.

+0

Esta es una buena sugerencia de depuración, gracias. – voxobscuro

+0

Bueno, gracias por la sugerencia ...Sé un poco más sobre el problema ahora, pero todavía no puedo resolverlo. – voxobscuro

+0

Puedes publicar el código para el método que responde a 'visitar '/ sessions/index'', presumiblemente' sessions_controller # index' (podría valer la pena considerar poner esto en 'sessions/new' btw) y también tu controlador de aplicación. –

0

La respuesta correcta de arriba me ha ayudado. Por supuesto, necesitaba cambiar algunas otras pruebas que suponían (incorrectamente o no) que no existía un accesorio. Para más información: hay algo de información sobre esto en Capybara README.

"Si está utilizando una base de datos SQL, es común para ejecutar todas las pruebas en una transacción, que se deshace al final de la prueba, rspec-carriles hace esto por defecto fuera de la cuadro por ejemplo. Dado que las transacciones generalmente no se comparten entre subprocesos, esto hará que los datos que haya puesto en la base de datos en su código de prueba sean invisibles para Capybara ".

También puede config RSpec para limpiar después de su prueba de forma manual:

https://github.com/jnicklas/carrierwave/wiki/How-to%3A-Cleanup-after-your-Rspec-tests

Cuestiones relacionadas