2

我有这个与使用 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. 这是关于 Artist 控制器的失败测试:

    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
    

当我运行测试时,这是我在上述测试中遇到的错误:

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 结果。

4

1 回答 1

2

ActiveRecord::Base#errors(ie Artist#errors) 不是一个简单的哈希。它应该是一个实例ActiveModel::Errors。您正在使用哈希对它进行存根,而 ActiveRecord 正在尝试调用add_on_blank它,但它失败了。

我认为根本不save调用is_valid?,我怀疑它正在运行验证,然后尝试调用add_on_blank以附加错误,但由于你已经 stubbed errors,那是失败的。

这并不是测试控制器的好方法。它对Artist. 你也在测试根本不属于控制器的东西;errors在动作的任何地方都没有引用。控制器中唯一值得测试的行为是它是否创建了Artist; 如果Artist保存失败,它会用它呈现 JSON;如果保存成功,它会重定向。这就是控制器的全部责任。

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

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

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

如果你想尝试测试你的方式,你可以手动创建一个实例ActiveModel::Errors并填充它,或者在它上面的存根方法,然后存根Artist.any_instance.stubs(:errors)返回你的具有ActiveModel::Errors-compatible 行为的模拟,但这是很多模拟。

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

于 2013-03-29T03:38:34.777 回答