2012-06-12 8 views
14

Nota: ho letto la domanda this e la risposta, ma per qualche motivo il codice non funziona per me. (vedi sotto per l'errore che sto ricevendo)Metodo di test RSpec destroy (Esercitazioni Rails 3.2 Cap. 9, Es. 10)

Esercizio 10 del capitolo 9 del tutorial Rails ti chiede di: Modificare l'azione di distruzione [per utenti] per impedire agli utenti amministratori di distruggersi. (Scrivi prima un test.)

La parte difficile qui è il test, perché l'applicazione nasconde già il collegamento "Elimina" per l'utente corrente, quindi devi fare direttamente la richiesta http.

Ho ottenuto il codice funzionante e l'ho verificato rimuovendo il pezzo di codice che nasconde il collegamento di eliminazione per l'utente corrente. Abbastanza sicuro, se clicco sul link di eliminazione per l'utente attualmente loggato, mi reindirizza e mi dà il messaggio di avviso.

Da 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 

Il problema che sto avendo è iscritto i test per questo che invieranno la richiesta di eliminazione e chiamare il metodo distruggere. Ho provato la soluzione da Rspec test for destroy if no delete link, che sto copiando qui:

Da 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 

Ma quando ho eseguito questo, ottengo questo errore da 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)>' 

Quindi, le mie domande sono: 1) Perché questo codice sopra non funziona? 2) Come simulare un "delete" per chiamare l'azione destroy nel mio controller?

Ambiente: Mac OSX rubino 1.9.3p194 Rails 3.2.3

gemme per i test:
gruppo: prova di fare gioiello 'RSpec-guide', '2.9.0' gioiello 'capibara ',' 1.1.2 ' gem' rb-fsevent ',' 0.4.3.1 ',: require => false gem' growl ',' 1.0.3 ' gem' guard-spork ',' 0.3.2 ' gem 'spork', '0.9.0' gem 'factory_girl_rails', '1.4.0' fine

Ulteriori informazioni Ho provato tonnellata di modi per cercare di simulare cliccando sul link di cancellazione e nessuno sembra funzionare. Sto usando la gemma del debugger per vedere se il metodo destroy viene anche chiamato. Nel test che fa clic sul link per eliminare un utente diverso, il metodo destroy viene chiamato e funziona benissimo:

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

Ma nulla ho cercato di generare la richiesta di eliminazione diretta ha lavorato per chiamare il metodo destroy.

Grazie per il vostro aiuto!

Will

** UPDATE **

Ho provato il suggerimento di 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 

E ottenuto questo errore:

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)>' 

SOLUZIONE

L'ho capito dopo PER SEMPRE.

Ho dovuto utilizzare Rack :: Test per emettere la richiesta DELETE, ma Capybara e Rack :: Test non condividono la stessa MockSession, quindi ho dovuto inserire i cookie: remember_token e:! Sample_app_session e inserirli nella richiesta DELETE manualmente. Ecco cosa ha funzionato. (L'altro problema che stavo avendo, di seguito elencati, era che avevo una dichiarazione force_ssl che non è stato lasciando che la mia azione distruggere ottenere chiamato.

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 

ho avuto una dichiarazione force_ssl dopo i miei before_filters nella mia users_controller.rb e questo è stato in qualche modo gettare le cose fuori quindi non ho mai avuto modo di azione distruggere.

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 

Questi sono stati utili per arrivare a una soluzione

https://gist.github.com/484787

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

+0

Una cosa che ho appena scoperto, che non è il mio problema esattamente, ma potrebbe essere un problema * * è che il metodo let è pigro, così ho potuto immaginare rovinare la aspettano to_not cambiare la funzionalità. Così ho modificato il codice per usare let! quando si crea l'utente amministratore. –

+0

Qual è lo scenario che stai testando? L'amministratore accede, elimina il collegamento è nascosto, ma in qualche modo crea la richiesta di cancellazione? (solo chiedendo) –

+0

Buona domanda. Principalmente la risposta è che questo è stato un esercizio nell'esercitazione di Rails. È stato un buon esercizio, perché ho imparato tutto sui cookie, le richieste http, Capybara e Rack :: Test. Suppongo che potrei provare lo scenario in cui il codice per nascondere il link di eliminazione non funziona e voglio che il mio controller abbia un backup. –

risposta

5

Stai confondendo-rails RSpec richiesta specifiche che sono test di integrazione e vengono eseguiti in un browser e del controller simulato spec quale controller in isolamento di prova. delete(action, *args) (e get, post e così via) - è un metodo che simula la richiesta da ActionController :: TestCase, quindi non è disponibile nel test.

Quindi l'unica opzione è quella di simulare un clic in un browser. Non so come nascondi il tuo link di cancellazione, se l'html è lì ma nascosto dovresti essere in grado di cliccarlo. Se non è lì (rimosso dal lato server durante la generazione della vista) è possibile utilizzare capybara's page.execute_script (ma è necessario abilitare javascript per questo esempio :js => true). È possibile aggiungere il link:

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

o effettuare chiamata AJAX:

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

non prova, ma qualcosa di simile dovrebbe funzionare.

3

Prova questo:

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

Provato, stesso errore (vedi sopra) –

+0

Questo ha funzionato per me. Grazie! –

6

Ho risolto questo stesso problema usando la seguente:

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

Penso che funzioni solo nei test del controller, non nei test di integrazione. – Dean

+0

Questo test è valido, nonostante sia stato testato manualmente e non funzioni. (dovrebbe fallire) –

6

soluzione CallumD ha lavorato per me, e sembrava la più coerente con le tecniche consigliate nel resto del tutorial di Michael Hartl.Ma ho voluto stringere la sintassi un po 'per renderlo più coerente con le altre specifiche dello stesso tutorial:

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

Sono d'accordo. Ho fatto lo stesso. – KMcA

Problemi correlati