Yanıtlar:
Rad yolu =>
let(:dummy_class) { Class.new { include ModuleToBeTested } }
Alternatif olarak, test sınıfını modülünüzle genişletebilirsiniz:
let(:dummy_class) { Class.new { extend ModuleToBeTested } }
'Let' kullanmak, önceki (: each) içindeki kukla sınıfı tanımlamak için bir örnek değişkeni kullanmaktan daha iyidir
let(:dummy_class) { Class.new { include ModuleToBeTested } }
let(:class_instance) { (Class.new { include Super::Duper::Module }).new }
bu şekilde herhangi bir şekilde test için en sık kullanılan örnek değişkeni alırım.
include
benim için çalışmıyor ama extend
doeslet(:dummy_class) { Class.new { extend ModuleToBeTested } }
subject(:instance) { Class.new.include(described_class).new }
Mike ne dedi. İşte önemsiz bir örnek:
modül kodu ...
module Say
def hello
"hello"
end
end
spec parçası ...
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
include Say
Arama yapmak yerine DummyClass beyanının içinde olmamanız için herhangi bir neden var extend
mı?
extend
sınıf örneğine giriyor, yani sonra new
çağrıldı. Bunu daha önce new
yapsaydınız o zaman haklısıninclude
DummyClass
Sabiti neden tanımlarsınız ? Neden sadece @dummy_class = Class.new
? Artık gereksiz bir sınıf tanımıyla test ortamınızı kirletiyorsunuz. Bu DummyClass, özelliklerinizin her biri için tanımlanmıştır ve aynı yaklaşımı kullanmaya karar verdiğiniz ve bir şey içerebileceği DummyClass tanımını yeniden açtığınız bir sonraki spesifikasyonda (bu önemsiz örnekte tanım gerçek hayatta boş olsa da) vakaları kullanmanız bir noktada bir şeylerin eklenmesi ve bu yaklaşımın tehlikeli hale gelmesi muhtemeldir.)
Tek başına ya da sınıf alay ederek test edilebilen modüller için, aşağıdakiler boyunca bir şeyden hoşlanırım:
modülü:
module MyModule
def hallo
"hallo"
end
end
spec:
describe MyModule do
include MyModule
it { hallo.should == "hallo" }
end
Yuvalanmış örnek gruplarını ele geçirmek yanlış görünebilir, ancak ben küstahlığı seviyorum. Düşüncesi olan var mı?
let
@Metakungfu tarafından açıklanan yöntemi kullanmak daha iyi olduğunu düşünüyorum .
Rspec ana sayfasında daha iyi bir çözüm buldum. Görünüşe göre paylaşılan örnek grupları destekliyor. Gönderen https://www.relishapp.com/rspec/rspec-core/v/2-13/docs/example-groups/shared-examples !
Paylaşılan Örnek Gruplar
Paylaşılan örnek gruplar oluşturabilir ve bu grupları diğer gruplara dahil edebilirsiniz.
Ürününüzün büyük veya küçük tüm sürümleri için geçerli bazı davranışlarınız olduğunu varsayalım.
İlk olarak, “paylaşılan” davranışı hesaba katın:
shared_examples_for "all editions" do
it "should behave like all editions" do
end
end
Büyük ve Küçük sürümler için davranışı tanımlamanız gerektiğinde, paylaşılan davranışı it_should_behave_like () yöntemini kullanarak referans alın.
describe "SmallEdition" do
it_should_behave_like "all editions"
it "should also behave like a small edition" do
end
end
Başımın üstünden, test komut dosyanızda bir kukla sınıf oluşturabilir ve modülü buna dahil edebilir misiniz? Sonra kukla sınıfın davranışın beklediğiniz şekilde olduğunu test edin.
DÜZENLEME: Yorumlarda belirtildiği gibi modül, karıştırıldığı sınıfta bazı davranışların olmasını beklerse, o zaman bu davranışların mankenlerini uygulamaya çalışırım. Modülü görevlerini yerine getirmekten mutlu edecek kadar.
Bununla birlikte, bir modül ana bilgisayarından çok şey beklediğinde tasarımım hakkında biraz gergin olurdum ("host" diyoruz?) - Bir temel sınıftan miras almazsam veya enjekte edemezsem miras ağacına yeni işlevsellik o zaman bir modülün sahip olabileceği beklentileri en aza indirmeye çalışacağımı düşünüyorum. Benim endişem, tasarımımın hoş olmayan esnek olmayan bazı alanlar geliştirmeye başlamasıdır.
Kabul edilen cevap bence doğru cevaptır, ancak rpsec shared_examples_for
ve it_behaves_like
yöntemlerin nasıl kullanılacağına dair bir örnek eklemek istedim . Kod snippet'inde birkaç numaradan bahsediyorum, ancak daha fazla bilgi için bu relishapp-rspec-rehberine bakın .
Bununla modülünüzü içeren sınıflardan herhangi birinde test edebilirsiniz. Yani uygulamanızda kullandıklarınızı gerçekten test ediyorsunuz.
Bir örnek görelim:
# 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
Şimdi modülümüz için spec oluşturalım: 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
Ne dersin:
describe MyModule do
subject { Object.new.extend(MyModule) }
it "does stuff" do
expect(subject.does_stuff?).to be_true
end
end
Ben daha büyük ve çok kullanılan modüller için burada @Andrius tarafından önerilen "Paylaşılan Örnek Grupları" tercih gerektiğini öneriyoruz . Birden fazla dosyaya sahip olma gibi sorunlardan geçmek istemediğiniz basit şeyler için, kukla şeylerinizin görünürlüğü üzerinde maksimum kontrolü nasıl sağlayacağınız aşağıda açıklanmıştır (rspec 2.14.6 ile test edilmiştir, kodu kopyalayıp bir spec dosyası ve çalıştırın):
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
subject { dummy_class.new }
çalışıyor. Dava subject { dummy_class }
benim için çalışmıyor.
son işim, mümkün olduğunca az kablolama kullanarak
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
Keşke
subject {Class.new{include described_class}.new}
çalıştı, ancak çalışmıyor (Ruby MRI 2.2.3 ve RSpec :: Core 3.3.0'da olduğu gibi)
Failure/Error: subject {Class.new{include described_class}.new}
NameError:
undefined local variable or method `described_class' for #<Class:0x000000063a6708>
Açıkça açıklandığı gibi, bu kapsamda görünmez.
Modülünüzü test etmek için şunu kullanın:
describe MyCoolModule do
subject(:my_instance) { Class.new.extend(described_class) }
# examples
end
Birden fazla özellikte kullandığınız bazı şeyleri KURUTMAK için, paylaşılan bir bağlam kullanabilirsiniz:
RSpec.shared_context 'some shared context' do
let(:reused_thing) { create :the_thing }
let(:reused_other_thing) { create :the_thing }
shared_examples_for 'the stuff' do
it { ... }
it { ... }
end
end
require 'some_shared_context'
describe MyCoolClass do
include_context 'some shared context'
it_behaves_like 'the stuff'
it_behaves_like 'the stuff' do
let(:reused_thing) { create :overrides_the_thing_in_shared_context }
end
end
Kaynaklar:
Yardımcı türünü de kullanabilirsiniz
# api_helper.rb
module Api
def my_meth
10
end
end
# spec/api_spec.rb
require "api_helper"
RSpec.describe Api, :type => :helper do
describe "#my_meth" do
it { expect( helper.my_meth ).to eq 10 }
end
end
İşte belgeler: https://www.relishapp.com/rspec/rspec-rails/v/3-3/docs/helper-specs/helper-spec
Bunları içeren sınıfta bağımsız olan modül yöntemini test etmek için olası bir çözüm
module moduleToTest
def method_to_test
'value'
end
end
Ve bunun için spec
describe moduleToTest do
let(:dummy_class) { Class.new { include moduleToTest } }
let(:subject) { dummy_class.new }
describe '#method_to_test' do
it 'returns value' do
expect(subject.method_to_test).to eq('value')
end
end
end
Ve onları KURU test etmek istiyorsanız, paylaşılan_örnekler iyi bir yaklaşımdır
subject(:module_to_test_instance) { Class.new.include(described_class) }
. Aksi takdirde cevabınızla ilgili yanlış bir şey görmüyorum.
Birden fazla modülü test etmeniz gerekeceğinden bu tekrarlayan bir modeldir. Bu nedenle, bunun için bir yardımcı oluşturmak istenir.
Bulduğum bu yazı yapmak ancak site bir noktada aşağı alınabilir beri buraya Başa çıkıyorum açıklar.
Bu, nesne örneklerinin örnek yöntemini uygulamamasını önlemek içindir :: sınıftaki allow
yöntemleri denerken aldığınız hata dummy
.
İçinde spec/support/helpers/dummy_class_helpers.rb
module DummyClassHelpers
def dummy_class(name, &block)
let(name.to_s.underscore) do
klass = Class.new(&block)
self.class.const_set name.to_s.classify, klass
end
end
end
İçinde spec/spec_helper.rb
# skip this if you want to manually require
Dir[File.expand_path("../support/**/*.rb", __FILE__)].each {|f| require f}
RSpec.configure do |config|
config.extend DummyClassHelpers
end
Teknik özelliklerinizde:
require 'spec_helper'
RSpec.shared_examples "JsonSerializerConcern" do
dummy_class(:dummy)
dummy_class(:dummy_serializer) do
def self.represent(object)
end
end
describe "#serialize_collection" do
it "wraps a record in a serializer" do
expect(dummy_serializer).to receive(:represent).with(an_instance_of(dummy)).exactly(3).times
subject.serialize_collection [dummy.new, dummy.new, dummy.new]
end
end
end