ruby रुबी में इकाई परीक्षण संरक्षित और निजी तरीकों का सबसे अच्छा तरीका क्या है?




unit-testing private (13)

रूबी में मानक रूबी Test::Unit फ्रेमवर्क का उपयोग करके इकाई परीक्षण संरक्षित और निजी तरीकों के लिए सबसे अच्छा तरीका क्या है?

मुझे यकीन है कि कोई पाइप करेगा और dogmatically जोर देकर कहते हैं कि "आपको केवल यूनिट परीक्षण सार्वजनिक तरीकों का परीक्षण करना चाहिए; अगर इसे इकाई परीक्षण की आवश्यकता है, तो यह एक संरक्षित या निजी विधि नहीं होनी चाहिए", लेकिन मुझे वास्तव में उस पर बहस करने में दिलचस्पी नहीं है। मेरे पास कई विधियां हैं जो अच्छे और वैध कारणों से संरक्षित या निजी हैं, ये निजी / संरक्षित विधियां मामूली जटिल हैं, और कक्षा में सार्वजनिक विधियां इन संरक्षित / निजी विधियों पर सही ढंग से कार्य करने पर निर्भर करती हैं, इसलिए मुझे परीक्षण करने का एक तरीका चाहिए संरक्षित / निजी तरीकों।

एक और बात ... मैं आम तौर पर एक फ़ाइल में किसी दिए गए वर्ग के लिए सभी विधियों को डालता हूं, और उस वर्ग के लिए इकाई किसी अन्य फ़ाइल में परीक्षण करता है। आदर्श रूप में, मैं मुख्य स्रोत फ़ाइल को यथासंभव सरल और सीधा रखने के लिए, मुख्य स्रोत फ़ाइल नहीं, इकाई परीक्षण फ़ाइल में, "संरक्षित और निजी विधियों की इकाई परीक्षण" कार्यक्षमता को कार्यान्वित करने के लिए सभी जादू चाहता हूं।


मुझे पता है कि मैं पार्टी के लिए देर हो चुकी हूं, लेकिन निजी तरीकों का परीक्षण न करें .... मैं ऐसा करने का कोई कारण नहीं सोच सकता। सार्वजनिक रूप से सुलभ विधि कहीं भी उस निजी विधि का उपयोग कर रही है, सार्वजनिक विधि और परिदृश्यों की विविधता का परीक्षण करें जो उस निजी विधि का उपयोग करने का कारण बनेंगी। कुछ अंदर चला जाता है, कुछ आता है। निजी तरीकों का परीक्षण करना एक बड़ा नो-नो है, और यह बाद में आपके कोड को दोबारा प्रतिक्रिया देना कठिन बनाता है। वे एक कारण के लिए निजी हैं।


अतीत में मैंने इसे एक तरह से किया है:

class foo
  def public_method
    private_method
  end

private unless 'test' == Rails.env

  def private_method
    'private'
  end
end

मैं शायद example_eval () का उपयोग करने की ओर झुकना होगा। Example_eval () के बारे में मुझे पता था, हालांकि, मैं अपनी यूनिट परीक्षण फ़ाइल में एक व्युत्पन्न कक्षा बनाउंगा। फिर मैं निजी विधि को सार्वजनिक होने के लिए सेट करूंगा।

नीचे दिए गए उदाहरण में, build_year_range विधि प्रकाशन खोज :: ISIQuery क्लास में निजी है। केवल परीक्षण प्रयोजनों के लिए एक नई कक्षा को प्राप्त करने से मुझे सार्वजनिक होने के लिए एक विधि (ओं) सेट करने की अनुमति मिलती है, और इसलिए, सीधे परीक्षण योग्य। इसी तरह, व्युत्पन्न वर्ग 'परिणाम' नामक एक आवृत्ति चर का खुलासा करता है जिसे पहले प्रकट नहीं किया गया था।

# A derived class useful for testing.
class MockISIQuery < PublicationSearch::ISIQuery
    attr_accessor :result
    public :build_year_range
end

मेरे यूनिट टेस्ट में मेरे पास एक टेस्ट केस है जो MockISIQuery क्लास को तुरंत चालू करता है और सीधे build_year_range () विधि का परीक्षण करता है।


मुझे यकीन है कि कोई पाइप करेगा और dogmatically जोर देकर कहते हैं कि "आपको केवल यूनिट परीक्षण सार्वजनिक तरीकों का परीक्षण करना चाहिए; अगर इसे इकाई परीक्षण की आवश्यकता है, तो यह एक संरक्षित या निजी विधि नहीं होनी चाहिए", लेकिन मुझे वास्तव में उस पर बहस करने में दिलचस्पी नहीं है।

आप उन लोगों को एक नई वस्तु में भी दोबारा कर सकते हैं जिसमें वे विधियां सार्वजनिक हैं, और मूल कक्षा में निजी तौर पर उन्हें प्रतिनिधिमंडल देते हैं। यह आपको अपने चश्मा में जादू मेटारबी के बिना विधियों का परीक्षण करने की अनुमति देगा जबकि अभी तक उन्हें निजी रखेगा।

मेरे पास कई विधियां हैं जो अच्छे और वैध कारणों से संरक्षित या निजी हैं

वे वैध कारण क्या हैं? अन्य ओओपी भाषाएं निजी तरीकों के बिना दूर हो सकती हैं (छोटे-छोटे दिमाग में आता है - जहां निजी विधियां केवल एक सम्मेलन के रूप में मौजूद होती हैं)।


instance_eval() मदद कर सकता है:

--------------------------------------------------- Object#instance_eval
     obj.instance_eval(string [, filename [, lineno]] )   => obj
     obj.instance_eval {| | block }                       => obj
------------------------------------------------------------------------
     Evaluates a string containing Ruby source code, or the given 
     block, within the context of the receiver (obj). In order to set 
     the context, the variable self is set to obj while the code is 
     executing, giving the code access to obj's instance variables. In 
     the version of instance_eval that takes a String, the optional 
     second and third parameters supply a filename and starting line 
     number that are used when reporting compilation errors.

        class Klass
          def initialize
            @secret = 99
          end
        end
        k = Klass.new
        k.instance_eval { @secret }   #=> 99

आप निजी विधियों और इंस्टेंस चरों को सीधे एक्सेस करने के लिए इसका उपयोग कर सकते हैं।

आप send() का उपयोग करने पर भी विचार कर send() , जो आपको निजी और संरक्षित तरीकों तक पहुंच प्रदान करेगा (जैसे जेम्स बेकर ने सुझाव दिया)

वैकल्पिक रूप से, आप उस ऑब्जेक्ट के लिए निजी / संरक्षित विधियों को सार्वजनिक बनाने के लिए अपने परीक्षण ऑब्जेक्ट के मेटाक्लास को संशोधित कर सकते हैं।

    test_obj.a_private_method(...) #=> raises NoMethodError
    test_obj.a_protected_method(...) #=> raises NoMethodError
    class << test_obj
        public :a_private_method, :a_protected_method
    end
    test_obj.a_private_method(...) # executes
    test_obj.a_protected_method(...) # executes

    other_test_obj = test.obj.class.new
    other_test_obj.a_private_method(...) #=> raises NoMethodError
    other_test_obj.a_protected_method(...) #=> raises NoMethodError

यह आपको उस वर्ग की अन्य वस्तुओं को प्रभावित किए बिना इन विधियों को कॉल करने देगा। आप अपनी परीक्षा निर्देशिका में कक्षा को फिर से खोल सकते हैं और उन्हें अपने परीक्षण कोड के भीतर सभी उदाहरणों के लिए सार्वजनिक कर सकते हैं, लेकिन इससे सार्वजनिक इंटरफ़ेस के आपके परीक्षण को प्रभावित हो सकता है।


ऐसा करने के लिए:

disrespect_privacy @object do |p|
  assert p.private_method
end

आप इसे अपनी test_helper फ़ाइल में कार्यान्वित कर सकते हैं:

class ActiveSupport::TestCase
  def disrespect_privacy(object_or_class, &block)   # access private methods in a block
    raise ArgumentError, 'Block must be specified' unless block_given?
    yield Disrespect.new(object_or_class)
  end

  class Disrespect
    def initialize(object_or_class)
      @object = object_or_class
    end
    def method_missing(method, *args)
      @object.send(method, *args)
    end
  end
end

यदि आप आरएसपीईसी का उपयोग करते हैं तो यहां एक आसान तरीका है:

before(:each) do
  MyClass.send(:public, *MyClass.protected_instance_methods)  
end

आप कक्षा को फिर से खोल सकते हैं और एक नई विधि प्रदान कर सकते हैं जो निजी व्यक्ति को सौंपे:

class Foo
  private
  def bar; puts "Oi! how did you reach me??"; end
end
# and then
class Foo
  def ah_hah; bar; end
end
# then
Foo.new.ah_hah

Obj.send के बजाय आप एक सिंगलटन विधि का उपयोग कर सकते हैं। यह आपके टेस्ट क्लास में कोड की 3 और लाइनें है और परीक्षण के लिए वास्तविक कोड में कोई बदलाव की आवश्यकता नहीं है।

def obj.my_private_method_publicly (*args)
  my_private_method(*args)
end

जब भी आप my_private_method का परीक्षण करना चाहते हैं तो परीक्षण मामलों में आप my_private_method_publicly उपयोग करें।

http://mathandprogramming.blogspot.com/2010/01/ruby-testing-private-methods.html

निजी तरीकों के लिए obj.send send! दिया गया था send! 1.9 में, लेकिन बाद में send! फिर से हटा दिया गया था। तो obj.send पूरी तरह से अच्छी तरह से काम करता है।


@ विलसर्जेंट की प्रतिक्रिया के समान, मैंने कुछ संरक्षित सत्यापनकर्ताओं को परीक्षण करने के विशेष मामले के लिए describe ब्लॉक में उपयोग किया है, जिसे फैक्टरीगर्ल के साथ बनाने / अपडेट करने की हेवीवेट प्रक्रिया के माध्यम से जाने की आवश्यकता के बिना (और आप इसी तरह से private_instance_methods उपयोग कर सकते हैं):

  describe "protected custom `validates` methods" do
    # Test these methods directly to avoid needing FactoryGirl.create
    # to trigger before_create, etc.
    before(:all) do
      @protected_methods = MyClass.protected_instance_methods
      MyClass.send(:public, *@protected_methods)
    end
    after(:all) do
      MyClass.send(:protected, *@protected_methods)
      @protected_methods = nil
    end

    # ...do some tests...
  end

आप प्रेषण विधि के साथ encapsulation बाईपास कर सकते हैं:

myobject.send(:method_name, args)

यह रूबी की 'फीचर' है। :)

रूबी 1.9 विकास के दौरान आंतरिक बहस हुई थी, जिसे सम्मान गोपनीयता send! और send! इसे अनदेखा करें, लेकिन अंत में रुबी 1.9 में कुछ भी नहीं बदला। send! चर्चा की गई टिप्पणियों को अनदेखा करें send! और चीजों को तोड़ना।


बस अपनी टेस्ट फ़ाइल में कक्षा को फिर से खोलें, और विधि या विधियों को सार्वजनिक रूप से दोबारा परिभाषित करें। आपको विधि के गले को फिर से परिभाषित करने की ज़रूरत नहीं है, बस प्रतीक को public कॉल में पास करें।

यदि आप मूल वर्ग को इस प्रकार परिभाषित करते हैं:

class MyClass

  private

  def foo
    true
  end
end

आप परीक्षण फ़ाइल में, बस ऐसा कुछ करें:

class MyClass
  public :foo

end

यदि आप अधिक निजी तरीकों का पर्दाफाश करना चाहते हैं तो आप कई प्रतीकों को public से पारित कर सकते हैं।

public :foo, :bar

यहां कक्षा का एक सामान्य जोड़ा है जिसका मैं उपयोग करता हूं। यह केवल आपके द्वारा परीक्षण की जा रही विधि को सार्वजनिक करने की तुलना में थोड़ा अधिक शॉटगन है, लेकिन ज्यादातर मामलों में इससे कोई फर्क नहीं पड़ता है, और यह बहुत अधिक पठनीय है।

class Class
  def publicize_methods
    saved_private_instance_methods = self.private_instance_methods
    self.class_eval { public *saved_private_instance_methods }
    begin
      yield
    ensure
      self.class_eval { private *saved_private_instance_methods }
    end
  end
end

MyClass.publicize_methods do
  assert_equal 10, MyClass.new.secret_private_method
end

सुरक्षित / निजी विधियों तक पहुंचने के लिए भेजने का उपयोग 1.9 में टूट गया है, इसलिए एक अनुशंसित समाधान नहीं है।





protected