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

من أعلى رأسي ، هل يمكنك إنشاء فئة وهمية في البرنامج النصي للاختبار وتضمين الوحدة النمطية في ذلك؟ ثم اختبر أن الطبقة الوهمية لديها السلوك بالطريقة التي تتوقعها.

تحرير: إذا ، كما أشير في التعليقات ، تتوقع الوحدة النمطية أن تكون بعض السلوكيات موجودة في الفصل الذي تختلط فيه ، ثم أحاول تنفيذ دمى من تلك السلوكيات. فقط ما يكفي لجعل الوحدة سعيدة لأداء واجباتها.

ومع ذلك ، سأكون متوترة قليلاً بشأن تصميمي عندما تتوقع وحدة نمطية مجموعة كبيرة من مضيفها (هل نقول فئة "المضيف"؟) - إذا لم أرث بالفعل من فئة أساسية أو لا أحقن وظيفة جديدة في شجرة الميراث ثم أعتقد أنني سأحاول التقليل من أي توقعات من هذا القبيل أن وحدة قد يكون. قلقي هو أن تصميمي سيبدأ في تطوير بعض المناطق غير المرنة.





rspec