ruby - اختبار وحدات في rspec
unit-testing (8)
أحد الحلول الممكنة لاختبار طريقة الوحدة النمطية التي تكون مستقلة عن الفئة التي ستشملها
module moduleToTest
def method_to_test
'value'
end
end
والمواصفات لذلك
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
وإذا كنت تريد اختبار الجاف ، فإن shared_examples هو shared_examples جيد
ما هي أفضل الممارسات في اختبار الوحدات النمطية في rspec؟ لدي بعض الوحدات التي تم تضمينها في نماذج قليلة ، والآن لدي ببساطة اختبارات مكررة لكل طراز (مع بعض الاختلافات). هل هناك طريقة لتجفيفها؟
أقترح أنه بالنسبة للوحدات الأكبر حجماً والأكثر استخدامًا ، يجب اختيار "مجموعات الأمثلة المشتركة" كما هو مقترح بواسطةAndrius here . بالنسبة للأشياء البسيطة التي لا تريد الدخول في مشكلة وجود ملفات متعددة إلخ. إليك كيفية ضمان أقصى قدر من التحكم في رؤية الأشياء الوهمية لديك (تم اختباره باستخدام rspec 2.14.6 ، فقط قم بنسخ الشفرة ولصقها في ملف المواصفات وتشغيله):
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
بالنسبة للوحدات التي يمكن اختبارها في العزلة أو عن طريق الاستهزاء بالطبقة ، فأنا أحب شيئا على غرار ما يلي:
وحدة:
module MyModule
def hallo
"hallo"
end
end
المواصفات:
describe MyModule do
include MyModule
it { hallo.should == "hallo" }
end
قد يبدو من الخطأ اختطاف مجموعات أمثلة متداخلة ، لكني أحب التكران. أي أفكار؟
تحتاج ببساطة إلى تضمين الوحدة النمطية الخاصة بك إلى ملف المواصفات الخاص بك mudule Test module MyModule def test 'test' end end end
في ملف المواصفات RSpec.describe Test::MyModule do include Test::MyModule #you can call directly the method *test* it 'returns test' do expect(test).to eql('test') end 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}
عملت ، لكنها لا (كما في Rubi 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>
من الواضح أن array_class غير مرئي في هذا النطاق.
لقد وجدت حلا أفضل في الصفحة الرئيسية rspec. على ما يبدو أنه يدعم مجموعات سبيل المثال المشتركة. من https://www.relishapp.com/rspec/rspec-core/v/2-13/docs/example-groups/shared-examples !
مجموعات الأمثلة المشتركة
يمكنك إنشاء مجموعات أمثلة مشتركة وتضمين تلك المجموعات في مجموعات أخرى.
افترض أن لديك بعض السلوكيات التي تنطبق على جميع إصدارات منتجك ، كبيرها وصغيرها.
أولاً ، تناول السلوك "المشترك":
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
ماذا عن:
describe MyModule do
subject { Object.new.extend(MyModule) }
it "does stuff" do
expect(subject.does_stuff?).to be_true
end
end
من أعلى رأسي ، هل يمكنك إنشاء فئة وهمية في البرنامج النصي للاختبار وتضمين الوحدة النمطية في ذلك؟ ثم اختبر أن الطبقة الوهمية لديها السلوك بالطريقة التي تتوقعها.
تحرير: إذا ، كما أشير في التعليقات ، تتوقع الوحدة النمطية أن تكون بعض السلوكيات موجودة في الفصل الذي تختلط فيه ، ثم أحاول تنفيذ دمى من تلك السلوكيات. فقط ما يكفي لجعل الوحدة سعيدة لأداء واجباتها.
ومع ذلك ، سأكون متوترة قليلاً بشأن تصميمي عندما تتوقع وحدة نمطية مجموعة كبيرة من مضيفها (هل نقول فئة "المضيف"؟) - إذا لم أرث بالفعل من فئة أساسية أو لا أحقن وظيفة جديدة في شجرة الميراث ثم أعتقد أنني سأحاول التقليل من أي توقعات من هذا القبيل أن وحدة قد يكون. قلقي هو أن تصميمي سيبدأ في تطوير بعض المناطق غير المرنة.