[英]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.