Rails和RSpec – 测试关注类方法
我有以下(简化)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中测试这个模块的好方法 – 如果我只创建一个虚拟类,当我尝试检查validation是否正常时,我会收到活动记录错误。 还有其他人遇到过这个问题吗?
查看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
您可以通过将测试留在包含此模块的类中来隐式地测试模块。 或者,您可以在虚拟类中包含其他必需的模块。 例如,AR模型中的validates
方法由ActiveModel::Validations
。 所以,对于你的测试:
class DummyClass include ActiveModel::Validations include HasTerms end
根据您在HasTerms
模块中隐式依赖的依赖关系,可能需要引入其他模块。
我正在努力解决这个问题,并想出以下解决方案,这很像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
这是另一个例子(使用Factorygirl的“创建”方法“和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
基于Aaron K 在这里的优秀答案,你可以使用一些很好的技巧,RSpec提供的described_class
使你的方法无处不在,让工厂为你工作。 这是我最近为应用程序制作的共享示例的片段:
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
- 使用open_id_authentication插件时,如何在RSpec用户故事/ Cucumber中伪造OpenID登录
- 为什么我的RSpec测试失败,但我的应用程序正在运行?
- 在RSpec请求规范中对控制器操作进行存根
- 如何使用RSpec测试`rand()`?
- Rails – ruby / 2.1.0 / minitest / parallel_each.rb:67:in”:类’Minitest :: Unit’的未定义方法’_run_suites’(NameError)
- 使用Ruby 1.8.7的标题案例
- rspec,未知属性问题
- 在rails 3.1上,Rspec中的Cookie不会存在
- 在rails集成规范中向同一个控制器发出两个请求