English 中文(简体)
RSpec 未定义方法“ 无: NilClass 错误 ” 的更新属性
原标题:RSpec undefined method `update_attributes for nil:NilClass Error

I am trying to test my Rails app. Now I am testing my tiny controller but I have a problem. I use device to authentication. My controller which I test is like,

class WidgetsController < ApplicationController
  #skip_before_filter :authenticate, :only => [:new, :create]
  before_filter :authenticate, :except=>[:disabled]

  def index
    @widgets = current_user.widgets.all

    respond_to do |format|
      format.html # index.html.erb
      format.json { render json: @widgets }
    end
  end

  def new
    @widget = Widget.new

    respond_to do |format|
      format.html # new.html.erb
      format.json { render json: @widget }
    end
  end

  def create
    @widget = Widget.new(params[:widget])
    @widget.user_id = current_user.id
    @widget.score = 0
    @widget.total_score = 0
    @widget.click_number = 0
    @widget.average = 0

    respond_to do |format|
      if @widget.save
        format.html { redirect_to edit_widget_path(@widget), notice:  Widget was successfully created.  }
        format.json { render json: @widget, status: :created, location: @widget }
      else
        format.html { render action: "new" }
        format.json { render json: @widget.errors, status: :unprocessable_entity }
        raise  an error occurs when creation 
      end
    end
  end

  def show
    @widget = Widget.find_by_uuid(params[:uuid])

    respond_to do |format|
      format.html # show.html.erb
      format.json { render json: @widget }
    end
  end

  def edit
    @widget = Widget.find_by_uuid(params[:uuid])
  end

  def update
    @widget = Widget.find_by_uuid(params[:uuid])

    respond_to do |format|
      if @widget.update_attributes(params[:widget])
        format.html { redirect_to edit_widget_path(@widget), notice:  Widget was successfully updated.  }
        format.json { render json: @widget, status: :created, location: @widget }
      else
        format.html { render action: "edit" }
        format.json { render json: @widget.errors, status: :unprocessable_entity }
        raise  there is an error when alteration 
      end
    end
  end

  def destroy
    @widget = Widget.find_by_uuid(params[:uuid]).destroy

    respond_to do |format|
      format.html { redirect_to widgets_path }
      format.json { head :no_content }
    end
  end

  #generate widget
  def generate
    respond_to do |format|
      format.js {}
    end
  rescue
    #TODO add widget not found page
    render :template =>  application/widget_not_found , :status => :not_found
  end



  protected

  def authenticate
    unless current_user
      redirect_to root_url
    end
  end

end

我的路线文件 也许你可以看看它

Rwidget::Application.routes.draw do

  match  widgets  =>  widgets#index , :via => :get
  match  widgets/new  =>  widgets#new , :via => :get
  match  widgets  =>  widgets#create , :via => :post
  match  widgets/:uuid  =>  widgets#show , :via => :get
  get  widgets/:uuid/edit  =>  widgets#edit 
  match  widgets/:uuid  =>  widgets#update , :via => :put
  match  widgets/:uuid  =>  widgets#destroy , :via => :delete

  resources :widgets, :collection => [:destroy]

  match  rws/generate/:uuid  =>  rws#generate 

  get  rws/:uuid  =>  rws#show 
  put  rws/:uuid  =>  rws#update 
  post  rws/getUserInfo  =>  rws#getUserInfo 

  resources :authentications
  match  /auth/:provider/callback  =>  authentications#create 

  devise_for :users do
    get  /users/sign_out  =>  devise/sessions#destroy 
  end

  root :to => "home#index"

和我的控制器 spec. rb是

require  spec_helper 

describe WidgetsController do
  login_user

  describe "User" do
    before(:each) do
      @current_user = mock_model(User, :id => 1)
      @widget = mock_model(Widget, :user_id => 1)
      Widget.stub!(:current_user).and_return(@current_user)
      Widget.stub!(:widgets).and_return(@widget)
    end

    it "should have a current_user" do
      subject.current_user.should_not be_nil
    end
  end

  def mock_widget(stubs={})
    @mock_widget ||= mock_model(Widget, stubs).as_null_object
  end

  describe "Get index" do
    it "should get all widgets " do
      Widget.stub(:all) { [mock_widget] }
      get :index
      assigns(:widgets) == @widgets
    end
  end

  describe "Post widget" do
    it "creates a new widget" do
      Widget.stub(:new).with({ these  =>  params }) { mock_widget(:language => "en") }
      post :create, :widget => { these  =>  params }
      assigns(:widget) == @widgets
      response.should redirect_to (edit_widget_path(@mock_widget))
    end
  end

  describe "Get widget" do
    it "shows exact widget via id" do
      Widget.stub(:find).with("10") { [mock_widget] }
      get :show
      assigns(:mock_widget) == mock_widget
    end
  end

  describe "Put widget" do
    it "updates the widget attributes" do
      Widget.stub(:find).with("6") { [mock_widget] }
      mock_widget.should_receive(:update_attributes).with({ these  =>  params })
      put :update, :id => "6", :widget => { these  =>  params }
    end
  end

  describe "delete destroy" do
    it "destroys the requested widget" do
      Widget.stub(:find).with("10") { [mock_widget] }
      mock_widget.should_receive(:destroy)
      get :destroy, :id => "10"
    end
  end
end

Any I configured my spec helper to devise settings. My controller_macros.rb is

module ControllerMacros
  def login_user
    before(:each) do
      @request.env["devise.mapping"] = Devise.mappings[:user]
      user = Factory(:user)
      #user.confirm! # or set a confirmed_at inside the factory. Only necessary if you are using the confirmable module
      sign_in user
    end
  end
end

和我的服务员.rb

# This file is copied to spec/ when you run  rails generate rspec:install 
ENV["RAILS_ENV"] ||=  test 
require File.expand_path("../../config/environment", __FILE__)
require  rspec/rails 
require  rspec/autorun 

# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}

RSpec.configure do |config|
  # ## Mock Framework
  #
  # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
  #
  # config.mock_with :mocha
  # config.mock_with :flexmock
  # config.mock_with :rr
  config.include Devise::TestHelpers, :type => :controller
  config.extend ControllerMacros, :type => :controller
  config.include RequestMacros, :type => :request
  # Remove this line if you re not using ActiveRecord or ActiveRecord fixtures
  config.fixture_path = "#{::Rails.root}/spec/fixtures"

  # If you re not using ActiveRecord, or you d prefer not to run each of your
  # examples within a transaction, remove the following line or assign false
  # instead of true.
  config.use_transactional_fixtures = true

  # If true, the base class of anonymous controllers will be inferred
  # automatically. This will be the default behavior in future versions of
  # rspec-rails.
  config.infer_base_class_for_anonymous_controllers = false

end

最后,我的设计. rb 属于支持文件,

RSpec.configure do |config|
  config.include Devise::TestHelpers, :type => :controller
end

So. When I debug my controller.spec file, there is no current_user in update and delete describe blocks. When I run the ender$ rspec spec/controllers/widgets_controller_spec.rb I face with

Failures:

  1) WidgetsController Put widget updates the widget attributes
     Failure/Error: put :update, :id => "6", :widget => { these  =>  params }
     NoMethodError:
       undefined method `update_attributes  for nil:NilClass
     # ./app/controllers/widgets_controller.rb:60:in `block in update 
     # ./app/controllers/widgets_controller.rb:59:in `update 
     # ./spec/controllers/widgets_controller_spec.rb:52:in `block (3 levels) in <top (required)> 

  2) WidgetsController delete destroy destroys the requested widget
     Failure/Error: get :destroy, :id => "10"
     NoMethodError:
       undefined method `destroy  for nil:NilClass
     # ./app/controllers/widgets_controller.rb:72:in `destroy 
     # ./spec/controllers/widgets_controller_spec.rb:60:in `block (3 levels) in <top (required)> 

Finished in 0.4569 seconds
6 examples, 2 failures

Failed examples:

rspec ./spec/controllers/widgets_controller_spec.rb:49 # WidgetsController Put widget updates the widget attributes
rspec ./spec/controllers/widgets_controller_spec.rb:57 # WidgetsController delete destroy destroys the requested widget

我怎样修好它?

SOLUTION: I shifted findbyid to findbyuuid because of searching over uuid. After that, I need to return an array and I got it on this way,

Widget.stub(:find_by_uuid).with("6").and_return(mock_widget(:update_attributes => true))

结束了!

最佳回答

在你的测试中,你正在做

Widget.stub(:find).with("6")...

但您的应用程序代码调用 元件.find_by_uuid 。 您的应用程序代码应该会抽出您的应用程序代码的调用。 同样, 您的应用程序代码也期望设置 参数[:uid] , 但您的参数只是 参数[:id]

您还应该返回正确的返回值 - find_ by 返回一个对象,但您正在设置的根根返回一个数组。

Widget.stub(:find_by_uuid).with("6") { mock_widget}

将会使您的根块返回模拟部件, 而不是包含模拟部件的数组

问题回答

您只需查看元件堆放器# 更新, 才能知道哪个对象正在被调用更新(_atritute) 。

  def update
    @widget = Widget.find_by_uuid(params[:uuid])

    respond_to do |format|
      if @widget.update_attributes(params[:widget])

因此,基本上,这个呼吁是回到零:

Widget.find_by_uuid(params[:uuid])

您可以使用轨迹控制台调试此项 。





相关问题
uninitialized constant

I m trying to do this tutorial-> http://netbeans.org/kb/docs/ruby/rapid-ruby-weblog.html BUT its giving me this error: NameError in PostsController#index uninitialized constant PostsController::...

Rails - Too much logic in views?

I have an application used by several organizations and I want to check that users of one domain (a.domain.com) cannot edit users of another domain (b.domain.com). My question is where to put the ...

Controller specs in isolation mode and render :update

I am using RSpec for writing my controller tests/specs. I faced the problem, that the following code gets rendered: render :update do |page| page[ middle_content ].replace_html :partial => "...

registering event listeners in a controller

I m working on creating a simple MCV application, in order to understand MVC better. The problem I m having is registering event listeners. The way I see MVC is that the view dispatches events, the ...

System.Web.Mvc.Controller Initialize

i have the following base controller... public class BaseController : Controller { protected override void Initialize(System.Web.Routing.RequestContext requestContext) { if (...

热门标签