Rspec, रेल: नियंत्रकों के निजी तरीकों का परीक्षण कैसे करें?


125

मेरे पास नियंत्रक है:

class AccountController < ApplicationController
  def index
  end

  private
  def current_account
    @current_account ||= current_user.account
  end
end

current_accountRspec के साथ निजी पद्धति का परीक्षण कैसे करें ?

PS मैं Rspec2 और रूबी का उपयोग रेल 3 पर करता हूं


8
यह आपको सवाल का जवाब नहीं देता है, लेकिन निजी तरीकों का परीक्षण नहीं किया जाना चाहिए। आपके परीक्षणों को केवल वास्तविक चीज़ के बारे में ध्यान रखना चाहिए - आपका सार्वजनिक एपीआई। यदि आपके सार्वजनिक तरीके काम करते हैं, तो वे जो निजी कॉल करते हैं वे भी काम करते हैं।
सैमी डिंडाने

77
मैं असहमत हूं। आपके कोड में किसी भी पर्याप्त जटिल सुविधा के परीक्षण में मूल्य है।
श्वेतहाट १०

11
मैं भी असहमत हूं। यदि आपका सार्वजनिक एपीआई काम करता है, तो आप केवल यह मान सकते हैं कि आपके निजी तरीके उम्मीद के मुताबिक काम कर रहे हैं। लेकिन आपके चश्मे संयोग से गुजर रहे होंगे।
रिमीयन

4
निजी विधि को एक नए वर्ग में निकालना बेहतर होगा जो कि परीक्षण योग्य है, यदि निजी विधि को परीक्षण की आवश्यकता है।
क्रिश

10
@RonLugge तुम सही हो। अधिक अड़चन और अनुभव के साथ, मैं अपनी तीन साल पुरानी टिप्पणी से असहमत हूं। :)
समी डिंडाने

जवाबों:


196

#Instance_eval का उपयोग करें

@controller = AccountController.new
@controller.instance_eval{ current_account }   # invoke the private method
@controller.instance_eval{ @current_account }.should eql ... # check the value of the instance variable

94
यदि आप चाहें, तो आप यह भी कह सकते हैं: @ नियंत्रक। Send (: current_account)।
भ्रम

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

9
हालांकि, यह उत्तर तकनीकी रूप से प्रश्न का उत्तर देता है, मैं इसे नीचा दिखा रहा हूं, क्योंकि यह परीक्षण में सर्वोत्तम प्रथाओं का उल्लंघन करता है। निजी तरीकों का परीक्षण नहीं किया जाना चाहिए, और सिर्फ इसलिए कि रूबी आपको विधि दृश्यता को दरकिनार करने की क्षमता देता है, इसका मतलब यह नहीं है कि आपको इसका दुरुपयोग करना चाहिए।
श्रीजान पेजिक

24
श्रीजान पेजिक क्या आप इस बारे में विस्तार से बता सकते हैं कि निजी तरीकों का परीक्षण क्यों नहीं किया जाना चाहिए?
जॉन बाचिर

35
मुझे लगता है कि आप उन उत्तरों को गलत बताते हैं जो गलत हैं या सवाल का जवाब नहीं देते हैं। यह उत्तर सही है और इसे अस्वीकृत नहीं किया जाना चाहिए। यदि आप निजी तरीकों का परीक्षण करने के अभ्यास से असहमत हैं, तो टिप्पणी में डाल दें क्योंकि यह अच्छी जानकारी है (जैसा कि कई लोगों ने किया) और फिर लोग उस टिप्पणी को अपदस्थ कर सकते हैं, जो अभी भी अनावश्यक रूप से मान्य उत्तर को अस्वीकार किए बिना आपकी बात को दर्शाता है।
15

37

मैं भेजने की विधि का उपयोग करें। उदाहरण के लिए:

event.send(:private_method).should == 2

क्योंकि "भेजें" निजी तरीकों को बुला सकता है


आप निजी विधि का उपयोग करके .sendकिस प्रकार से उदाहरण चर का परीक्षण करेंगे ?
12

23

करंट_एसीकाउंट विधि का उपयोग कहां किया जा रहा है? इससे कौन सा उद्देश्य पूरा होगा?

आमतौर पर, आप निजी तरीकों का परीक्षण नहीं करते हैं, बल्कि उन तरीकों का परीक्षण करते हैं जो निजी कॉल करते हैं।


5
आदर्श रूप से प्रत्येक विधि का परीक्षण करना चाहिए। मैंने rspec में ज्यादा सफलता के साथ subject.send और subject.instance_eval दोनों का इस्तेमाल किया है
David W. Keith

7
@Pullets मैं असहमत हूं, आपको एपीआई के सार्वजनिक तरीकों का परीक्षण करना चाहिए जो कि निजी फोन कर रहे हैं, जैसा कि मेरा मूल जवाब कहता है। आपको आपके द्वारा प्रदत्त एपीआई का परीक्षण करना चाहिए, न कि केवल आपके द्वारा देखे जा सकने वाले निजी तरीकों का।
रयान बिग

5
मैं @Ryan Bigg से सहमत हूँ। आप निजी तरीकों का परीक्षण नहीं करते हैं। यह उक्त विधि के कार्यान्वयन को फिर से भरने या बदलने की आपकी क्षमता को हटा देता है, भले ही वह परिवर्तन आपके कोड के सार्वजनिक भागों को प्रभावित न करता हो। स्वचालित परीक्षण लिखते समय कृपया सर्वोत्तम प्रथाओं पर पढ़ें।
श्रीजन पेजिक

4
हम्म, शायद मुझे कुछ याद आ रहा है। मेरे द्वारा लिखी गई कक्षाओं में सार्वजनिक लोगों की तुलना में कई अधिक निजी विधियां थीं। केवल सार्वजनिक एपीआई के माध्यम से परीक्षण सैकड़ों परीक्षणों की एक सूची के परिणामस्वरूप होगा जो कोड का परीक्षण नहीं कर रहे हैं।
डेविड डब्ल्यू कीथ

9
मेरी समझ के अनुसार यदि आप यूनिट परीक्षण में वास्तविक ग्रैन्युलैरिटी प्राप्त करना चाहते हैं तो निजी तरीकों का भी परीक्षण किया जाना चाहिए। अगर आप रिफैक्टर कोड यूनिट टेस्ट करना चाहते हैं, तो भी उसी के अनुसार रिफैक्ट होना चाहिए। यह आपके नए कोड को भी उम्मीद के मुताबिक काम करता है।
इंडिका के

7

आपको अपने निजी तरीकों का सीधे परीक्षण नहीं करना चाहिए , उन्हें सार्वजनिक तरीकों से कोड का उपयोग करके अप्रत्यक्ष रूप से परीक्षण किया जाना चाहिए।

यह आपको अपने परीक्षणों को बदलने के बिना सड़क के नीचे अपने कोड के आंतरिक को बदलने की अनुमति देता है।


4

आप निजी या संरक्षित तरीके सार्वजनिक कर सकते हैं:

MyClass.send(:public, *MyClass.protected_instance_methods) 
MyClass.send(:public, *MyClass.private_instance_methods)

इस कोड को अपने परीक्षण वर्ग में अपने वर्ग नाम को प्रतिस्थापित करते हुए रखें। यदि लागू हो तो नाम स्थान शामिल करें।


3
require 'spec_helper'

describe AdminsController do 
  it "-current_account should return correct value" do
    class AccountController
      def test_current_account
        current_account           
      end
    end

    account_constroller = AccountController.new
    account_controller.test_current_account.should be_correct             

   end
end

1

यूनिट परीक्षण निजी विधियाँ अनुप्रयोग के व्यवहार के साथ संदर्भ से बहुत बाहर लगती हैं।

क्या आप पहले अपना कॉलिंग कोड लिख रहे हैं? इस कोड को आपके उदाहरण में नहीं कहा जाता है।

व्यवहार है: आप किसी अन्य ऑब्जेक्ट से भरी हुई वस्तु चाहते हैं।

context "When I am logged in"
  let(:user) { create(:user) }
  before { login_as user }

  context "with an account"
    let(:account) { create(:account) }
    before { user.update_attribute :account_id, account.id }

    context "viewing the list of accounts" do
      before { get :index }

      it "should load the current users account" do
        assigns(:current_account).should == account
      end
    end
  end
end

आप जिस व्यवहार का वर्णन करने की कोशिश कर रहे हैं, उसके संदर्भ में आप परीक्षण को संदर्भ से बाहर क्यों लिखना चाहते हैं?

क्या इस कोड का उपयोग कई जगहों पर होता है? अधिक सामान्य दृष्टिकोण की आवश्यकता है?

https://www.relishapp.com/rspec/rspec-rails/v/2-8/docs/controller-specs/anonymous-controller


1

एक संदर्भ में अस्थायी रूप से निजी तरीकों को सार्वजनिक करने के लिए आरपीईसी-संदर्भ-निजी रत्न का उपयोग करें ।

gem 'rspec-context-private'

यह आपके प्रोजेक्ट में एक साझा संदर्भ जोड़कर काम करता है।

RSpec.shared_context 'private', private: true do

  before :all do
    described_class.class_eval do
      @original_private_instance_methods = private_instance_methods
      public *@original_private_instance_methods
    end
  end

  after :all do
    described_class.class_eval do
      private *@original_private_instance_methods
    end
  end

end

फिर, यदि आप :privateकिसी describeखंड में मेटाडेटा के रूप में पास करते हैं, तो उस संदर्भ में निजी विधियाँ सार्वजनिक होंगी।

describe AccountController, :private do
  it 'can test private methods' do
    expect{subject.current_account}.not_to raise_error
  end
end

0

यदि आपको एक निजी फ़ंक्शन का परीक्षण करने की आवश्यकता है, तो एक सार्वजनिक विधि बनाएं जो निजी को आमंत्रित करती है।


3
मेरा मानना ​​है कि इसका मतलब है कि यह आपके यूनिट टेस्ट कोड में होना चाहिए। यह अनिवार्य रूप से .instance_eval और .send कोड की एक पंक्ति में है। (और जो एक छोटे से एक ही प्रभाव होने पर लंबे समय तक परीक्षण लिखना चाहता है?)
डेविड डब्ल्यू कीथ

3
आह, यह एक रेल नियंत्रक है। विधि को निजी होना चाहिए। वास्तविक प्रश्न पढ़ने के लिए धन्यवाद।
माइकल जॉनसन

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

0

मुझे पता है कि यह थोड़े हैसी है, लेकिन यह काम करता है यदि आप rspec द्वारा परीक्षण योग्य तरीकों को चाहते हैं, लेकिन उत्पादों में दिखाई नहीं दे रहे हैं।

class Foo
  def public_method
    #some stuff
  end

  eval('private') unless Rails.env == 'test'

  def testable_private_method
    # You can test me if you set RAILS_ENV=test
  end 
end

अब जब आप चला सकते हैं तो आप कल्पना कर सकते हैं:

RAILS_ENV=test bundle exec rspec spec/foo_spec.rb 
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.