আরএসপিপিতে মডিউল পরীক্ষা করা


175

আরএসপিপিতে মডিউল পরীক্ষা করার সর্বোত্তম অনুশীলনগুলি কী কী? আমার কয়েকটি মডিউল রয়েছে যা কয়েকটি মডেলের সাথে অন্তর্ভুক্ত হয় এবং বর্তমানে আমি প্রতিটি মডেলের জন্য কেবল অনুলিপি পরীক্ষা করে থাকি (কয়েকটি পার্থক্য সহ)। এটি শুকানোর কোনও উপায় আছে?

উত্তর:


219

রাড উপায় =>

let(:dummy_class) { Class.new { include ModuleToBeTested } }

বিকল্পভাবে আপনি আপনার মডিউল দিয়ে পরীক্ষা ক্লাসটি প্রসারিত করতে পারেন:

let(:dummy_class) { Class.new { extend ModuleToBeTested } }

'লেট' ব্যবহারের আগে ডামি ক্লাসটি সংজ্ঞায়িত করার জন্য উদাহরণ পরিবর্তনশীল ব্যবহারের চেয়ে ভাল (: প্রতিটি)

আরএসপেক কখন ব্যবহার করবেন?


1
খুশী হলাম। এটি আমাকে ক্লাস আইভারস স্প্যানিং টেস্টগুলির সাথে সমস্ত ধরণের সমস্যা এড়াতে সহায়তা করেছিল। ধ্রুবককে বরাদ্দ করে ক্লাসের নাম দিয়েছেন।
অধিনায়ক

3
@ লুলালালা না, এটি একটি দুর্দান্ত শ্রেণি: রুবি- ডক.আর.অর্গ.ওকোর ২.০.০ / ক্লাস এইচটিএমএল# মেমোডি- সি- নতুন পরীক্ষা করার জন্য মডিউলগুলি এরকম কিছু করুন:let(:dummy_class) { Class.new { include ModuleToBeTested } }
টিমো

26
ওয়ে রেড। আমি সাধারণত:: let(:class_instance) { (Class.new { include Super::Duper::Module }).new }এইভাবে আমি উদাহরণটি পরিবর্তনশীল পাই যা বেশিরভাগ ক্ষেত্রেই কোনওভাবে পরীক্ষার জন্য ব্যবহৃত হয়।
অটোম্যাটিকো

3
ব্যবহার করা includeআমার পক্ষে কাজ করে না তবে extendকরেlet(:dummy_class) { Class.new { extend ModuleToBeTested } }
মাইক ডব্লু

8
এমনকি র‌্যাডার:subject(:instance) { Class.new.include(described_class).new }
রিচার্ড-দেগেন

108

মাইক কি বলেছে। এখানে একটি তুচ্ছ উদাহরণ:

মডিউল কোড ...

module Say
  def hello
    "hello"
  end
end

বিশেষ খণ্ড ...

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

3
include Sayডামি ক্লাস ঘোষণার ভিতরে কল করার পরিবর্তে কোনও কারণ আপনি না পেয়েছেন extend?
গ্রান্ট বার্চমিয়ার

2
গ্রান্ট-বার্চমিয়ার, সে extendক্লাসের উদাহরণে ডাকা হচ্ছে, তার পরে newডাকা হবে। যদি আপনি এটি করার আগে newবলা হয়ে থাকেন তবে আপনি ঠিক বলেছেন আপনি ব্যবহার করবেনinclude
হেজেহগ

8
আমি কোডটি আরও সংক্ষিপ্ত হতে সম্পাদিত করেছি। @ ডামি_ক্লাস = Class. নতুন {প্রসারিত করুন Say আপনাকে একটি মডিউল পরীক্ষা করার জন্য প্রয়োজন। আমি সন্দেহ করি যে লোকেরা এটিকে পছন্দ করবে যেহেতু আমরা বিকাশকারীরা প্রায়শই প্রয়োজনের চেয়ে বেশি টাইপ করতে পছন্দ করেন না।
টিম হার্পার

@ টিমহার্পার চেষ্টা করেছেন তবে উদাহরণ পদ্ধতি শ্রেণিবদ্ধ হয়ে উঠেছে। থটস?
লুলালালা

6
কেন আপনি DummyClassধ্রুবক সংজ্ঞায়িত করবেন ? শুধু কেন নয় @dummy_class = Class.new? এখন আপনার পরীক্ষার পরিবেশকে অপ্রয়োজনীয় শ্রেণীর সংজ্ঞা দিয়ে দূষিত করছে। এই ডামি ক্লাসটি আপনার প্রতিটি স্পেকের জন্য সংজ্ঞায়িত করা হয়েছে এবং পরবর্তী স্পেসে যেখানে আপনি একই পদ্ধতির ব্যবহার এবং ডামি ক্লাস সংজ্ঞাটি আবার খুলতে চান সেখানে এটি ইতিমধ্যে কিছু থাকতে পারে (যদিও এই তুচ্ছ উদাহরণে সংজ্ঞাটি কঠোরভাবে খালি, বাস্তব জীবনে কেসগুলি ব্যবহার করুন সম্ভবত সম্ভবত কিছু সময়ে কিছু যুক্ত হয়ে যায় এবং তারপরে এই পদ্ধতিটি বিপজ্জনক হয়ে ওঠে))
টিমো

29

মডিউলগুলির জন্য যা পৃথকীকরণে বা ক্লাসকে উপহাস করে পরীক্ষা করা যেতে পারে, আমি এর লাইন ধরে কিছু পছন্দ করি:

মডিউল:

module MyModule
  def hallo
    "hallo"
  end
end

বৈশিষ্ট:

describe MyModule do
  include MyModule

  it { hallo.should == "hallo" }
end

নেস্টেড উদাহরণ গোষ্ঠীগুলি হাইজ্যাক করা ভুল মনে হতে পারে তবে আমি দৃers়তা পছন্দ করি। কোন চিন্তা?


1
আমি এটি পছন্দ করি, এটি এত সোজা।
আইয়েন

2
আরএসপিকে গোলমাল করতে পারে। আমি মনে করি let@metakungfu দ্বারা বর্ণিত পদ্ধতিটি ব্যবহার করা আরও ভাল।
অটোমেটিকো

@ কর্ট3z আপনার অবশ্যই নিশ্চিত হওয়া দরকার যে পদ্ধতির নামগুলি সংঘর্ষে না চলে। আমি যখন এই বিষয়গুলি সত্যই সহজ হয় তখনই আমি এই পদ্ধতির ব্যবহার করছি।
ফ্র্যাঙ্ক সি শোয়েটস

নাম সংঘর্ষের কারণে এটি আমার পরীক্ষার স্যুটটি গোলমাল করেছিল।
roxxypoxxy

24

আমি আরএসপেক হোমপেজে আরও ভাল সমাধান পেয়েছি। স্পষ্টতই এটি ভাগ করা উদাহরণ গ্রুপগুলিকে সমর্থন করে। Https://www.relishapp.com/rspec/rspec-core/v/2-13/docs/example-groups/shared- উদাহরণ থেকে !

ভাগ করা উদাহরণ গোষ্ঠী

আপনি ভাগ করা উদাহরণ গোষ্ঠী তৈরি করতে পারেন এবং এই গোষ্ঠীগুলিকে অন্য গ্রুপগুলিতে অন্তর্ভুক্ত করতে পারেন।

ধরুন আপনার এমন কিছু আচরণ রয়েছে যা আপনার পণ্যের বড় এবং ছোট উভয় সংস্করণের ক্ষেত্রে প্রযোজ্য।

প্রথমে "ভাগ করা" আচরণটি নির্ধারণ করুন:

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


21

আমার মাথার শীর্ষে, আপনি কি আপনার পরীক্ষার স্ক্রিপ্টে একটি ডামি ক্লাস তৈরি করতে এবং মডিউলটিকে এতে অন্তর্ভুক্ত করতে পারেন? তারপরে পরীক্ষা করুন যে ডামি ক্লাসটি আপনার প্রত্যাশা মতো আচরণ করে।

সম্পাদনা: মন্তব্যগুলিতে যেমন উল্লেখ করা হয়েছে, মডিউলটি আশা করছে যে এটির মিশ্রিত শ্রেণিতে কিছু আচরণ উপস্থিত থাকবে তবে আমি সেই আচরণগুলির ডামি বাস্তবায়নের চেষ্টা করব। মডিউলটিকে তার দায়িত্ব পালনে খুশি করার জন্য যথেষ্ট।

এটি বলেছিল, যখন কোনও মডিউল তার হোস্ট (আমরা কী "হোস্ট" বলি?) শ্রেণীর কাছ থেকে পুরোটা প্রত্যাশা করি তখন আমি আমার নকশাটি সম্পর্কে কিছুটা ঘাবড়ে যাই - যদি আমি ইতিমধ্যে বেস ক্লাসের উত্তরাধিকারী না হই বা ইনজেকশন নাও দিতে পারি উত্তরাধিকার গাছের মধ্যে নতুন কার্যকারিতা তখন আমি মনে করি যে কোনও মডিউলটি পেতে পারে এমন কোনও প্রত্যাশা হ্রাস করার চেষ্টা করব। আমার উদ্বেগ হচ্ছে যে আমার নকশাটি অপ্রীতিকর জটিলতার কিছু ক্ষেত্র বিকাশ করা শুরু করবে।


আমার মডিউলটি যদি শ্রেণীর উপর নির্ভর করে নির্দিষ্ট বৈশিষ্ট্য এবং আচরণ রাখে?
Andrius

10

গৃহীত উত্তর হ'ল সঠিক উত্তর আমার মনে হয়, তবে আমি কীভাবে আরপিএস shared_examples_forএবং it_behaves_likeপদ্ধতি ব্যবহার করতে পারি তার একটি উদাহরণ যুক্ত করতে চেয়েছিলাম । আমি কোড স্নিপেটে কয়েকটি কৌশল উল্লেখ করেছি তবে আরও তথ্যের জন্য এই রিলাশাপ-আরএসপেক-গাইডটি দেখুন

এটির সাহায্যে আপনি যে কোনও ক্লাসে এটি অন্তর্ভুক্ত করে আপনার মডিউলটি পরীক্ষা করতে পারেন। সুতরাং আপনি আপনার অ্যাপ্লিকেশনটিতে যা ব্যবহার করেন তা সত্যই পরীক্ষা করছেন।

আসুন একটি উদাহরণ দেখুন:

# 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

এখন আমাদের মডিউলটির জন্য বিশেষ তৈরি করতে দিন: 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


6

আমি পরামর্শ দেব যে বৃহত্তর এবং অনেক বেশি ব্যবহৃত মডিউলগুলির জন্য এখানে @ এন্ড্রিয়াসের পরামর্শ অনুযায়ী "ভাগ করা উদাহরণ গোষ্ঠী" বেছে নেওয়া উচিত । আপনি যে সাধারণ জিনিসগুলির জন্য একাধিক ফাইল ইত্যাদির সমস্যায় পড়তে চান না তার জন্য এখানে কীভাবে আপনার ডামি স্টাফের দৃশ্যমানতার উপর সর্বাধিক নিয়ন্ত্রণ নিশ্চিত করা যায় (আরএসপেক ২.১.6..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

কিছু কারণে শুধুমাত্র subject { dummy_class.new }কাজ করা হয়। মামলাটি subject { dummy_class }আমার পক্ষে কাজ করছে না।
ভাল

6

আমার সাম্প্রতিক কাজ, যতটা সম্ভব হার্ড ওয়্যারিং ব্যবহার করে

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}

কাজ করেছে, তবে তা হয়নি (যেমন রুবি এমআরআই ২.২.৩ এবং আরএসপেক :: কোর ৩.৩.০)

Failure/Error: subject {Class.new{include described_class}.new}
  NameError:
    undefined local variable or method `described_class' for #<Class:0x000000063a6708>

স্পষ্টত বর্ণিত_ক্লাস সেই সুযোগে দৃশ্যমান নয়।


6

আপনার মডিউলটি পরীক্ষা করতে, ব্যবহার করুন:

describe MyCoolModule do
  subject(:my_instance) { Class.new.extend(described_class) }

  # examples
end

আপনি একাধিক চশমা জুড়ে ব্যবহার করেন এমন কিছু জিনিস শুকানোর জন্য, আপনি একটি ভাগ করা প্রসঙ্গ ব্যবহার করতে পারেন:

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

সম্পদ:


4

আপনি সাহায্যকারী প্রকারটিও ব্যবহার করতে পারেন

# 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

এখানে ডকুমেন্টেশন রয়েছে: https://www.relishapp.com/rspec/rspec-rails/v/3-3/docs/helper-specs/helper-spec


0

আপনি কেবল আপনার বৈশিষ্ট ফাইলে আপনার মডিউল অন্তর্ভুক্ত করতে হবে 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


-1

মডিউল পদ্ধতি যা ক্লাসে স্বতন্ত্র রয়েছে সেগুলি পরীক্ষার জন্য একটি সম্ভাব্য সমাধান যা তাদের অন্তর্ভুক্ত করবে

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

এবং যদি আপনি সেগুলি পরীক্ষা করে দেখতে চান, তবে ভাগ করে নেওয়া উদাহরণগুলি হল ভাল পদ্ধতির


যিনি আপনাকে হ্রাস করেছিলেন আমি নই, তবে আমি আপনার দুটি এলইটি প্রতিস্থাপনের পরামর্শ দিই subject(:module_to_test_instance) { Class.new.include(described_class) }। অন্যথায় আমি আপনার উত্তরের সাথে সত্যিই কোনও ভুল দেখতে পাচ্ছি না।
অ্যালিসন

-1

এটি একটি পুনরাবৃত্ত প্যাটার্ন যেহেতু আপনাকে একাধিক মডিউল পরীক্ষা করতে হবে। যে কারণে, এটির জন্য কোনও সহায়ক তৈরি করা বাঞ্ছনীয়।

আমি এই পোস্টটি পেয়েছি যাতে এটি কীভাবে করা যায় তা ব্যাখ্যা করে তবে আমি এখানে এখানে মোকাবিলা করছি যেহেতু সাইটটি কোনও এক সময় নামানো হতে পারে।

এটি অবজেক্টের উদাহরণগুলি এড়ানোর জন্য উদাহরণ উদাহরণটি প্রয়োগ করে না::allow পদ্ধতিগুলি চেষ্টা করার সময় আপনি যে কোনও ত্রুটি পাবেনdummy ক্লাসে ।

কোড:

ভিতরে 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

ভিতরে 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

আপনার চশমা:

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
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.