2012-06-12 11 views
14

Nota: He leído this pregunta y la respuesta, pero por alguna razón el código no funciona para mí. (Consulte a continuación el error que estoy recibiendo)Método de destrucción de prueba RSpec (Rails Tutorial 3.2 Ch. 9, Ej. 10)

El ejercicio 10 del Capítulo 9 de Rails Tutorial le pide que: Modifique la acción de destrucción [para los usuarios] para evitar que los usuarios administradores se destruyan a sí mismos. (Escriba primero una prueba)

La parte difícil aquí es probarla, porque la aplicación ya oculta el enlace "eliminar" para el usuario actual, por lo que debe hacer la solicitud http directamente.

Obtuve el código funcionando, y lo probé quitando la pieza de código que oculta el enlace de eliminación para el usuario actual. Efectivamente, si hago clic en el enlace de eliminación para el usuario actualmente conectado, me redirige y me da el mensaje de notificación.

De users_controller.rb

def destroy 
    @user = User.find(params[:id]) 
    if current_user?(@user) 
     redirect_to users_path, notice: "You can't destroy yourself." 
    else 
     @user.destroy 
     flash[:success] = "User destroyed." 
     redirect_to users_path 
    end 
    end 

El problema que estoy teniendo es por escrito las pruebas de este que va a enviar la solicitud de eliminación y llamar al método destroy. Probé la solución de Rspec test for destroy if no delete link, que estoy copiando aquí:

De user_pages_spec.rb

describe "destroy" do 
    let(:admin) { FactoryGirl.create(:admin) } 

    it "should not allow the admin to delete herself" do 
     sign_in admin 
     #expect { delete user_path(admin), method: :delete }.should change(User, :count) 
     expect { delete :destroy, :id => admin.id }.should_not change(User, :count) 
    end 
    end 

Pero cuando corro esto, me sale este error de RSpec

Failures: 

    1) User Pages destroy should not allow the admin to delete herself 
    Failure/Error: expect { delete :destroy, :id => admin.id }.should_not change(User, :count) 
    ArgumentError: 
     bad argument (expected URI object or URI string) 
    # ./spec/requests/user_pages_spec.rb:180:in `block (4 levels) in <top (required)>' 
    # ./spec/requests/user_pages_spec.rb:180:in `block (3 levels) in <top (required)>' 

Entonces, mis preguntas son: 1) ¿Por qué este código está fallando? 2) ¿Cómo simulo una "eliminación" para llamar a la acción destruir en mi controlador?

Medio Ambiente: Mac OS X rubí 1.9.3p194 Rails 3.2.3

gemas para las pruebas:
grupo: Prueba de hacer joya 'rspec-carriles', '2.9.0' joya 'capibara ',' 1.1.2 ' gema' rb-fsevent ',' 0.4.3.1 ',: require => falso gema' gruñido ',' 1.0.3 ' gema' guard-spork ',' 0.3.2 ' gema 'spork', '0.9.0' gema 'factory_girl_rails', '1.4.0' final

Más información He probado un montón de maneras de tratar de simular clic en el enlace de eliminación y ninguno parece funcionar. He estado usando la gema del depurador para ver si se está llamando al método de destrucción. En la prueba de que hace clic en el enlace para eliminar un usuario diferente, el método destroy se llama y funciona bien:

it "should be able to delete another user" do 
    expect { click_link('delete') }.to change(User, :count).by(-1) 
end 

Pero nada de lo que han tratado de generar la solicitud de eliminación directa ha trabajado para llamar al método destroy.

Gracias por su ayuda!

Will

** ACTUALIZACIÓN **

Probé la sugerencia de DVG:

describe "destroy" do 
    let(:admin) { FactoryGirl.create(:admin) } 

    it "should not allow the admin to delete herself" do 
     sign_in admin 
     #expect { delete user_path(admin), method: :delete }.should change(User, :count) 
     expect { delete :destroy, :id => admin }.to_not change(User, :count) 
    end 
    end 

Y dio este mensaje:

6) User Pages destroy should not allow the admin to delete herself 
    Failure/Error: expect { delete :destroy, :id => admin }.to_not change(User, :count) 
    ArgumentError: 
     bad argument (expected URI object or URI string) 
    # ./spec/requests/user_pages_spec.rb:190:in `block (4 levels) in <top (required)>' 
    # ./spec/requests/user_pages_spec.rb:190:in `block (3 levels) in <top (required)>' 

SOLUCIÓN

Lo descubrí después de FOREVER.

tuve que usar rack :: Prueba de emitir la solicitud DELETE, pero capibara y rack :: Prueba no comparten la misma MockSession, por lo que tuvo que retirarse en el: remember_token y galletas: sample_app_session y ponerlos en la solicitud DELETE manualmente. Aquí está lo que funcionó. (El otro problema que tenía, se enumeran a continuación, fue que tuve una declaración force_ssl que no estaba dejando que mi acción destruir a ser llamado.

describe "destroy" do 
    let!(:admin) { FactoryGirl.create(:admin) } 

    before do 
     sign_in admin 
    end 

    it "should delete a normal user" do 
     user = FactoryGirl.create(:user) 
     expect { delete user_path(user), {}, 
     'HTTP_COOKIE' => "remember_token=#{admin.remember_token}, 
     #{Capybara.current_session.driver.response.headers["Set-Cookie"]}" }. 
     to change(User, :count).by(-1) 
    end 

    it "should not allow the admin to delete herself" do 
     expect { delete user_path(admin), {}, 
     'HTTP_COOKIE' => "remember_token=#{admin.remember_token}, 
     #{Capybara.current_session.driver.response.headers["Set-Cookie"]}" }. 
     to_not change(User, :count) 
    end 
    end 

tuve una declaración force_ssl después de mis before_filters en mi users_controller.rb y esto fue de alguna manera tirar cosas fuera de lo que nunca llegué a la acción de destruir.

class UsersController < ApplicationController 
    before_filter :signed_in_user, only: [:edit, :update, :index] 
    before_filter :existing_user, only: [:new, :create] 
    before_filter :correct_user, only: [:edit, :update] 
    before_filter :admin_user,  only: :destroy 

    #force_ssl 

    def index 
    @users = User.paginate(page: params[:page]) 
    end 

    def show 
    @user = User.find(params[:id]) 
    @microposts = @user.microposts.paginate(page: params[:page]) 
    end 

    def destroy 
    @user = User.find(params[:id]) 
    if current_user?(@user) 
     redirect_to users_path, notice: "You can't destroy yourself." 
    else 
     @user.destroy 
     flash[:success] = "User destroyed." 
     redirect_to users_path 
    end 
    end 

Estos fueron útiles para llegar a una solución

https://gist.github.com/484787

http://collectiveidea.com/blog/archives/2012/01/05/capybara-cucumber-and-how-the-cookie-crumbles/

+0

Una cosa que acabo de descubrir, que no es exactamente mi problema, pero podría ser un problema * * es que el método let es lento, por lo que se podía imaginar que atornillar la espera to_not cambiar la funcionalidad. ¡Así que ajusté el código para usar let! al crear el usuario administrador. –

+0

¿cuál es el escenario que está probando? El administrador inicia sesión, el enlace de eliminación está oculto, pero de alguna manera crea la solicitud de eliminación. (solo pregunta) –

+0

Buena pregunta. La mayoría de las respuestas es que esto fue un ejercicio en el Tutorial de Rails. Resultó que fue un buen ejercicio, porque aprendí todo tipo de cosas sobre cookies, solicitudes de http, Capybara y Rack :: Test. Supongo que podría estar probando el escenario donde falla el código para ocultar el enlace de eliminación y quiero que mi controlador tenga una copia de seguridad. –

Respuesta

5

Usted está confundiendo-carriles rspec especificaciones de petición que son pruebas de integración y se ejecutan en un navegador y controlador simulados especificaciones de qué controlador de manera aislada de prueba. delete(action, *args) (y get, post y así sucesivamente): es un método que simula la solicitud de ActionController :: TestCase, por lo que no está disponible en su prueba.

Así que su única opción es simular un clic en un navegador. No sé cómo escondes tu enlace de eliminación, si el html está allí pero está oculto, deberías poder hacer clic en él. Si no está allí (eliminado en el lado del servidor al generar vista) puede usar el page.execute_script de capybara (pero debe habilitar javascript para este ejemplo :js => true). Usted puede añadir el enlace de vuelta:

page.execute_script("$('body').append("<a href="https://stackoverflow.com/users/1" data-method="delete" rel="nofollow">Destroy</a>")") 

o hacer llamada AJAX:

page.execute_script("$.ajax({type:'DELETE',url:'/users/1'})") 

no probó, pero algo como esto debería funcionar.

3

Prueba esto:

expect { delete :destroy, :id => admin }.to_not change(User, :count) 
+0

Lo intenté, el mismo error (ver más arriba) –

+0

Esto funcionó para mí. ¡Gracias! –

6

He resuelto el mismo problema utilizando la siguiente:

describe "should not be able to delete themselves" do 
    it { expect { delete user_path(admin) }.not_to change(User, :count) } 
end 
+1

Creo que esto solo funciona en las pruebas del controlador, no en las pruebas de integración. – Dean

+0

Pasa esta prueba, a pesar de que la probé manualmente y no funciona. (debe fallar) –

6

solución de CallumD trabajó para mí, y parecía el más consistente con las técnicas recomendadas en el resto del tutorial de Michael Hartl.Pero quería endurecer la sintaxis un poco para que sea más consistente con las otras especificaciones en el mismo tutorial:

it "should not be able to delete itself" do 
    expect { delete user_path(admin) }.not_to change(User, :count) 
end 
+1

Estoy de acuerdo. Yo hice lo mismo. – KMcA

Cuestiones relacionadas