एक नई भाषा कैसे दिखेगी अगर इसे खरोंच से डिज़ाइन किया गया हो तो टीडीडी को आसान बनाया जा सकता है?


9

कुछ सबसे आम भाषाओं (जावा, सी #, जावा, आदि) के साथ कभी-कभी ऐसा लगता है कि आप भाषा के साथ काम कर रहे हैं जब आप पूरी तरह से अपना कोड TDD करना चाहते हैं।

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

तो एक भाषा कैसी दिखेगी, अगर इसे स्क्रैच से महान बनाया जा सके तो टीडीडी के साथ? संभवतः निर्भरता का वर्णन करने का कुछ तरीका भाषा स्तर का तरीका (एक निर्माणकर्ता के लिए इंटरफेस को पारित करने के बजाय) और स्पष्ट रूप से ऐसा किए बिना एक वर्ग के इंटरफ़ेस को अलग करने में सक्षम है?


ऐसी भाषा के बारे में जिसे TDD की आवश्यकता नहीं है? blog.8thlight.com/uncle-bob/2011/10/20/Simple-Hickey.html
जॉब

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

टेस्ट ड्रिवेन डेवलपमेंट आपके आंतरिक और बाहरी एपीआई के अधिकार को प्राप्त करने के बारे में है , और आगे ऐसा करें। इसलिए जावा में यह है इंटरफेस के बारे में सभी - वास्तविक कक्षाएं उपोत्पाद है।

जवाबों:


6

कई साल पहले मैंने एक प्रोटोटाइप को फेंक दिया था जो एक समान प्रश्न को संबोधित करता था; यहाँ एक स्क्रीनशॉट है:

जीरो बटन टेस्टिंग

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


2
हाहा, यह अद्भुत है! मुझे वास्तव में कोड के साथ परीक्षण लगाने का विचार पसंद है। यह काफी थकाऊ है (हालांकि बहुत अच्छे कारण हैं) .NET में यूनिट परीक्षणों के समानांतर नामस्थान के साथ अलग-अलग असेंबली हैं। यह भी फिर से सक्रिय करना आसान बनाता है क्योंकि मूविंग कोड, स्वचालित रूप से परीक्षणों को आगे बढ़ाता है: P
जियोफ

लेकिन क्या आप परीक्षणों को वहां छोड़ना चाहते हैं? क्या आप उन्हें उत्पादन कोड के लिए सक्षम छोड़ देंगे? हो सकता है कि वे सी के लिए # ifdef'd हों, अन्यथा हम कोड-आकार / रन-टाइम हिट देख रहे हैं।
मावग का कहना है कि मोनिका

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

5

यह सांख्यिकीय रूप से टाइप किए जाने के बजाय गतिशील रूप से होगा । बत्तख टाइपिंग फिर वही काम करेगा जो इंटरफेस टाइप की गई भाषाओं में करते हैं। इसके अलावा, इसकी कक्षाएं रनटाइम के दौरान परिवर्तनीय होंगी ताकि एक परीक्षण ढांचा मौजूदा कक्षाओं पर आसानी से ठूंठ या नकली तरीकों का उपयोग कर सके। रूबी एक ऐसी भाषा है; TDD के लिए rspec इसका प्रमुख परीक्षण ढांचा है।

कैसे गतिशील टाइपिंग एड्स परीक्षण

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

class MessageSender
  def send
    # Do something with a side effect
  end
end

मान लें कि हमारे पास एक MessageSenderUser है जो MessageSender का एक उदाहरण का उपयोग करता है:

class MessageSenderUser

  def initialize(message_sender)
    @message_sender = message_sender
  end

  def do_stuff
    ...
    @message_sender.send
    ...
    @message_sender.send
    ...
  end

end

यहाँ निर्भरता इंजेक्शन के उपयोग पर ध्यान दें , इकाई परीक्षण का एक मुख्य आधार। हम उस पर वापस आएंगे।

आप परीक्षण करना चाहते हैं कि MessageSenderUser#do_stuffकॉल दो बार भेजें। जैसे आप वैधानिक रूप से टाइप की गई भाषा में हैं, आप एक नकली मैसेजएन्डर बना सकते हैं जो यह बताता है कि कितनी बार sendकॉल किया गया था। लेकिन सांख्यिकीय रूप से टाइप की गई भाषा के विपरीत, आपको कोई इंटरफ़ेस वर्ग नहीं चाहिए। आप बस आगे बढ़ें और इसे बनाएं:

class MockMessageSender

  attr_accessor :send_count

  def initialize
    @send_count = 0
  end

  def send
    @send_count += 1
  end

end

और अपने परीक्षण में इसका उपयोग करें:

mock_sender = MockMessageSender.new
MessageSenderUser.new(mock_sender).do_stuff
assert_equal(mock_sender.send_count, 2)

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

क्या होगा अगर आपको क्लास टेस्ट करने योग्य बनाने के लिए निर्भरता इंजेक्शन का उपयोग नहीं करना पड़े?

मान लीजिए कि MessageSenderUser संदेश भेजने के लिए केवल MessageSender का उपयोग करेगा, और आपको किसी अन्य वर्ग के साथ MessageSender के प्रतिस्थापन की अनुमति देने की कोई आवश्यकता नहीं है। एक कार्यक्रम के भीतर अक्सर ऐसा होता है। चलिए MessageSenderUser को फिर से लिखते हैं ताकि यह केवल एक निर्भरता इंजेक्शन के साथ एक MessageSender बनाता और उपयोग करता है।

class MessageSenderUser

  def initialize
    @message_sender = MessageSender.new
  end

  def do_stuff
    ...
    @message_sender.send
    ...
    @message_sender.send
    ...
  end

end

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

खुली कक्षाएं आपको निर्भरता इंजेक्शन के बिना परीक्षण करने देती हैं

डायनामिक टाइपिंग और ओपन क्लास वाली भाषा में एक टेस्ट फ्रेमवर्क TDD को काफी अच्छा बना सकता है। यहाँ MessageSenderUser के लिए एक rspec परीक्षण से एक कोड स्निपेट है:

mock_message_sender = mock MessageSender
MessageSender.should_receive(:new).and_return(mock_message_sender)
mock_message_sender.should_receive(:send).twice.with(no_arguments)
MessageSenderUser.new.do_stuff

वह पूरी परीक्षा है। यदि ठीक दो बार MessageSenderUser#do_stuffआह्वान नहीं MessageSender#sendकिया जाता है, तो यह परीक्षण विफल हो जाता है। वास्तविक MessageSender वर्ग को कभी भी लागू नहीं किया जाता है: हमने परीक्षण से कहा कि जब भी कोई व्यक्ति MessageSender बनाने का प्रयास करता है, तो उन्हें इसके बजाय हमारा नकली MessageSender प्राप्त करना चाहिए। कोई निर्भरता इंजेक्शन आवश्यक नहीं है।

इस तरह के एक सरल परीक्षण में इतना अच्छा है। जब तक यह वास्तव में आपके डिज़ाइन के लिए समझ में नहीं आता, तब तक निर्भरता इंजेक्शन का उपयोग न करना कभी अच्छा नहीं है।

लेकिन खुले वर्गों के साथ इसका क्या करना है? को कॉल नोट करें MessageSender.should_receive। जब हमने MessageSender लिखा था, तो हमने #should_receive को परिभाषित नहीं किया था, इसलिए किसने किया? इसका उत्तर यह है कि टेस्ट फ्रेमवर्क, सिस्टम कक्षाओं के कुछ सावधान संशोधनों को बनाते हुए, यह प्रकट करने में सक्षम है जैसा कि #should_receive के माध्यम से प्रत्येक वस्तु पर परिभाषित किया गया है। अगर आपको लगता है कि सिस्टम कक्षाओं को संशोधित करना कुछ सावधानी की आवश्यकता है, तो आप सही हैं। लेकिन यह सही बात है कि टेस्ट लाइब्रेरी यहाँ क्या कर रही है, और खुली कक्षाएं इसे संभव बनाती हैं।


बहुत बढ़िया जवाब! आप लोग मुझे वापस गतिशील भाषाओं में बात करना शुरू कर रहे हैं :) मुझे लगता है कि बतख टाइपिंग यहाँ कुंजी है, .new के साथ चाल संभवतः एक सांख्यिकीय रूप से टाइप की गई भाषा में भी होगी (हालांकि यह बहुत कम सुरुचिपूर्ण होगी)।
ज्योफ

3

तो एक भाषा कैसी दिखेगी, अगर इसे स्क्रैच से महान बनाया जा सके तो टीडीडी के साथ?

'TDD के साथ अच्छी तरह से काम करता है' निश्चित रूप से एक भाषा का वर्णन करने के लिए पर्याप्त नहीं है, इसलिए यह किसी भी चीज़ की तरह "दिख सकता है"। लिस्प, प्रोलॉग, सी ++, रूबी, पायथन ... अपनी ले लो।

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

शायद टूल और फ्रेमवर्क के साथ टीडीडी का समर्थन करना बेहतर है। इसे IDE में बनाएँ। एक विकास प्रक्रिया बनाएं जो इसे प्रोत्साहित करे।

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

आप वास्तव में इस प्रश्न का उत्तर देने के लिए कह सकते हैं कि ऐसी भाषा परीक्षण के लिए अनुकूल होगी। मुझे पता है कि बहुत मदद नहीं करता है, लेकिन मुझे लगता है कि समस्या सवाल के साथ है।


सहमत थे, यह अच्छी तरह से वाक्यांश के लिए एक बहुत मुश्किल सवाल है। मुझे लगता है कि मेरा क्या मतलब है कि जावा / सी # जैसी भाषाओं के लिए वर्तमान परीक्षण उपकरण ऐसा महसूस करते हैं कि भाषा थोड़ी सी मिल रही है और कुछ अतिरिक्त / वैकल्पिक भाषा सुविधा पूरे अनुभव को और अधिक सुरुचिपूर्ण बना देगी (अर्थात 90 के लिए इंटरफेस नहीं होना चाहिए) मेरी कक्षाओं का%, केवल वे ही जहाँ यह उच्च-स्तरीय डिज़ाइन बिंदु से समझ में आता है)।
ज्योफ

0

खैर, गतिशील रूप से टाइप की गई भाषाओं को स्पष्ट इंटरफेस की आवश्यकता नहीं होती है। रूबी या पीएचपी देखें, आदि।

दूसरी ओर, जावा और C # या C ++ जैसी वैधानिक रूप से टाइप की गई भाषाएं उन इंटरफेसों को लिखने के लिए आपको टाइप और बाध्य करती हैं।

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

इसलिए, हालांकि आपका प्रश्न दिलचस्प लग सकता है, इसका मतलब है कि आपको निर्भरता इंजेक्शन तकनीक को समझने या लागू करने में समस्या है।

और सीधे आपके सवाल का जवाब देने के लिए, रूबी और पीएचपी के पास अपने यूनिट टेस्टिंग फ्रेमवर्क में निर्मित और अलग से डिलीवर किए गए दोनों मॉकिंग इन्फ्रास्ट्रक्चर हैं (देखें Mockery for PHP)। कुछ मामलों में, ये रूपरेखाएँ आपको वही करने की अनुमति देती हैं जो आप सुझा रहे हैं, स्थैतिक कॉल या ऑब्जेक्ट इनिशियलाइज़ेशन को बिना किसी निर्भरता के स्पष्ट रूप से इंजेक्ट करने जैसी चीजें।


1
मैं मानता हूं कि इंटरफेस बहुत बढ़िया हैं और एक प्रमुख डिजाइन तत्व है। हालांकि, मेरे कोड में मुझे लगता है कि 90% वर्गों में एक इंटरफ़ेस है और उस इंटरफ़ेस के केवल दो कार्यान्वयन हैं, उस वर्ग का वर्ग और नकली। हालांकि यह तकनीकी रूप से बिल्कुल इंटरफेस का बिंदु है, मैं यह महसूस करने में मदद नहीं कर सकता कि यह अयोग्य है।
ज्योफ

मैं जावा और सी # में मॉकिंग से बहुत परिचित नहीं हूं, लेकिन जहां तक ​​मुझे पता है, एक नकली वस्तु वास्तविक वस्तु की नकल करती है। मैं अक्सर ऑब्जेक्ट के प्रकार के एक पैरामीटर का उपयोग करके और इसके बजाय विधि / वर्ग के लिए एक मॉक भेजकर निर्भरता इंजेक्शन करता हूं। फ़ंक्शन की तरह कुछ someName (OtherClass $ object = null) {$ this-> otherObject = $ ऑब्जेक्ट? : नया एक और क्लस्टर; } यह एक इंटरफेस से निकले बिना निर्भरता को इंजेक्ट करने के लिए अक्सर इस्तेमाल की जाने वाली ट्रिक है।
पेटकोस Csaba

1
यह निश्चित रूप से है जहाँ गतिशील भाषाओं में मेरे प्रश्न के संबंध में जावा / सी # प्रकार की भाषाओं का लाभ है। एक ठोस वर्ग का एक विशिष्ट नकली वास्तव में वर्ग का एक उपवर्ग बना देगा, जिसका अर्थ है कि ठोस वर्ग निर्माता को बुलाया जाएगा, जो कुछ ऐसा है जिसे आप निश्चित रूप से बचना चाहते हैं (अपवाद हैं, लेकिन उनकी अपनी समस्याएं हैं)। एक गतिशील मॉक सिर्फ बतख टाइपिंग का लाभ उठाता है, इसलिए कंक्रीट वर्ग और इसके बीच कोई संबंध नहीं है। मैं पायथन में बहुत कोड करता था, लेकिन वह मेरे TDD दिनों से पहले था, शायद यह एक और रूप लेने का समय है!
ज्योफ
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.