मॉक / स्टब / स्पॉक टेस्ट फ्रेमवर्क में अंतर


101

मैं नकली परीक्षण में मॉक, स्टब और स्पाई के बीच के अंतर को नहीं समझता और जिन ट्यूटोरियल को मैं ऑनलाइन देख रहा हूं, उन्हें विस्तार से नहीं समझाता।

जवाबों:


94

ध्यान दें: मैं देखरेख करने जा रहा हूं और शायद आने वाले पैराग्राफ में थोड़ा सा गलत भी हो सकता है। अधिक विस्तृत जानकारी के लिए मार्टिन फाउलर की वेबसाइट देखें

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

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

एक स्पाई वास्तविक वस्तु और स्टब के बीच एक प्रकार का संकर है, अर्थात यह मूल रूप से स्टब विधियों द्वारा छायांकित कुछ (सभी नहीं) विधियों के साथ वास्तविक वस्तु है। गैर-स्टब किए गए तरीके मूल ऑब्जेक्ट के माध्यम से रूट किए जाते हैं। इस तरह से आप "सस्ते" या तुच्छ तरीकों के लिए मूल व्यवहार कर सकते हैं और "महंगी" या जटिल तरीकों के लिए नकली व्यवहार कर सकते हैं।


अद्यतन 2017-02-06: वास्तव में उपयोगकर्ता मिखाइल का उत्तर मेरे मूल से ऊपर स्पॉक के लिए अधिक विशिष्ट है। तो Spock के दायरे में, वह जो वर्णन करता है वह सही है, लेकिन यह मेरे सामान्य उत्तर को गलत नहीं ठहराता है:

  • एक स्टब विशिष्ट व्यवहार का अनुकरण करने से संबंधित है। Spock में यह सब एक स्टब कर सकता है, इसलिए यह सबसे सरल चीज़ है।
  • एक नकली एक (संभवतः महंगी) वास्तविक वस्तु के लिए खड़े होने के साथ संबंधित है, सभी विधि कॉल के लिए नो-ऑप उत्तर प्रदान करता है। इस संबंध में, एक ठूंठ की तुलना में सरल है। लेकिन स्पॉक में, एक मॉक विधि परिणाम भी स्टब कर सकती है, अर्थात मॉक और स्टब दोनों हो सकते हैं। इसके अलावा, Spock में हम यह परीक्षण कर सकते हैं कि एक परीक्षण के दौरान कुछ मापदंडों के साथ कितनी बार विशिष्ट नकली तरीकों को बुलाया गया है।
  • एक जासूस हमेशा एक वास्तविक वस्तु को लपेटता है और डिफ़ॉल्ट मार्गों से सभी विधि मूल वस्तु को कॉल करता है, मूल परिणामों से भी गुजर रहा है। विधि कॉल काउंटिंग भी जासूसों के लिए काम करती है। स्पॉक में, एक जासूस मूल वस्तु के व्यवहार को भी संशोधित कर सकता है, विधि कॉल मापदंडों और / या परिणामों में हेरफेर कर सकता है या मूल तरीकों को बिल्कुल भी कॉल करने से रोक सकता है।

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

package de.scrum_master.stackoverflow

import org.spockframework.mock.TooFewInvocationsError
import org.spockframework.runtime.InvalidSpecException
import spock.lang.FailsWith
import spock.lang.Specification

class MockStubSpyTest extends Specification {

  static class Publisher {
    List<Subscriber> subscribers = new ArrayList<>()

    void addSubscriber(Subscriber subscriber) {
      subscribers.add(subscriber)
    }

    void send(String message) {
      for (Subscriber subscriber : subscribers)
        subscriber.receive(message);
    }
  }

  static interface Subscriber {
    String receive(String message)
  }

  static class MySubscriber implements Subscriber {
    @Override
    String receive(String message) {
      if (message ==~ /[A-Za-z ]+/)
        return "ok"
      return "uh-oh"
    }
  }

  Subscriber realSubscriber1 = new MySubscriber()
  Subscriber realSubscriber2 = new MySubscriber()
  Publisher publisher = new Publisher(subscribers: [realSubscriber1, realSubscriber2])

  def "Real objects can be tested normally"() {
    expect:
    realSubscriber1.receive("Hello subscribers") == "ok"
    realSubscriber1.receive("Anyone there?") == "uh-oh"
  }

  @FailsWith(TooFewInvocationsError)
  def "Real objects cannot have interactions"() {
    when:
    publisher.send("Hello subscribers")
    publisher.send("Anyone there?")

    then:
    2 * realSubscriber1.receive(_)
  }

  def "Stubs can simulate behaviour"() {
    given:
    def stubSubscriber = Stub(Subscriber) {
      receive(_) >>> ["hey", "ho"]
    }

    expect:
    stubSubscriber.receive("Hello subscribers") == "hey"
    stubSubscriber.receive("Anyone there?") == "ho"
    stubSubscriber.receive("What else?") == "ho"
  }

  @FailsWith(InvalidSpecException)
  def "Stubs cannot have interactions"() {
    given: "stubbed subscriber registered with publisher"
    def stubSubscriber = Stub(Subscriber) {
      receive(_) >> "hey"
    }
    publisher.addSubscriber(stubSubscriber)

    when:
    publisher.send("Hello subscribers")
    publisher.send("Anyone there?")

    then:
    2 * stubSubscriber.receive(_)
  }

  def "Mocks can simulate behaviour and have interactions"() {
    given:
    def mockSubscriber = Mock(Subscriber) {
      3 * receive(_) >>> ["hey", "ho"]
    }
    publisher.addSubscriber(mockSubscriber)

    when:
    publisher.send("Hello subscribers")
    publisher.send("Anyone there?")

    then: "check interactions"
    1 * mockSubscriber.receive("Hello subscribers")
    1 * mockSubscriber.receive("Anyone there?")

    and: "check behaviour exactly 3 times"
    mockSubscriber.receive("foo") == "hey"
    mockSubscriber.receive("bar") == "ho"
    mockSubscriber.receive("zot") == "ho"
  }

  def "Spies can have interactions"() {
    given:
    def spySubscriber = Spy(MySubscriber)
    publisher.addSubscriber(spySubscriber)

    when:
    publisher.send("Hello subscribers")
    publisher.send("Anyone there?")

    then: "check interactions"
    1 * spySubscriber.receive("Hello subscribers")
    1 * spySubscriber.receive("Anyone there?")

    and: "check behaviour for real object (a spy is not a mock!)"
    spySubscriber.receive("Hello subscribers") == "ok"
    spySubscriber.receive("Anyone there?") == "uh-oh"
  }

  def "Spies can modify behaviour and have interactions"() {
    given:
    def spyPublisher = Spy(Publisher) {
      send(_) >> { String message -> callRealMethodWithArgs("#" + message) }
    }
    def mockSubscriber = Mock(MySubscriber)
    spyPublisher.addSubscriber(mockSubscriber)

    when:
    spyPublisher.send("Hello subscribers")
    spyPublisher.send("Anyone there?")

    then: "check interactions"
    1 * mockSubscriber.receive("#Hello subscribers")
    1 * mockSubscriber.receive("#Anyone there?")
  }
}

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

आपकी प्रतिक्रिया के लिए धन्यवाद @ मिखाइल और चिपिकिक। मैंने अपने उत्तर को अपडेट किया है, उम्मीद है कि मैंने जो कुछ चीजें लिखी हैं, उनमें सुधार कर रहा हूं। डिस्क्लेमर: मेरे मूल उत्तर में मैंने कहा था कि मैं कुछ स्पोक से संबंधित तथ्यों की देखरेख कर रहा हूं और कुछ गलत साबित कर रहा हूं। मैं चाहता था कि लोग बुनियादी मतभेदों को समझें, ठगना, मजाक करना और जासूसी करना।
krgaex

@chipiik, आपकी टिप्पणी के उत्तर के रूप में एक और बात: मैं कई वर्षों से विकास टीमों की कोचिंग कर रहा हूं और उन्हें अन्य नकली रूपरेखाओं के साथ Spock या अन्य JUnit का उपयोग करते हुए देखा है। ज्यादातर मामलों में, जब मॉक का उपयोग करते हैं, तो उन्होंने व्यवहार को सत्यापित करने के लिए ऐसा नहीं किया (यानी गणना विधि कॉल) लेकिन विषय को इसके वातावरण से अलग करने के लिए। इंटरेक्शन काउंटिंग IMO केवल एक ऐड-ऑन गुडी है और इसका इस्तेमाल सोच-समझकर और संयम से किया जाना चाहिए क्योंकि ऐसे परीक्षणों के लिए एक प्रवृत्ति होती है जब वे अपने वास्तविक व्यवहार से अधिक घटकों के तारों का परीक्षण करते हैं।
कुर्गेक्स

इसका संक्षिप्त लेकिन अभी भी बहुत ही उपयोगी उत्तर है
चकलादार असफ़ाक आरफ़

55

प्रश्न स्पॉक फ्रेमवर्क के संदर्भ में था और मुझे विश्वास नहीं है कि वर्तमान उत्तर इसे ध्यान में रखते हैं।

स्पॉक डॉक्स के आधार पर (उदाहरण के लिए अनुकूलित, मेरा अपना शब्द जोड़ा गया):

स्टब: सहयोगियों को एक निश्चित तरीके से विधि कॉल का जवाब देने के लिए उपयोग किया जाता है। जब एक विधि को ठोकर मारते हैं, तो आप परवाह नहीं करते हैं कि क्या और कितनी बार विधि कहा जा रहा है; आप बस यह चाहते हैं कि जब भी यह कहा जाए, कुछ मूल्य लौटाएं, या कुछ दुष्प्रभाव करें।

subscriber.receive(_) >> "ok" // subscriber is a Stub()

मॉक: विनिर्देश और उसके सहयोगियों के तहत वस्तु के बीच बातचीत का वर्णन करने के लिए उपयोग किया जाता है।

def "should send message to subscriber"() {
    when:
        publisher.send("hello")

    then:
        1 * subscriber.receive("hello") // subscriber is a Mock()
}

एक नकली एक नकली और एक ठूंठ के रूप में कार्य कर सकता है:

1 * subscriber.receive("message1") >> "ok" // subscriber is a Mock()

जासूसी: हमेशा वास्तविक चीजों पर आधारित वास्तविक वस्तु पर आधारित होती है जो वास्तविक चीजें करती हैं। चुनिंदा तरीकों के रिटर्न वैल्यू को बदलने के लिए स्टब की तरह इस्तेमाल किया जा सकता है। बातचीत का वर्णन करने के लिए एक मॉक की तरह इस्तेमाल किया जा सकता है।

def subscriber = Spy(SubscriberImpl, constructorArgs: ["Fred"])

def "should send message to subscriber"() {
    when:
        publisher.send("hello")

    then:
        1 * subscriber.receive("message1") >> "ok" // subscriber is a Spy(), used as a Mock an Stub
}

def "should send message to subscriber (actually handle 'receive')"() {
    when:
        publisher.send("hello")

    then:
        1 * subscriber.receive("message1") // subscriber is a Spy(), used as a Mock, uses real 'receive' function
}

सारांश:

  • एक ठूंठ () एक ठूंठ है।
  • एक मॉक () एक स्टब और मॉक है।
  • एक स्पाई () स्टब, मॉक और स्पाई है।

स्टोक () पर्याप्त है तो मॉक () का उपयोग करने से बचें।

जासूस () का उपयोग करने से बचें, यदि आप ऐसा कर सकते हैं, तो एक गंध और गलत परीक्षण या परीक्षण के तहत वस्तु के गलत डिजाइन पर संकेत हो सकता है।


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

1
"ए स्पाई () स्टब, मॉक एंड स्पाई है।" यह पापी जासूसों के लिए सही नहीं है?
के - एसओ में विषाक्तता बढ़ रही है।

2
मैंने बस सिनोन जासूसों पर एक त्वरित नज़र डाली और वे ऐसे दिखते हैं जैसे वे मोक्स या स्टब्स के रूप में व्यवहार नहीं करते हैं। ध्यान दें कि यह प्रश्न / उत्तर Spock के संदर्भ में है, जो कि Groovy है, JS नहीं।
मिखाइल

यह सही उत्तर होना चाहिए, क्योंकि यह स्पोक के संदर्भ में है। इसके अलावा, यह कहते हुए कि एक स्टब एक फैंसी मॉक है, भ्रामक हो सकता है, क्योंकि एक मॉक में अतिरिक्त कार्यक्षमता (इन्वोकेशन काउंट की जांच) होती है, जो स्टब नहीं करता है (स्टब की तुलना में मॉक> प्रशंसक)। फिर से, नकली के अनुसार नकली और स्टब्स।
CGK

13

समान्य शब्दों में:

मॉक: आप एक प्रकार का मजाक करते हैं और मक्खी पर आपको एक वस्तु बनाई जाती है। इस नकली वस्तु के तरीके रिटर्न प्रकार के डिफ़ॉल्ट मान लौटाते हैं।

स्टब: आप एक स्टब क्लास बनाते हैं जहाँ तरीकों को आपकी आवश्यकता के अनुसार परिभाषा के साथ पुनर्परिभाषित किया जाता है। Ex: वास्तविक ऑब्जेक्ट विधि में आप कॉल करते हैं और बाहरी एपीआई करते हैं और उपयोगकर्ता नाम और आईडी को वापस करते हैं। स्टबड ऑब्जेक्ट विधि में आप कुछ डमी नाम वापस करते हैं।

जासूस: आप एक असली वस्तु बनाते हैं और फिर आप उसकी जासूसी करते हैं। अब आप कुछ तरीकों का मजाक उड़ा सकते हैं और कुछ के लिए ऐसा नहीं करने का विकल्प चुना।

एक उपयोग अंतर यह है कि आप विधि स्तर की वस्तुओं का मजाक नहीं उड़ा सकते। जब आप विधि में एक डिफ़ॉल्ट ऑब्जेक्ट बना सकते हैं और तब जासूसी वस्तु में तरीकों का वांछित व्यवहार प्राप्त करने के लिए उस पर जासूसी कर सकते हैं।


0

स्टब्स वास्तव में केवल इकाई परीक्षण की सुविधा के लिए हैं, वे परीक्षण का हिस्सा नहीं हैं। मोक्स, परीक्षण का हिस्सा, सत्यापन का हिस्सा, पास / असफल का हिस्सा हैं।

तो, मान लीजिए कि आपके पास एक ऐसी विधि है जो एक पैरामीटर के रूप में किसी ऑब्जेक्ट में ले जाती है। आप कभी भी ऐसा कुछ नहीं करते हैं जो परीक्षण में इस पैरामीटर को बदलता है। आप बस इससे एक मान पढ़ते हैं। वह एक ठूंठ है।

यदि आप कुछ भी बदलते हैं, या ऑब्जेक्ट के साथ किसी प्रकार की सहभागिता को सत्यापित करने की आवश्यकता है, तो यह एक नकली है।

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