在rspec中testing模块
rspectesting模块的最佳实践是什么? 我有一些模块,包括在几个模型,现在我只是有重复testing每个模型(几乎没有差异)。 有没有办法干掉它?
rad方式=>
let(:dummy_class) { Class.new { include ModuleToBeTested } }
或者,您可以使用您的模块扩展testing类:
let(:dummy_class) { Class.new { extend ModuleToBeTested } }
使用'let'比使用实例variables在before(:each)中定义虚拟类要好,
什么时候使用RSpec let()?
什么话筒说。 这是一个简单的例子:
模块代码…
module Say def hello "hello" end end
spec片段…
class DummyClass end before(:each) do @dummy_class = DummyClass.new @dummy_class.extend(Say) end it "get hello string" do expect(@dummy_class.hello).to eq "hello" end
对于可以单独testing或嘲笑课程的模块,我喜欢以下几点:
模块:
module MyModule def hallo "hallo" end end
规格:
describe MyModule do include MyModule it { hallo.should == "hallo" } end
劫持嵌套的示例组似乎是错误的,但我喜欢简洁。 有什么想法吗?
我在rspec主页find了一个更好的解决scheme。 显然它支持共享示例组。 从https://www.relishapp.com/rspec/rspec-core/v/2-13/docs/example-groups/shared-examples !
共享示例组
您可以创build共享示例组,并将这些组包括到其他组中。
假设您有一些行为适用于您的产品的所有版本,无论大小。
首先,考虑“共享”行为:
shared_examples_for "all editions" do it "should behave like all editions" do end end
那么当您需要为大版本和小版本定义行为时,请使用it_should_behave_like()方法引用共享行为。
describe "SmallEdition" do it_should_behave_like "all editions" it "should also behave like a small edition" do end end
关于我的头顶,你可以在testing脚本中创build一个虚拟类,并将模块包含进去吗? 然后testing虚拟类的行为是否符合您的期望。
编辑:如果正如在评论中指出的那样,模块希望某些行为出现在它所混合的类中,那么我会尝试实现这些行为的假象。 正好足以让模块乐意履行职责。
这就是说,当我的devise模块期望从它的主机(我们说“主机”?)的一大堆时,我会有点紧张 – 类 – 如果我还没有从基类inheritance或不能inheritance树中的新function,那么我想我会尽量减less任何模块可能有这样的期望。 我的担心是,我的devise会开始发展一些令人不愉快的僵化的地方。
接受的答案是我想的正确答案,但是我想添加一个如何使用rpsecs shared_examples_for
和it_behaves_like
方法的示例。 我在代码片段中提到了一些技巧,但更多的信息请参阅这个relishapp-rspec-guide 。
有了这个,你可以在任何包含它的类中testing你的模块。 所以你真的在testing你在你的应用程序中使用什么。
我们来看一个例子:
# Lets assume a Movable module module Movable def self.movable_class? true end def has_feets? true end end # Include Movable into Person and Animal class Person < ActiveRecord::Base include Movable end class Animal < ActiveRecord::Base include Movable end
现在让我们为我们的模块创build规格: movable_spec.rb
shared_examples_for Movable do context 'with an instance' do before(:each) do # described_class points on the class, if you need an instance of it: @obj = described_class.new # or you can use a parameter see below Animal test @obj = obj if obj.present? end it 'should have feets' do @obj.has_feets?.should be_true end end context 'class methods' do it 'should be a movable class' do described_class.movable_class?.should be_true end end end # Now list every model in your app to test them properly describe Person do it_behaves_like Movable end describe Animal do it_behaves_like Movable do let(:obj) { Animal.new({ :name => 'capybara' }) } end end
关于什么:
describe MyModule do subject { Object.new.extend(MyModule) } it "does stuff" do expect(subject.does_stuff?).to be_true end end
我build议对于更大和更多使用的模块,应该select@Andrius 在这里build议的“共享示例组”。 对于简单的东西,你不想经历多个文件的麻烦等,这里是如何确保最大限度地控制你的虚拟东西的可见性(用rspec 2.14.6testing,只需将代码复制并粘贴到spec文件并运行它):
module YourCoolModule def your_cool_module_method end end describe YourCoolModule do context "cntxt1" do let(:dummy_class) do Class.new do include YourCoolModule #Say, how your module works might depend on the return value of to_s for #the extending instances and you want to test this. You could of course #just mock/stub, but since you so conveniently have the class def here #you might be tempted to use it? def to_s "dummy" end #In case your module would happen to depend on the class having a name #you can simulate that behaviour easily. def self.name "DummyClass" end end end context "instances" do subject { dummy_class.new } it { subject.should be_an_instance_of(dummy_class) } it { should respond_to(:your_cool_module_method)} it { should be_a(YourCoolModule) } its (:to_s) { should eq("dummy") } end context "classes" do subject { dummy_class } it { should be_an_instance_of(Class) } it { defined?(DummyClass).should be_nil } its (:name) { should eq("DummyClass") } end end context "cntxt2" do it "should not be possible to access let methods from anohter context" do defined?(dummy_class).should be_nil end end it "should not be possible to access let methods from a child context" do defined?(dummy_class).should be_nil end end #You could also try to benefit from implicit subject using the descbie #method in conjunction with local variables. You may want to scope your local #variables. You can't use context here, because that can only be done inside #a describe block, however you can use Porc.new and call it immediately or a #describe blocks inside a describe block. #Proc.new do describe "YourCoolModule" do #But you mustn't refer to the module by the #constant itself, because if you do, it seems you can't reset what your #describing in inner scopes, so don't forget the quotes. dummy_class = Class.new { include YourCoolModule } #Now we can benefit from the implicit subject (being an instance of the #class whenever we are describing a class) and just.. describe dummy_class do it { should respond_to(:your_cool_module_method) } it { should_not be_an_instance_of(Class) } it { should be_an_instance_of(dummy_class) } it { should be_a(YourCoolModule) } end describe Object do it { should_not respond_to(:your_cool_module_method) } it { should_not be_an_instance_of(Class) } it { should_not be_an_instance_of(dummy_class) } it { should be_an_instance_of(Object) } it { should_not be_a(YourCoolModule) } end #end.call end #In this simple case there's necessarily no need for a variable at all.. describe Class.new { include YourCoolModule } do it { should respond_to(:your_cool_module_method) } it { should_not be_a(Class) } it { should be_a(YourCoolModule) } end describe "dummy_class not defined" do it { defined?(dummy_class).should be_nil } end
我最近的工作,尽可能使用硬连线
require 'spec_helper' describe Module::UnderTest do subject {Object.new.extend(described_class)} context '.module_method' do it {is_expected.to respond_to(:module_method)} # etc etc end end
我希望
subject {Class.new{include described_class}.new}
工作,但它不(如在Ruby MRI 2.2.3和RSpec :: Core 3.3.0)
Failure/Error: subject {Class.new{include described_class}.new} NameError: undefined local variable or method `described_class' for #<Class:0x000000063a6708>
显然describe_class在该范围内是不可见的。
您也可以使用助手types
# api.rb module Api def my_meth 10 end end
# spec/api_spec.rb describe Api, type: :helper do it { expect( helper.my_meth ).to eq 10 } end
以下是文档: https : //www.relishapp.com/rspec/rspec-rails/v/3-3/docs/helper-specs/helper-spec
- 如何更改用于在rails中testing的默认“www.example.com”域?
- 如何在运行rspec时将Rails.logger打印到控制台/标准输出?
- object.any_instance should_receive vs expect()来接收
- 如何在RSpec中运行单个testing/ spec文件?
- 找不到生成器rspec:install。
- RSpec中的mock和mock_model有什么区别?
- 使用RSpec来检查是否有其他对象的实例
- Selenium :: WebDriver :: Error :: JavascriptError:等待evaluate.js加载失败的Firefox 23
- 如何发送RubyMine通知咆哮?