Ruby on rails 铁路及;RSpec-测试关注类方法
我有以下(简化的)问题:Ruby on rails 铁路及;RSpec-测试关注类方法,ruby-on-rails,ruby-on-rails-3,rspec,module,Ruby On Rails,Ruby On Rails 3,Rspec,Module,我有以下(简化的)问题: module HasTerms extend ActiveSupport::Concern module ClassMethods def optional_agreement # Attributes #---------------------------------------------------------------------------- attr_accessible :agrees_to_ter
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中测试这个模块的好方法——如果我只是创建一个虚拟类,那么当我尝试检查验证是否正常工作时,就会出现活动记录错误。还有其他人遇到过这个问题吗?您可以将测试留在包含该模块的类中,从而隐式地测试该模块。或者,您可以在虚拟类中包含其他必需的模块。例如,AR模型中的
validates
方法由ActiveModel::Validations
提供。因此,对于您的测试:
class DummyClass
include ActiveModel::Validations
include HasTerms
end
根据HasTerms
模块中隐含依赖的依赖关系,可能需要引入其他模块 查看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
我自己也在努力解决这个问题,并想出了以下解决方案,这与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
在Aaron K的优秀答案的基础上,RSpec提供了一些很好的技巧,可用于
descripted\u 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
下面是另一个示例(使用Factorygirl的“创建”方法和共享的示例)
关注点规范
可评论的关注
并且restranu规范可能看起来像这样(我不是Rspec大师,所以不要认为我编写规范的方式是好的-最重要的是在开始时):
我同意隐式测试很容易,但我觉得我也需要能够显式测试。这在编写我的类还没有使用过的类函数时尤其重要。像
DummyClass
这样的东西是你当时正在寻找的替代品。这显然是最好的答案。可以是exp在虚拟情况下合法,并在父类的规范中测试相同的API。这在处理任何“灵活”API(读取:method_missing)时会产生很大的不同。只有在“真实”(非虚拟)中使用它时,才会想到一些情况类,共享示例将很好地在每个必要的上下文中练习代码。当您的模块添加动态属性时,这会出现问题。假设您的模块允许类方法:允许上传:csv
,它添加了csv\u文件路径
和csv\u文件大小
等方法。但您有另一种模式l调用上载文件:附件。现在您的“文件”充当上载文件“规范将失败,因为一个正在添加csv\u文件路径
,另一个有附件文件路径
。出于这个原因,我觉得在很多情况下,使用一个虚拟类来测试模块的行为将最适合您的需要,如@Martijn的answer@nzifnab明确地说,模块没有添加方法,子类是显式的。在这里,共享示例是否合适是特定于代码库的判断调用。但是,您仍然可以以这种方式使用它们。可以向他们传递信息,就像您在通话中所做的那样:它的行为类似于“作为上传”,csv
@AaronK Oh!我不知道你可以传递一个值给它。事实证明,您还可以给它一个块,为共享示例提供一些上下文。类似于:它的行为类似于“上传”;:csv do;主题{Upload.new(文件:some_dummy_文件)};结束
谢谢你的提示:p这是我找到的唯一有效的解决方案。当使用命名的虚拟类时,如果调用修改该类的方法,那么下一个规范也将看到该类的修改,那么您的规范将开始相互渗透。尽管我的更像是let(:dummy_class){class.new(…)}
,而不是直接插入it
块。
#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
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