24

我有以下(简化的)Rails 问题:

module HasTerms
  extend ActiveSupport::Concern

  module ClassMethods
    def optional_agreement
      # Attributes
      #----------------------------------------------------------------------------
      attr_accessible :agrees_to_terms
    end

    def required_agreement
      # Attributes
      #----------------------------------------------------------------------------
      attr_accessible :agrees_to_terms

      # Validations
      #----------------------------------------------------------------------------
      validates :agrees_to_terms, :acceptance => true, :allow_nil => :false, :on => :create
    end
  end
end

但是,我想不出在 RSpec 中测试此模块的好方法 - 如果我只是创建一个虚拟类,当我尝试检查验证是否正常工作时,我会收到活动记录错误。有没有其他人遇到过这个问题?

4

5 回答 5

46

查看 RSpec共享示例

这样,您可以编写以下内容:

# spec/support/has_terms_tests.rb
shared_examples "has terms" do
   # Your tests here
end


# spec/wherever/has_terms_spec.rb
module TestTemps
  class HasTermsDouble
    include ActiveModel::Validations
    include HasTerms
  end
end

describe HasTerms do

  context "when included in a class" do
    subject(:with_terms) { TestTemps::HasTermsDouble.new }

    it_behaves_like "has terms"
  end

end


# spec/model/contract_spec.rb
describe Contract do

  it_behaves_like "has terms"

end
于 2013-05-09T14:37:24.970 回答
7

您可以通过将测试留在包含此模块的类中来隐式测试模块。或者,您可以在虚拟类中包含其他必需模块。例如,validatesAR 模型中的方法由ActiveModel::Validations. 因此,对于您的测试:

class DummyClass
  include ActiveModel::Validations
  include HasTerms
end

根据您在模块中隐式依赖的依赖关系,您可能需要引入其他HasTerms模块。

于 2013-05-09T02:29:47.707 回答
7

我自己也在为此苦苦挣扎,并想出了以下解决方案,这很像 rossta 的想法,但使用了匿名类:

it 'validates terms' do
  dummy_class = Class.new do
    include ActiveModel::Validations
    include HasTerms

    attr_accessor :agrees_to_terms

    def self.model_name
      ActiveModel::Name.new(self, nil, "dummy")
    end
  end

  dummy = dummy_class.new
  dummy.should_not be_valid
end
于 2013-09-03T08:39:19.120 回答
4

这是另一个示例(使用 Factorygirl 的“create”方法和 shared_examples_for)

关注规范

#spec/support/concerns/commentable_spec
require 'spec_helper'
shared_examples_for 'commentable' do
  let (:model) { create ( described_class.to_s.underscore ) }
  let (:user) { create (:user) }

  it 'has comments' do
    expect { model.comments }.to_not raise_error
  end
  it 'comment method returns Comment object as association' do
    model.comment(user, "description")
    expect(model.comments.length).to eq(1)
  end
  it 'user can make multiple comments' do
    model.comment(user, "description")
    model.comment(user, "description")
    expect(model.comments.length).to eq(2)
  end
end

可评论的关注

module Commentable
  extend ActiveSupport::Concern
  included do
    has_many :comments, as: :commentable
  end

  def comment(user, description)
    Comment.create(commentable_id: self.id,
                  commentable_type: self.class.name,
                  user_id: user.id,
                  description: description
                  )
  end

end

restraunt_spec 可能看起来像这样(我不是 Rspec 专家,所以不要认为我编写规范的方式很好 - 最重要的是在开头):

require 'rails_helper'

RSpec.describe Restraunt, type: :model do
  it_behaves_like 'commentable'

  describe 'with valid data' do
    let (:restraunt) { create(:restraunt) }
    it 'has valid factory' do
      expect(restraunt).to be_valid
    end
    it 'has many comments' do
      expect { restraunt.comments }.to_not raise_error
    end
  end
  describe 'with invalid data' do
    it 'is invalid without a name' do
      restraunt = build(:restraunt, name: nil)
      restraunt.save
      expect(restraunt.errors[:name].length).to eq(1)
    end
    it 'is invalid without description' do
      restraunt = build(:restraunt, description: nil)
      restraunt.save
      expect(restraunt.errors[:description].length).to eq(1)
    end
    it 'is invalid without location' do
      restraunt = build(:restraunt, location: nil)
      restraunt.save
      expect(restraunt.errors[:location].length).to eq(1)
    end
    it 'does not allow duplicated name' do
      restraunt = create(:restraunt, name: 'test_name')
      restraunt2 = build(:restraunt, name: 'test_name')
      restraunt2.save
      expect(restraunt2.errors[:name].length).to eq(1)
    end
  end
end
于 2015-06-11T12:49:56.677 回答
3

基于 Aaron K在此处的出色回答,您可以使用described_classRSpec 提供的一些不错的技巧来使您的方法无处不在,并使工厂为您工作。这是我最近为应用程序制作的共享示例的片段:

shared_examples 'token authenticatable' do
  describe '.find_by_authentication_token' do
    context 'valid token' do
      it 'finds correct user' do
        class_symbol = described_class.name.underscore
        item = create(class_symbol, :authentication_token)
        create(class_symbol, :authentication_token)

        item_found = described_class.find_by_authentication_token(
          item.authentication_token
        )

        expect(item_found).to eq item
      end
    end

    context 'nil token' do
      it 'returns nil' do
        class_symbol = described_class.name.underscore
        create(class_symbol)

        item_found = described_class.find_by_authentication_token(nil)

        expect(item_found).to be_nil
      end
    end
  end
end
于 2013-09-28T19:48:25.337 回答