2017-10-29 4 views
0

In meiner Rails 4 app Ich habe diese update Aktion:Wie kann ich update_attributes Funktion in Rails mit RSpec testen?

class UsersController < ApplicationController 

    ... 

    def update 
    current_email = @user.email 
    new_email = user_params[:email].downcase 
    if @user.update_attributes(user_params) 
     if current_email != new_email 
     @user.email = current_email 
     @user.new_email = new_email.downcase 
     @user.send_email_confirmation_email 
     flash[:success] = "Please click the link we've just sent you to confirm your new email address." 
     else 
     flash[:success] = "User updated." 
     end 
     redirect_to edit_user_path(@user) 
    else 
     render :edit 
    end 
    end 

    ... 

end 

Es macht im Grunde sicher, dass ein user kann einfach keine neue E-Mail-Adresse speichern. Er muss dies zuerst bestätigen, indem er auf einen Link in einer E-Mail klickt, die wir an ihn senden.

Das funktioniert gut, aber aus irgendeinem Grund habe ich keine Möglichkeit gefunden, es zu testen.

Die folgende RSpec Test hält versagt, egal, was ich tue:

it "changes the user's new_email attribute" do 
    @user = FactoryGirl.create(:user, :email => "[email protected]") 
    patch :update, :id => @user, :user => FactoryGirl.attributes_for(:user, :email => "[email protected]") 
    expect(@user.reload.new_email).to eq("[email protected]") 
end 

@user.new_email ist immer nil und der Test nicht bestanden immer. Was fehlt mir hier?

Re-Factoring meiner update Aktion wäre überhaupt kein Problem. Vielleicht gibt es einen besseren Weg? Danke für jede Hilfe.

Antwort

1

würde ich die Spezifikation wie so schreiben:

let(:user) { FactoryGirl.create(:user, email: "[email protected]") } 

it "changes the user's new_email attribute" do 
    expect do 
    patch :update, id: @user, user: FactoryGirl.attributes_for(:user, email: "[email protected]") 
    user.reload 
    end.to change(user, :new_email).from("[email protected]").to("[email protected]") 
end 

Wenn es um die Controller-Aktion selbst das Problem ist, dass die new_email Eigenschaft nie in der Datenbank gespeichert ist, außer, dass seine Art ein Durcheinander. Sie können es durch die Verwendung ActiveRecord::Dirty aufzuräumen, das Attribut Änderungen im Modell verfolgt:

class User < ApplicationRecord 
    # updates user with attrs but moves a new email to the `new_email` 
    # column instead 
    def update_with_email(attrs, &block) 
    update(attrs) do |record| 
     if record.email_changed? 
     record.new_email = record.email.downcase 
     record.restore_attribute!(:email) 
     end 
     # keeps the method signature the same as the normal update 
     yield record if block_given? 
    end 
    end 
end 

Umsetzung dieser Business-Logik in dem Modell lässt Sie auch separat testen:

RSpec.describe User, type: :model do 
    describe "#update_with_email" do 
    let(:user) { FactoryGirl.create(:user) } 

    it "does not change the email attribute" do 
     expect do 
     user.update_with_email(email: ”[email protected]”) 
     user.reload 
     end.to_not change(user, :email) 
    end 

    it "updates the new_email" do 
     expect do 
     user.update_with_email(email: ”[email protected]”) 
     user.reload 
     end.to change(user, :new_email).to('[email protected]') 
    end 
    end 
end 

Auf diese Weise können Sie den Controller halten nett und dünn:

def update 
    if @user.update_with_email(user_params) 
    if @user.new_email_changed? 
     @user.send_email_confirmation_email 
     flash[:success] = "Please click the link we've just sent you to confirm your new email address." 
    else 
     flash[:success] = "User updated." 
    end 
    # You probably want to redirect the user away from the form instead. 
    redirect_to edit_user_path(@user) 
    else 
    render :edit 
    end 
end 
+0

Danke! Das sieht sehr vielversprechend aus und viel besser als mein ursprünglicher Code! Leider habe ich "undefinierte Methode" geändert? für "": String' in meinem Controller in Zeile 'if @ user.new_email.changed?'. Ich habe deinen Code Wort für Wort kopiert. Was vermisse ich? – Tintin81

+0

Ich habe schon verschiedene Dinge ausprobiert, habe manuell auch das 'ActiveModel :: Dirty' eingebaut, aber ohne Erfolg. Ich benutze Rails 4.2.8. – Tintin81

+1

Oh Ups sollte .new_email_changed sein? – max

Verwandte Themen