0

我真的在为 Rspec DSL 苦苦挣扎!我在 SO 和互联网上阅读了很多内容,所以我发布了我的特定问题,因为我虚弱的头脑无法找到解决方案。

在我的控制器中有一个更新用户电子邮件的 post 方法。工作正常,但我在规范上苦苦挣扎,因为我得到的只是 NilClass 的未定义方法(即使我已经尝试对每个对象和方法等进行存根)。

users_controller.rb

def update_user_email
   @user = User.find_by_id(params[:id])
   new_email = params[:user][:new_email].downcase.strip
   user_check = User.find_by_email('new_email')
   if user_check.blank?
     @user.email = new_email
     @user.save
     flash[:notice] = "Email updated to #{new_email}"
   else
     flash[:alert] = "This email is already being used by someone else!"
   end

   respond_with @user do |format|
     format.html { redirect_to admin_user_path(@user) }
     format.json { head :no_content }
   end
 end

这是我正在尝试编写的规范。如果不是这个,我应该写什么测试,我能做些什么来防止 NilClass 错误上的未定义方法!

users_controller_spec.rb

describe Admin::UsersController do
  let!(:user) { FactoryGirl.create(:user, password: 'oldpass', email: 'bar@foo.com') }
  ...
  describe "admin actions for each user" do
    it "resets user email" do
      post :update_user_email, {user: {new_email: 'foo@bar.com'} }
      response.status.should == 200
    end
  end
 ...
end

和错误:

Admin::UsersController admin actions for each user resets user email
Failure/Error: post :update_user_email, {user: {new_email: 'foo@bar.com'} }
NoMethodError:
   undefined method `email=' for nil:NilClass
4

3 回答 3

1

问题是您还需要传递User要更新的 id。失败的行是@user.email = new_email, since@user是 nil。

要立即通过测试,您需要将post方法更改为:

post :update_user_email, {id:'bar@foo.com', user: {new_email: 'foo@bar.com'} }

顺便说一句,可以说在UsersController#update方法中实际执行此操作可能会更好,以维护 RESTful 路由。至于强制执行唯一的电子邮件地址 - 在User课堂上进行验证可能会更好。

于 2013-10-11T00:45:49.220 回答
1

失败的行是:

@user = User.find_by_id(params[:id)

由于您在测试期间没有传递 id,因此找不到用户,因此您尝试在 nil 上调用 email=。这是清理控制器和测试的方法。

class YourController < ApplicationController
  before_filter :find_user, only: [:update_user_email]

  def update_user_email
    new_email = params[:user][:new_email].downcase.strip
    user_check = User.where(email: new_email)
    if user_check.blank?
       @user.email = new_email
       @user.save
       flash[:notice] = "Email updated to #{new_email}"
    else
       flash[:alert] = "This email is already being used by someone else!"
    end

    respond_with @user do |format|
      format.html { redirect_to admin_user_path(@user) }
      format.json { head :no_content }
    end
  end

  def find_user
    @user = User.find(params[:id])
    rescue ActiveRecord::RecordNotFound
      flash[:error] = "It looks like that user does not exist"
      # redirect or render
  end
end


# your test


describe "admin actions for each user" do
  it "resets user email" do
    post :update_user_email, id: user.id, user: {new_email: 'foo@bar.com'}
    response.status.should == 200
  end
end

您可能还需要考虑将逻辑移出控制器并移入服务对象。该控制器方法有点长。

于 2013-10-11T00:46:49.713 回答
0

在您的情况下post :update_user_email,您没有传递 :id... 所以@user = User.find_by_id...没有找到用户,所以 @user 是一个 nil 对象。

post :update_user_email, id: user.id, {user: {new_email: 'foo@bar.com'} }
于 2013-10-11T00:45:28.053 回答