2012-11-29 6 views
35

RSpec उम्मीद परिवर्तन:क्या आरएसपीसी के लिए दो टेबलों में बदलाव की उम्मीद है?

it "should increment the count" do 
    expect{Foo.bar}.to change{Counter.count}.by 1 
end 

वहाँ दो तालिकाओं में परिवर्तन की उम्मीद करने के लिए एक तरीका है?

expect{Foo.bar}.to change{Counter.count}.by 1 
and change{AnotherCounter.count}.by 1 

उत्तर

20

यह दो परीक्षण होना चाहिए। RSpec best practices call for one assertion per test

describe "#bar" do 
    subject { lambda { Foo.bar } } 

    it { should change { Counter.count }.by 1 } 
    it { should change { AnotherCounter.count }.by 1 } 
end 
+7

कभी-कभी यह बॉयलरप्लेट कोड के बहुत सारे होते हैं (जब किसी स्पेक को जटिल सेटअप की आवश्यकता होती है)। या शायद मैं इसे गलत कर रहा हूं :) –

+2

आम तौर पर, अगर मैं बहुत सारे बॉयलरप्लेट कर रहा हूं, तो मैं अपने संदर्भ/विवरण ब्लॉक को परिष्कृत करने का प्रयास करता हूं ताकि मैं ब्लॉक से पहले सेटअप कर सकूं। वह आमतौर पर इसे साफ करता है। –

+3

मॉडल/यूनिट परीक्षणों के लिए यह सब ठीक है, लेकिन फीचर/एकीकरण परीक्षणों के बारे में क्या है, जहां एक परीक्षण में कई दावे करना सामान्य बात है? – Arcolye

2

मैं दो कारणों के लिए सर्वोत्तम प्रथाओं अनदेखी कर रहा हूँ:

  1. मेरी परीक्षण का एक सेट प्रतिगमन परीक्षण कर रहे हैं, मैं उन्हें तेजी से चलाना चाहते हैं, और वे शायद ही कभी टूट गया। वास्तव में के बारे में स्पष्टता का लाभ बहुत बड़ा नहीं है, और मेरे कोड को रीफैक्टर करने की मंदी है ताकि यह एक ही घटना को कई बार मेरे लिए सामग्री चला सके।
  2. मैं कभी कभी एक सा आलसी हूँ, और यह आसान है कि refactor

तरह से मैं यह कर रहा हूँ (जब मैं ऐसा करने की आवश्यकता) ऐसा नहीं करने के लिए इस तथ्य पर भरोसा करने के लिए है कि मेरे डेटाबेस शुरू होता है खाली है, इसलिए मैं तो लिख सकते हैं:

foo.bar 
expect(Counter.count).to eq(1) 
expect(Anothercounter.count).to eq(1) 

कुछ मामलों मेरी डेटाबेस खाली नहीं है में, लेकिन मैं या तो गिनती से पहले जानते हैं, या मैं स्पष्ट रूप से पहले के लिए परीक्षण कर सकते हैं गिनती:

counter_before = Counter.count 
another_counter_before = Anothercounter.count 

foo.bar 

expect(Counter.count - counter_before).to eq(1) 
expect(Anothercounter.count - another_counter_before).to eq(1) 

अंतिम ly, अगर आप (मैं कभी कभी करते हैं) की जाँच करने के वस्तुओं की एक बहुत कुछ है आप यह कर सकते हैं के रूप में:

before_counts = {} 
[Counter, Anothercounter].each do |classname| 
    before_counts[classname.name] = classname.count 
end 

foo.bar 

[Counter, Anothercounter].each do |classname| 
    expect(classname.count - before_counts[classname.name]).to be > 0 
end 

आप समान की जरूरत है यह मेरे लिए काम करेंगे है, तो अपने ही सलाह के साथ ऐसा करना होगा अपने आंखें खुली - प्रस्तावित अन्य समाधान अधिक सुरुचिपूर्ण हैं लेकिन कुछ परिस्थितियों में बस कुछ डाउनसाइड्स हैं।

11

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

it "should increment the count" do 
    expectation = expect { Foo.bar } 
    expectation.to change { Counter.count }.by 1 
    expectation.to change { AnotherCounter.count }.by 1 
end 
+0

वास्तविक प्रश्न का उत्तर देने के लिए धन्यवाद, इसकी बहुत सराहना की जाती है। –

+7

हम्म, यह अभी भी ब्लॉक को दो बार चलाता है। –

+0

यदि उम्मीदों में से एक यह है कि एक त्रुटि उठाई जाएगी, यह काम नहीं करता है - यह – xxjjnn

71

मैं इस वाक्य रचना (rspec 3 या बाद में) पसंद करते हैं:

it "should increment the counters" do 
    expect { Foo.bar }.to change { Counter,  :count }.by(1).and \ 
         change { AnotherCounter, :count }.by(1) 
end 

हाँ, यह एक ही स्थान पर दो कथनों रहे हैं, लेकिन क्योंकि ब्लॉक सिर्फ एक समय मार डाला जाता है, यह परीक्षण speedup कर सकते हैं ।

संपादित करें: जोड़ा गया बैकस्लैश .and के बाद सिंटेक्स त्रुटि

+0

उम्मीद को असाइन करने में विफल रहता है, यह केवल पिछले सभी को अनदेखा करते समय अंतिम दावा का परीक्षण करता है। दावे के संयोजन के उचित तरीके के लिए मेरा जवाब देखें। – somecto

+1

रुपये 3 में, उपर्युक्त विधि काम करेगी यदि आप रचना ऑपरेटर '.and' का उपयोग करते हैं (लेकिन आप rspec दस्तावेज़ों में वर्णित एकल' और 'उपनाम का उपयोग नहीं कर सकते हैं और & n ऑपरेटर को मूल उत्तर में) के रूप में उपयोग नहीं कर सकते हैं। मैंने काम के जवाब को इरादे के रूप में संपादित किया है। –

+3

असल में, ब्लॉक में आरएसपीईसी 3 रचना परिणामों का उपयोग कई बार चलाया जा रहा है :(। श्वास। धन्यवाद, आप सभी को एकीकरण परीक्षणों के लिए निंदा करने के लिए कार्गो कल्टीस्टिस्ट्स का धन्यवाद करते हैं जो –

3

जोर्ज Ladermann की वाक्य रचना से बचने के लिए अच्छे है, लेकिन यह काम नहीं करता। कई मूल्य परिवर्तनों के परीक्षण के लिए तरीका सरणी में मानों को जोड़कर है। अन्यथा, केवल अंतिम परिवर्तन दावा परीक्षण पर फैसला करेगा।

it "should increment the counters" do 
    expect { Foo.bar }.to change { [Counter.count, AnotherCounter.count] }.by([1,1]) 
end 

यह 'करें.अभियान' समारोह के साथ perfectecly काम करता है:

यहाँ कैसे मैं यह कर रहा है।

+3

, यह ऐसा नहीं कर रहा है जो आपको लगता है। यह केवल जोर दे रहा है कि अंतिम गणना [1, 1] है। मेरा मतलब यह देखने के लिए, पहले से मौजूद कुछ काउंटर रिकॉर्ड के साथ अपना परीक्षण चलाएं। –

+0

देखें [मेरा जवाब] (http://stackoverflow.com/a/24591809/173542) rspec 3 –

+0

@MichaelJohnston के लिए एक काम करने वाले बदलाव_मल्टीप्लर मैचर के लिए आप बिल्कुल सही हैं। यह '.by' विधि के साथ काम नहीं करता है, लेकिन यह '.to' विधि के साथ काम करता है। तो वास्तव में यह मूल प्रश्न का उत्तर देता है कि यदि एकाधिक तालिकाओं में परिवर्तन की अपेक्षा करना संभव है। इसके अलावा, ब्लॉक इस मामले में केवल एक बार चलता है। मैं मानता हूं कि केवल एक तत्व के लिए परीक्षण सीमित करना वास्तव में उप-स्थानिक है। – somecto

2

प्रस्तावित समाधानों में से कोई भी वास्तव में काम करने के बाद साबित हुआ, मैंने इसे change_multiple मैचर जोड़कर पूरा किया। यह केवल RSpec 3 के लिए काम करेंगे, और उपयोग के नहीं 2. *

module RSpec 
    module Matchers 
    def change_multiple(receiver=nil, message=nil, &block) 
     BuiltIn::ChangeMultiple.new(receiver, message, &block) 
    end 
    alias_matcher :a_block_changing_multiple, :change_multiple 
    alias_matcher :changing_multiple,   :change_multiple 

    module BuiltIn 
     class ChangeMultiple < Change 
     private 

      def initialize(receiver=nil, message=nil, &block) 
      @change_details = ChangeMultipleDetails.new(receiver, message, &block) 
      end 
     end 
     class ChangeMultipleDetails < ChangeDetails 
     def actual_delta 
      @actual_after = [@actual_after].flatten 
      @actual_before = [@actual_before].flatten 
      @actual_after.map.with_index{|v, i| v - @actual_before[i]} 
     end 
     end 
    end 
    end 
end 

उदाहरण:

it "expects multiple changes despite hordes of cargo cultists chanting aphorisms" do 
    a = "." * 4 
    b = "." * 10 
    times_called = 0 
    expect { 
    times_called += 1 
    a += ".." 
    b += "-----" 
    }.to change_multiple{[a.length, b.length]}.by([2,5]) 
    expect(times_called).to eq(1) 
end 

बनाना change_multiple के लिए by_at_least और by_at_most काम कुछ अतिरिक्त कार्य की आवश्यकता होगी।

+0

यह अच्छा है, लेकिन 'x' को 1 से 2 में बदलना चाहिए जैसे दावों को मिश्रण करना संभव नहीं है,' y' 3 में बदलना चाहिए, 'z' 4 से बदलना चाहिए, और के को बस बदलना चाहिए। –

4

सबसे अच्छा तरीका मैंने पाया यह करने के लिए "मैन्युअल" है:

counters_before   = Counter.count 
another_counters_before = AnotherCounter.count 
Foo.bar 
expect(Counter.count).to eq (counters_before + 1) 
expect(AnotherCounter.count).to eq (another_counters_before + 1) 

नहीं सबसे सुरुचिपूर्ण समाधान लेकिन यह

21

मैं @MichaelJohnston's solution उपयोग करने की कोशिश वाक्यविन्यास त्रुटियों मिला काम करता है; इस फार्म का है कि अंत में मेरे लिए काम किया है:

it "should increment the counters" do 
    expect { Foo.bar }.to change { Counter.count }.by(1) 
    .and change { AnotherCounter.count }.by(1) 
end 

मैं मैं गहरे लाल रंग का 2.2.2p95 उपयोग कर रहा हूँ उल्लेख करना चाहिए - इस संस्करण का कारण बनता है कि मुझे त्रुटियों पाने के लिए पार्स में कुछ सूक्ष्म परिवर्तन है, तो मैं नहीं जानता कि, ऐसा नहीं लगता है कि इस धागे में किसी और को यह समस्या है।

+1

यूप। यह मेरे लिए भी काम किया। धन्यवाद। – poweratom

+0

यहां एक और अंगूठे ऊपर। 2016 के मध्य में सब कुछ के नवीनतम संस्करण में काम किया। आप ब्लॉक के बजाय, 'परिवर्तन' विधि के लिए पैरा को भी पास कर सकते हैं: 'बदलने के लिए (काउंटर,: गिनती) .by (1)' अगर यह आपकी कल्पना को गुदगुदी करता है। –