如何使用Mocha和rspec测试模型错误?

时间:2013-03-29 02:44:58

标签: ruby-on-rails-3 rspec mocha rails-models

我遇到与使用Mocha测试模型错误相关的问题:

  1. 这是我的控制器(Api / Artist控制器):

    class Api::ArtistsController < ApplicationController
       respond_to :json
    
       def create
         artist = Artist.new(params[:artist])
         if artist.save <-- This is where the test fails
           render :json=>artist
         else
           respond_with artist
         end
       end
    end
    
  2. 这是我的模特(艺术家模特):

    class Artist < ActiveRecord::Base
       include Core::BaseModel
       attr_accessible :name
       has_many :albums
       validates :name, :presence=>true, :uniqueness=>{:case_sensitive=> false}
       default_scope where :deleted=>false
    end
    
  3. 这是关于艺术家控制器失败的测试:

    it "should not save a duplicated artist" do
      Artist.any_instance.stubs(:is_valid?).returns(false)
      Artist.any_instance.stubs(:errors).returns({:name=>[I18n.t('activerecord.errors.messages.taken')]})
      post :create, :format => :json
    
      expect(response).not_to be_success
      expect(response.code).to eq("422")
    
      results = JSON.parse(response.body)
      expect(results).to include({
          "errors"=>{
            "name"=>[I18n.t('activerecord.errors.messages.taken')]
          }
        })
    end
    
  4. 当我运行测试时,这是我在上述测试中得到的错误:

    Failure/Error: post :create, :format => :json
         NoMethodError:
           undefined method `add_on_blank' for {}:Hash
         # ./app/controllers/api/artists_controller.rb:17:in `create'
         # ./spec/controllers/api/artists_controller_spec.rb:56:in `block (3 levels) in <top (required)>'
    

    我开始使用Mocha,因此当我想测试重复名称的验证时,我不知道是否有测试特定情况的json结果的方法。< / p>

1 个答案:

答案 0 :(得分:1)

ActiveRecord::Base#errors(即Artist#errors)不是简单的哈希。它应该是ActiveModel::Errors的一个实例。你用哈希来对它进行存根,而ActiveRecord正试图在它上面调用add_on_blank,这是失败的。

我认为save根本不会调用is_valid?,我怀疑它正在运行验证,然后尝试调用add_on_blank来追加错误,但是因为你已经存根了out errors,那是失败的。

这不是测试控制器的好方法。它对Artist的内部做出了太多假设。你也在测试那些不属于控制器的东西;操作中的任何位置都未引用errors。在控制器中唯一值得测试的行为是它是否创建Artist;如果Artist无法保存,它会用它呈现JSON;如果保存成功,则重定向。这完全是控制人的责任。

如果要测试错误是以某种方式呈现的,则应编写单独的视图规范。如果要测试缺少的字段是否会产生错误,则应编写模型规范。如果您不想编写视图规范,依靠模型填充errors(在模型规范中测试)仍然足够,并且在您的控制器中,只需测试render被调用将json设置为Artist个实例。

一般来说,最好尽可能避免存根,但在这种情况下,我考虑使用存根的唯一事情是Artist.new返回模拟,并且该模拟上的save返回假。然后我会检查以确保它使用模拟渲染。

更简单的选择是创建一个实际的Artist记录,然后使用重复的参数调用post以触发验证失败。缺点是您访问了数据库,并且在控制器规范中避免使用它是值得称赞的,但通常更方便。如果你想避免控制器规格中的数据库命中,你可以在Capybara功能规范中这样做。

如果您想尝试按照自己的方式进行测试,可以手动创建ActiveModel::Errors的实例,并在其上填充该实例或存根方法,并将Artist.any_instance.stubs(:errors)存根,以返回您的模拟{ {1}} - 兼容的行为,但这是很多嘲弄。

最后一个提示:不要使用ActiveModel::Errors。使用post :create, :format => :json生成真正的Ajax请求,而不是依赖xhr :post, :create参数。这是对路由和响应代码的更可靠的测试。