简体   繁体   English

如何使用RSpec在Rails中测试update_attributes函数?

[英]How can I test update_attributes function in Rails with RSpec?

In my Rails 4 app I have this update action: 在我的Rails 4应用程序中,我有以下update操作:

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

It basically makes sure that a user cannot simply save any new email address. 它基本上可以确保user不能简单地保存任何新的电子邮件地址。 He will have to confirm it first by clicking on a link in an email we send to him. 他将必须首先通过单击我们发送给他的电子邮件中的链接来进行确认。

This works great, however, for some reason I haven't found a way to test it. 但是,由于某些原因,我没有找到一种测试方法,因此效果很好。

The following RSpec test keeps failing no matter what I do: 无论我做什么,以下RSpec测试都会失败:

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

@user.new_email is always nil and the test always fails. @user.new_email始终nil ,并且测试始终失败。 What am I missing here? 我在这里想念什么?

Re-factoring my update action wouldn't be a problem at all. 重构我的update操作根本不是问题。 Maybe there's a better way? 也许有更好的方法? Thanks for any help. 谢谢你的帮助。

I would write the spec like so: 我会这样写规范:

let(:user) { FactoryGirl.create(:user, email: "john@doe.com") }

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

When it comes to the controller action itself the problem is that the new_email property is never saved to the database, besides that its kind of a mess. 当涉及到控制器动作本身时,问题在于new_email属性从未被保存到数据库,除了它有点混乱。 You can clean it up by using ActiveRecord::Dirty which tracks attribute changes in the model: 您可以使用ActiveRecord :: Dirty对其进行清理,该方法可跟踪模型中的属性更改:

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

Putting this business logic in the model also lets you test it separatly: 将此业务逻辑放入模型中,还可以分别进行测试:

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: ”xxx@example.com”)
        user.reload
      end.to_not change(user, :email)
    end

    it "updates the new_email" do
      expect do
        user.update_with_email(email: ”xxx@example.com”)
        user.reload
      end.to change(user, :new_email).to('xxx@example.com')
    end
  end
end

This lets you keep the controller nice and skinny: 这使您可以保持控制器的美观和皮包骨头:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM