রুবিতে ইউনিট পরীক্ষা সুরক্ষিত এবং ব্যক্তিগত পদ্ধতিগুলির সর্বোত্তম উপায় কী?


136

স্ট্যান্ডার্ড রুবি Test::Unitফ্রেমওয়ার্কটি ব্যবহার করে রুবিতে ইউনিট পরীক্ষা সুরক্ষিত এবং ব্যক্তিগত পদ্ধতিগুলির সর্বোত্তম উপায় কী ?

আমি নিশ্চিত যে কেউ পাইপ আপ করবেন এবং গোপনে দৃ as়ভাবে দৃsert়ভাবে বলেছেন যে "আপনার কেবলমাত্র পাবলিক পদ্ধতিতে ইউনিট পরীক্ষা করা উচিত; যদি এটির ইউনিট পরীক্ষার প্রয়োজন হয় তবে এটি কোনও সুরক্ষিত বা ব্যক্তিগত পদ্ধতি হওয়া উচিত নয়", তবে আমি এটি নিয়ে বিতর্ক করতে আগ্রহী নই। আমি বিভিন্ন পদ্ধতি যে পেয়েছেন করছে সংরক্ষিত বা ভাল এবং বৈধ কারণে ব্যক্তিগত, এই ব্যক্তিগত / সংরক্ষিত পদ্ধতি পরিমিতরূপে খুবই জটিল, এবং ক্লাসে প্রকাশ্য পদ্ধতি এই সংরক্ষিত / ব্যক্তিগত পদ্ধতি সঠিকভাবে কাজ উপর নির্ভর, তাই আমি পরীক্ষা করার জন্য একটি উপায় প্রয়োজন সুরক্ষিত / ব্যক্তিগত পদ্ধতি।

আরও একটি জিনিস ... আমি সাধারণত একটি ফাইলে একটি নির্দিষ্ট শ্রেণীর জন্য সমস্ত পদ্ধতি রাখি এবং অন্য শ্রেণিতে সেই শ্রেণীর জন্য ইউনিট পরীক্ষা করি। মূলত, আমি মূল উত্স ফাইলটি যতটা সম্ভব সহজ এবং সহজবোধ্য রাখতে, "উত্সযুক্ত এবং ব্যক্তিগত পদ্ধতিগুলির ইউনিট টেস্ট" কার্যকারিতাটি ইউনিট পরীক্ষার ফাইলে নয়, মূল উত্স ফাইল হিসাবে প্রয়োগ করতে চাই all


উত্তর:


135

আপনি প্রেরণ পদ্ধতির সাথে এনক্যাপসুলেশনকে বাইপাস করতে পারেন:

myobject.send(:method_name, args)

এটি রুবির একটি 'বৈশিষ্ট্য'। :)

রুবি ১.৯ এর বিকাশের সময় অভ্যন্তরীণ বিতর্ক হয়েছিল যা sendগোপনীয়তার সম্মান রাখাকে বিবেচনা করে এবং send!এটিকে উপেক্ষা করে, তবে শেষ পর্যন্ত রুবি ১.৯-তে কিছুই পরিবর্তন হয়নি। send!জিনিসগুলি নিয়ে আলোচনা এবং বিরতিতে নীচের মন্তব্যগুলিকে উপেক্ষা করুন ।


আমি মনে করি এই ব্যবহারটি 1.9
জিন টি

6
আমি সন্দেহ করি তারা এটিকে প্রত্যাহার করে নেবে, কারণ তারা তত্ক্ষণাত বিপুল সংখ্যক রুবি প্রকল্প ভেঙে ফেলবে
অরিয়ন এডওয়ার্ডস

1
রুবি 1.9 প্রায় সবকিছু ভেঙে দেয়
jes5199

1
কেবল খেয়াল করুন: send!বিষয়টিকে মনে রাখবেন না , এটি অনেক আগেই প্রত্যাহার করা হয়েছিল send/__send__, সমস্ত দৃশ্যমানতার পদ্ধতিগুলি কল করতে পারে - redmine.ruby-lang.org/repositories/revision/1?rev=13824
dolzenko

2
আছে public_send(ডকুমেন্টেশন এখানে ) আপনি গোপনীয়তাকে সম্মান করতে চাই। আমি মনে করি এটি রুবি ১.৯-এ নতুন।
অ্যান্ড্রু গ্রিম

71

আপনি আরএসপেক ব্যবহার করেন তবে একটি সহজ উপায়:

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

9
হ্যাঁ, দুর্দান্ত। ব্যক্তিগত পদ্ধতিগুলির জন্য, সুরক্ষিত_ইনস্ট্যান্স_মোথডগুলি না করে ব্যক্তিগত_আইনস্ট্যান্স_সামগ্রীগুলি ব্যবহার করুন
মাইক ব্লাইথ

12
গুরুত্বপূর্ণ সতর্কতা: এটি আপনার পরীক্ষার স্যুট এক্সিকিউশনের অবশিষ্টাংশের জন্য এই শ্রেণীর উপর পদ্ধতিগুলি জনসাধারণকে তোলে, যার অপ্রত্যাশিত পার্শ্ব প্রতিক্রিয়া হতে পারে! আপনি পদ্ধতিগুলি পুনরায় সংজ্ঞায়িত করে আবার (: প্রতিটি) ব্লকের পরে বা ভবিষ্যতে ভুগল পরীক্ষার ব্যর্থতায় ভুগতে পারেন।
প্যাথোজেন

এটি একই সময়ে ভয়ঙ্কর এবং উজ্জ্বল
রবার্ট

আমি এর আগে কখনও দেখিনি এবং আমি প্রমাণ করতে পারি যে এটি দুর্দান্তভাবে কাজ করে। হ্যাঁ এটি ভয়াবহ এবং উজ্জ্বল উভয়ই তবে আপনি যতক্ষণ পরীক্ষা করছেন তার স্তরে যতক্ষণ আপনি এটি স্কোপ করেন ততক্ষণ আমি যুক্তি দেব যে প্যাথোজেন আপনাকে যে অপ্রত্যাশিত পার্শ্ব প্রতিক্রিয়া বলেছে তার কোনও প্রভাব পড়বে না।
fuzzygroup

32

কেবলমাত্র আপনার পরীক্ষার ফাইলে ক্লাসটি আবার খুলুন, এবং পদ্ধতি বা পদ্ধতিগুলি সর্বজনীন হিসাবে পুনরায় সংজ্ঞায়িত করুন। আপনাকে নিজেই পদ্ধতির সাহস পুনরায় সংজ্ঞায়িত করতে হবে না, কেবলমাত্র চিহ্নটিতে পাস করুনpublic কলটিতে ।

যদি আপনি মূল শ্রেণিটি এভাবে সংজ্ঞায়িত হন:

class MyClass

  private

  def foo
    true
  end
end

আপনার পরীক্ষার ফাইলটিতে, এই জাতীয় কিছু করুন:

class MyClass
  public :foo

end

আপনি publicআরও ব্যক্তিগত পদ্ধতি প্রকাশ করতে চাইলে আপনি একাধিক প্রতীক পাস করতে পারেন।

public :foo, :bar

2
এটি আপনার পছন্দের পদ্ধতির কারণ এটি আপনার কোডটি অচ্ছুত করে এবং কেবল নির্দিষ্ট পরীক্ষার জন্য গোপনীয়তার সাথে সামঞ্জস্য করে। আপনার পরীক্ষাগুলি চলার পরে জিনিস যেমন ছিল তেমন পেছনে ফেলে রাখা ভুলবেন না বা আপনি পরে পরীক্ষাগুলি দূষিত করতে পারেন।
ktec

10

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() , যা আপনাকে ব্যক্তিগত এবং সুরক্ষিত পদ্ধতিতে অ্যাক্সেসও দেবে (যেমন জেমস বেকার প্রস্তাবিত)

বিকল্পভাবে, কেবলমাত্র সেই অবজেক্টের জন্য ব্যক্তিগত / সুরক্ষিত পদ্ধতিগুলি সর্বজনীন করতে আপনি আপনার পরীক্ষার অবজেক্টের মেটাক্লাসটি পরিবর্তন করতে পারেন।

    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

এই শ্রেণীর অন্যান্য অবজেক্টগুলিকে প্রভাবিত না করে আপনাকে এই পদ্ধতিগুলি কল করতে দেবে। আপনি আপনার পরীক্ষার ডিরেক্টরিতে ক্লাসটি আবার খুলতে পারেন এবং আপনার পরীক্ষার কোডের মধ্যে সমস্ত দৃষ্টান্তের জন্য তাদের সর্বজনীন করে তুলতে পারেন, তবে এটি আপনার পাবলিক ইন্টারফেসের পরীক্ষাকে প্রভাবিত করতে পারে।


9

অতীতে এটি করার একটি উপায়:

class foo
  def public_method
    private_method
  end

private unless 'test' == Rails.env

  def private_method
    'private'
  end
end

8

আমি নিশ্চিত যে কেউ পাইপ আপ করবেন এবং গোপনে দৃ as়ভাবে দৃsert়ভাবে বলেছেন যে "আপনার কেবলমাত্র পাবলিক পদ্ধতিতে ইউনিট পরীক্ষা করা উচিত; যদি এটির ইউনিট পরীক্ষার প্রয়োজন হয় তবে এটি কোনও সুরক্ষিত বা ব্যক্তিগত পদ্ধতি হওয়া উচিত নয়", তবে আমি এটি নিয়ে বিতর্ক করতে আগ্রহী নই।

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

আমি বেশ কয়েকটি পদ্ধতি পেয়েছি যা ভাল এবং বৈধ কারণে সুরক্ষিত বা ব্যক্তিগত

এই বৈধ কারণগুলি কী কী? অন্যান্য ওওপি ভাষা বেসরকারী পদ্ধতিগুলি ছাড়াই মোটেও দূরে সরে যেতে পারে (স্মার্টটাক মনে পড়ে - যেখানে ব্যক্তিগত পদ্ধতিগুলি কেবল একটি সম্মেলন হিসাবে বিদ্যমান)।


হ্যাঁ, তবে বেশিরভাগ স্মার্টল্যাকাররা ভাবেননি যে এটি ভাষার একটি ভাল বৈশিষ্ট্য।
aenw

6

@ উইলসার্জেন্টের প্রতিক্রিয়াটির অনুরূপ, 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

5

বর্ণিত শ্রেণীর জন্য সমস্ত সুরক্ষিত এবং ব্যক্তিগত পদ্ধতিতে সর্বজনীন করার জন্য, আপনি নিম্নলিখিতটি আপনার স্পেশাল_হেল্পার.আরবিতে যুক্ত করতে পারেন এবং আপনার কোনও স্পেক ফাইলের স্পর্শ না করে।

RSpec.configure do |config|
  config.before(:each) do
    described_class.send(:public, *described_class.protected_instance_methods)
    described_class.send(:public, *described_class.private_instance_methods)
  end
end

3

আপনি ক্লাসটি "পুনরায় খুলুন" এবং একটি নতুন পদ্ধতি সরবরাহ করতে পারেন যা ব্যক্তিগতটিকে প্রেরণ করে:

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

2

আমি সম্ভবত ইনস্ট্যান্স_এভাল () ব্যবহারের দিকে ঝুঁকতে চাই। উদাহরণস্বরূপ () এর আগে আমি জানার আগে, আমি আমার ইউনিট পরীক্ষার ফাইলে একটি উত্পন্ন ক্লাস তৈরি করব। আমি তখন সর্বজনীন হওয়ার জন্য ব্যক্তিগত পদ্ধতি (গুলি) সেট করে দেব।

নীচের উদাহরণে, build_year_range পদ্ধতিটি প্রকাশনা অনুসন্ধান :: ISIQuery শ্রেণিতে ব্যক্তিগত Qu কেবল পরীক্ষার উদ্দেশ্যে নতুন শ্রেণীর উত্সাহ দেওয়া আমাকে জনসাধারণের জন্য একটি পদ্ধতি (গুলি) সেট করার অনুমতি দেয় এবং তাই সরাসরি পরীক্ষার জন্য। তেমনি, উত্পন্ন শ্রেণি 'রেজাল্ট' নামক একটি উদাহরণ ভেরিয়েবল প্রকাশ করে যা আগে প্রকাশ করা হয়নি।

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

আমার ইউনিট পরীক্ষায় আমার একটি পরীক্ষার কেস রয়েছে যা মকআইএসআইকিউরি ক্লাসটি ইনস্ট্যান্ট করে এবং সরাসরি বিল্ড_ইয়ার_আরঞ্জ () পদ্ধতি পরীক্ষা করে।


2

পরীক্ষায় :: ইউনিট কাঠামো লিখতে পারে,

MyClass.send(:public, :method_name)

এখানে "পদ্ধতি_নাম" ব্যক্তিগত পদ্ধতি।

& কল করার সময় এই পদ্ধতিটি লিখতে পারেন,

assert_equal expected, MyClass.instance.method_name(params)

1

আমি যে ক্লাসটি ব্যবহার করি তা এখানে একটি সাধারণ সংযোজন। আপনি যে পদ্ধতিটি পরীক্ষা করছেন সেটি কেবল জনসাধারণের তুলনায় এটি কিছুটা বেশি শটগান, তবে বেশিরভাগ ক্ষেত্রে এটি কোনও ব্যাপার নয় এবং এটি আরও বেশি পাঠযোগ্য।

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 ভাঙ্গা, তাই একটি প্রস্তাবিত সমাধান নয়।


1

উপরের শীর্ষস্থানীয় উত্তরটি সংশোধন করতে: রুবি ১.৯.১-তে, এটি অবজেক্ট # প্রেরণ যা সমস্ত বার্তা প্রেরণ করে, এবং অবজেক্ট # সার্বজনীন_সেন্ড যা গোপনীয়তার প্রতি শ্রদ্ধা রাখে।


1
আপনার সেই উত্তরটিতে মন্তব্য যুক্ত করা উচিত, অন্য কোনও সংশোধন করার জন্য নতুন উত্তর লিখছেন না।
zishe

1

আপত্তি.এর পরিবর্তে আপনি একটি সিঙ্গলটন পদ্ধতি ব্যবহার করতে পারেন। এটি আপনার পরীক্ষার শ্রেণিতে কোডের আরও 3 টি লাইন এবং পরীক্ষার জন্য আসল কোডে কোনও পরিবর্তন প্রয়োজন।

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

পরীক্ষার ক্ষেত্রে আপনি my_private_method_publiclyযখনই পরীক্ষা করতে চান তখন ব্যবহার করুন my_private_method

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

obj.sendব্যক্তিগত পদ্ধতির জন্য send!১.৯ সালে প্রতিস্থাপন করা হয়েছিল, তবে পরে send!আবার সরানো হয়েছে। সুতরাং obj.sendপুরোপুরি ভাল কাজ করে।


1

আমি জানি আমি পার্টিতে দেরি করেছি, তবে ব্যক্তিগত পদ্ধতিগুলি পরীক্ষা করব না .... আমি এটি করার কারণটি ভাবতে পারি না। সর্বজনীনভাবে অ্যাক্সেসযোগ্য পদ্ধতিটি সেই ব্যক্তিগত পদ্ধতিটি কোথাও ব্যবহার করা, সর্বজনীন পদ্ধতি এবং বিভিন্ন পরিস্থিতিতে কীভাবে সেই ব্যক্তিগত পদ্ধতিটি ব্যবহারের কারণ হতে পারে তা পরীক্ষা করে। কিছু ভিতরে যায়, কিছু আসে। ব্যক্তিগত পদ্ধতি পরীক্ষা করা একটি বড় সংখ্যা-নয় এবং এটি পরে আপনার কোডটি রিফেক্টর করা আরও শক্ত করে তোলে। তারা একটি কারণে ব্যক্তিগত।


14
তবুও এই অবস্থানটি বুঝতে পারি না: হ্যাঁ, ব্যক্তিগত পদ্ধতিগুলি কোনও কারণে ব্যক্তিগত, কিন্তু না, পরীক্ষার সাথে এই কারণটির কোনও যোগসূত্র নেই।
সেবাস্তিয়ান ভম মিয়ার

আমি আশা করি আমি এই আরও upvote করতে পারে। এই থ্রেডের একমাত্র সঠিক উত্তর।
সিসিনিক্স

আপনার যদি সেই দৃষ্টিভঙ্গি থাকে তবে কেন ইউনিট টেস্ট নিয়ে বিরক্ত করবেন? কেবল বৈশিষ্ট্যগুলির চশমাগুলি লিখুন: ইনপুটটি যায়, পৃষ্ঠাটি বেরিয়ে আসে, এর মধ্যে সমস্ত কিছু ঠিকঠাক করা উচিত?
ওহহ

1

এই কাজ করার জন্য:

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

আপনি এটি আপনার টেস্ট_হেল্পার ফাইলটিতে প্রয়োগ করতে পারেন:

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

হেই আমি এটির সাথে কিছু মজা করেছি: gist.github.com/amomchilov/ef1c84325fe6bb4ce01e0f0780837a82 (: পি) নামকরণ Disrespectকরে PrivacyViolatorএবং disrespect_privacyপদ্ধতিটি অস্থায়ীভাবে ব্লকের বাঁধাই সম্পাদনা করে, যাতে মোড়কের বস্তুতে লক্ষ্য বস্তুকে স্মরণ করিয়ে দেওয়া যায় তবে কেবল সময়ের জন্য ব্লকের এইভাবে আপনাকে কোনও ব্লক প্যারাম ব্যবহার করার দরকার নেই, আপনি কেবল একই নামের সাথে অবজেক্টটি উল্লেখ করতে পারেন।
আলেকজান্ডার -
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.