Yandex में परीक्षण: ObjectBuilders का वर्णन करने और सिंथेटिक परीक्षण डेटा उत्पन्न करने के लिए

नमस्ते! मेरा नाम डेनिस चेर्निल्वस्की है। यैंडेक्स में, मैं प्रदर्शन विज्ञापन प्रणाली के स्वचालन परीक्षण समूह का नेतृत्व करता हूं। यांडेक्स और पिछले स्थानों में मेरे काम की प्रक्रिया में, मैं 10+ लोगों की टीमों का नेतृत्व करने, प्रक्रियाओं को स्थापित करने और विभिन्न प्रणालियों के स्वचालित परीक्षण के लिए दृष्टिकोण के साथ आने में कामयाब रहा। और यह सिर्फ इतना हुआ कि इन परियोजनाओं में से प्रत्येक में मुझे परीक्षण डेटा तैयार करने के बारे में सोचना था। एक लंबे समय तक प्रतिबिंब के परिणामों के आधार पर, एक दृष्टिकोण का आविष्कार किया गया था जो सामान्य रूप से इस समस्या को हल करने और इसे विभिन्न परियोजनाओं में लागू करने की अनुमति देता है। इस तथ्य के अलावा कि मैं टेस्ट पर्यावरण पर इसके बारे में बात करूंगा, मैंने यहां विवरण बताने का फैसला किया।

वैसे, यदि आप परीक्षकों के लिए हमारे कार्यक्रम में नहीं आ सकते हैं, तो आप प्रसारण देख सकते हैं, जो कल, शनिवार, 30 नवंबर को 11:00 बजे शुरू होगा।

छवि

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

नीचे मैं समझने योग्य उदाहरणों में इस उपकरण के संचालन का वर्णन करने और दिखाने की कोशिश करूंगा, लेकिन सबसे पहले, अधिक विस्तार से, समस्या को हल करने और इसे हल करने के लिए संभावित विकल्पों के बारे में।

उद्देश्य: आपको अपने प्रदर्शन विज्ञापन प्रणाली का परीक्षण करने की आवश्यकता है।


तो, "क्या करें यदि परीक्षण के लिए मुझे सिस्टम में 30 (50, 100 ...) ऑब्जेक्ट बनाने की आवश्यकता है जो कि किसी एक से कई, कई से एक, एक से कई, एक से एक, और 10 (से) से जुड़े हुए हैं 20, 40 ...) प्रत्येक गुण? "

यहां मैं तुरंत एक आरक्षण करूंगा: यह कार्यात्मक ब्लैकबॉक्स परीक्षण स्थापित करने के बारे में है - इकाई परीक्षणों या भार के बिना।

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

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

ऐसा लगता है कि सब कुछ सरल है: उन्होंने अलग-अलग मापदंडों के साथ एक या कई विज्ञापन अभियान शुरू किए, सिस्टम शुरू किया, एक HTTP अनुरोध निकाला, एक प्रतिक्रिया प्राप्त की, सत्यापित किया कि वे इंतजार कर रहे थे कि वे क्या उम्मीद कर रहे थे और, शायद, कुछ साइड चीजों के लिए जाँच की: लॉग, सांख्यिकी, आदि। घ।

सिस्टम को कम से कम एक बैनर प्रदर्शित करने के प्रयास में एक दिन एक सप्ताह बीत गया। यह पता चला है कि सिस्टम में एक विज्ञापन अभियान बनाने और कम से कम एक बैनर दिखाने के लिए, डेटाबेस में 30 ऑब्जेक्ट्स को प्रत्येक 15 मापदंडों के साथ हथौड़ा करना आवश्यक है, और यहां तक ​​कि ताकि वे एक-दूसरे के साथ सही तरीके से जुड़े हों और विभिन्न परीक्षण डेटा एक-दूसरे को प्रभावित न करें। खैर, और यह भी वांछनीय है कि परीक्षण एक पंक्ति में कई बार चलाया जा सकता है!

लेकिन आखिरकार, प्रत्येक परीक्षण में हमें अलग-अलग विज्ञापन अभियानों की आवश्यकता होती है, और यहां तक ​​कि कई, और अलग-अलग सेटिंग्स के साथ भी।

छवि

और तब लगता है कि हम हिट हैं। लेकिन आपको कुछ करने की जरूरत है।

सोच, हमने निर्माण प्रक्रिया और परीक्षण डेटा के लिए आवश्यकताओं की निम्नलिखित सूची बनाई:
  1. बनाया गया डेटा सिस्टम का सही संचालन सुनिश्चित करना चाहिए।
  2. वस्तुओं के कनेक्शन या मापदंडों के तर्क को बदलते समय (उदाहरण के लिए, एक नई सुविधा विकसित करते समय), इन वस्तुओं का उपयोग करके सभी परीक्षणों को आसानी से और जल्दी से ठीक करना संभव होना चाहिए, अर्थात्। भविष्य में प्रकाश परीक्षण सहायता की आवश्यकता है।
  3. एक परीक्षण के डेटा को दूसरे परीक्षण के व्यवहार को प्रभावित नहीं करना चाहिए।
  4. परीक्षण को कई बार पुनरारंभ करना संभव होना चाहिए। इसलिए, डेटा को या तो हर बार अपनी मूल स्थिति में वापस लाया जाना चाहिए, या फिर से बनाया जाना चाहिए।
  5. इस डेटा के विवरण से, यह समझना संभव है कि वे अन्य सभी मामलों से बिल्कुल अलग कैसे हैं और इस परीक्षण के लिए ठीक ऐसी सेटिंग्स की आवश्यकता क्यों है

समाधान के विकल्प


आप उत्पादन प्रणाली से मौजूदा डेटा नहीं ले सकते।

प्रत्येक विशिष्ट परीक्षण के लिए पहले से डेटा के साथ एक डेटाबेस तैयार करने का विकल्प भी उपयुक्त नहीं है।
मुश्किल: कई मापदंडों और वस्तुओं

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

बिल्डर पैटर्न, या हमने अभी भी इसका उपयोग क्यों नहीं किया


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

विकी के उदाहरणों से, यह स्पष्ट है कि यह दृष्टिकोण हमारी सभी आवश्यकताओं को पूरा करता है:

बहुत बढ़िया!

इसके अलावा, बिल्डर का उपयोग समग्र वस्तुओं को बनाने के लिए किया जा सकता है। अर्थात्, संबंधित वस्तुओं के पूरे रेखांकन, जिनकी व्याख्या एकल तार्किक वस्तु के रूप में की जा सकती है। हमें बस एक-दूसरे के बीच विभिन्न प्रकार के संबंधों के साथ बहुत सी वस्तुओं को बनाने की जरूरत है, और वस्तुओं के पूरे पेड़ के किसी भी पैरामीटर और उसके व्यक्तिगत घटकों को एक ही तरीके से कॉन्फ़िगर करने में सक्षम हो।

ऐसा लगता है कि सब कुछ ठीक है, लेकिन इन पैटर्नों में एक खामी है: प्रत्येक प्रकार के ऑब्जेक्ट के लिए आपको अपने खुद के बिल्डर को लागू करने की आवश्यकता होती है और संभवतः अलग-अलग वर्गों के रूप में आपका खुद का कंपोजिट । उन तरीकों का वर्णन करना भी आवश्यक है जो आपको उन वस्तुओं को संशोधित करने की अनुमति देते हैं जिन्हें हम बनाने जा रहे हैं, उनके रिश्ते और गुण।

आलसी लोग होने के नाते, हमने सोचा कि किसी भी तरह से डेटा मॉडल का औपचारिक रूप से वर्णन करना बहुत अच्छा होगा, जिसके आधार पर बिल्डर काम करेगा और इसे आसानी से संशोधित करने में सक्षम होगा।

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

सिद्धांत का एक सा और दृष्टिकोण का वर्णन


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

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

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

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

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

एक सरल उदाहरण - एक कार कारखाना


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

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

हम इनपुट के लिए विभिन्न वाहन विन्यास की आपूर्ति करेंगे। प्रत्येक विन्यास में निम्नलिखित भाग होते हैं: चेसिस (यात्री कार, क्रॉस-कंट्री), इंजन (प्रकार: डीजल या गैसोलीन और वॉल्यूम), पहिए (पहियों, व्यास, कास्ट या मुद्रांकित), बॉडी (सेडान, कूप, परिवर्तनीय, सभी इलाके वाहन), गियरबॉक्स (मैनुअल, स्वचालित)। आदर्श रूप से, हमें सभी संभावित सही संयोजनों की जांच करने की आवश्यकता है (टीके द्वारा निषिद्ध लोगों को छोड़कर)।
यह स्पष्ट है कि संयंत्र विभिन्न कारों का उत्पादन कर सकता है, लेकिन कुछ अन्य की तुलना में अधिक सामान्य हैं। सामान्य तर्क और बाजार की आवश्यकताओं के आधार पर, यह मानना ​​तर्कसंगत होगा कि अक्सर हम 4 पहियों वाली कारों का उत्पादन करेंगे, एक सेडान में, 15 "मुद्रांकित पहियों पर, एक 1.6 पेट्रोल इंजन और एक मैनुअल गियरबॉक्स के साथ।

कम से कम 4% पहिए और सेडान बॉडी संभवतः उनमें से 90% में होगी! यह हमारा मूल विन्यास होगा। शेष मापदंडों को उसी सिद्धांत के अनुसार चुना जाता है।

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

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

ObjectBuilders लाइब्रेरी को लागू करना और उसका उपयोग करना


हमारा उपकरण 2 स्तरों पर काम करता है:
  1. निर्माण - वस्तुओं के बीच संबंध स्थापित करने और वस्तुओं को स्वयं उत्पन्न करने के लिए एक उपकरण प्रदान करता है।
  2. संशोधक - वस्तुओं के एक ग्राफ के लिए पैच (संशोधक) का वर्णन करने और लागू करने के लिए एक उपकरण प्रदान करता है।

बदले में, संशोधक 2 प्रकारों में विभाजित हैं:
  1. InstanceModifiers - ऑब्जेक्ट संशोधक।
  2. ConstructModifiers - संशोधक का निर्माण करें।


उदाहरण के लिए।

class Bar: bar = 1 class Foo: baz = 10 bars = Collection(Bar) my_foo = Builder(Foo).withA(NumberOf(Foo.bars, 5)).build() 


यहाँ bars = Collection(Bar) का निर्माण bars = Collection(Bar) स्तर 1 है। हम कहते हैं कि वर्ग फू में बार के N ऑब्जेक्ट हो सकते हैं। इन वस्तुओं को my_foo.bars[i] माध्यम से एक्सेस किया जा सकता है। और NumberOf(Foo.bars, 5) निर्माण NumberOf(Foo.bars, 5) स्तर 2 है। हम अंदर 5 बार ऑब्जेक्ट्स के साथ एक फू ऑब्जेक्ट प्राप्त करना चाहते हैं।

डिफ़ॉल्ट रूप से, 2 वस्तुओं का एक ग्राफ बनाया जाएगा: फू और बार ऑब्जेक्ट्स का एक नेस्टेड संग्रह, जिसमें एक तत्व शामिल है।
NumberOf(Foo.bars, 5) बिल्कुल संशोधक है जिसे ग्राफ पर लागू किया जा सकता है ताकि 5 बार ऑब्जेक्ट Foo ऑब्जेक्ट में एम्बेडेड हो।

वापस हमारे कार कारखाने के लिए।

ऑब्जेक्ट मॉडल


सबसे पहले, हमें वस्तुओं के वर्ग और उनके गुणों का वर्णन करना होगा जो हमारे कारखाने के साथ काम करते हैं।

 CHASSIS_LIGHT = 0 #  CHASSIS_HEAVY = 1 #  ENGINE_PETROL = 0 #  ENGINE_DIESEL = 1 #  WHEEL_STAMPED = 0 #  WHEEL_ALLOY = 1 #  BODY_SEDAN = 0 #  BODY_COUPE = 1 #  BODY_CABRIO = 2 #  BODY_HEAVY = 3 #  TRANSMISSION_MANUAL = 0 #  TRANSMISSION_AUTO = 1 #  # class Chassis: type = CHASSIS_LIGHT # class Engine: type = ENGINE_PETROL volume = 1.6 # class Wheel: radius = 15 type = WHEEL_STAMPED # class Body: type = BODY_SEDAN number = ??? #      .         . #  class Transmission: type = TRANSMISSION_MANUAL # #       .  -   /    . class Spoiler: foo = None 


बहुत बढ़िया!

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

उदाहरण के लिए।
 chassis = Chassis() wheels = [Wheel() for _ in range(4)] engine = Engine() body = Body() ... chassis.wheels = wheels chassis.engine = engine engine.chassis = chassis chassis.body = body body.number = random() ... 


छोटे रेखांकन के लिए, यह आसान है। मामले में जब ग्राफ में कई दर्जन ऑब्जेक्ट होते हैं और कई पैरामीटर होते हैं - यह बहुत महंगा हो जाता है। इसे सुविधाजनक बनाने के लिए, ObjectBuilders निर्माण प्रदान करते हैं।

निर्माणों


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

वर्ग संग्रह (टाइपट्विल्ड, संख्या = 1)

प्रकार TypeToBuild की वस्तुओं का एक संग्रह। Builder.build () को कॉल करने के बाद, यह निर्माण प्रकार typeToBuild की वस्तुओं की सूची में बदल जाता है। डिफ़ॉल्ट रूप से, वस्तुओं की संख्या = 1

क्लास यूनिक (typeToBuild)

Builder.build () कॉल करने के बाद यह typeToBuild प्रकार की एक अनूठी वस्तु में बदल जाता है। इस अर्थ में अद्वितीय है कि भले ही हमारे ग्राफ में कहीं न कहीं typeToBuild का कोई ऑब्जेक्ट पहले से ही है, फिर भी हम एक नया जेनरेट करेंगे।

वर्ग पुन: उपयोग (typeToBuild, स्थानीय = गलत, कुंजियाँ = [])

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

वर्ग शायद (निर्माण)

यह डिज़ाइन बताता है कि किसी अन्य ऑब्जेक्ट (और क्रमशः यह ऑब्जेक्ट) के साथ संचार मौजूद हो सकता है, या शायद नहीं। डिफ़ॉल्ट रूप से, जब Builder.build() । यह Enabled() संशोधक द्वारा सक्षम है, जिसे नीचे वर्णित किया जाएगा।

कक्षा रैंडम (प्रारंभ = 1, अंत = 100500, पैटर्न = कोई नहीं)

build() स्टेज पर यह निर्माण या तो शुरू से अंत तक एक यादृच्छिक इंट में परिवर्तित हो जाता है, या, यदि एक पैटर्न निर्दिष्ट किया जाता है, एक स्ट्रिंग के लिए। पैटर्न में एक मार्कर% d होना चाहिए, जिसके स्थान पर प्रारंभ से अंत तक एक यादृच्छिक संख्या प्रतिस्थापित की जाएगी।

क्लास अपलिंक ()

आपको दोनों दिशाओं में ऑब्जेक्ट के कनेक्शन को कॉन्फ़िगर करने की अनुमति देता है, उदाहरण के लिए: foo.bar.foo = foo।

आइए कंस्ट्रक्शंस का उपयोग करके हमारे मॉडल को फिर से लिखने की कोशिश करें ताकि इसमें एक दूसरे के साथ वस्तुओं के संबंध के बारे में जानकारी हो।
 # class Chassis: type = CHASSIS_LIGHT **engine = Unique(Engine)** **body = Unique(Body)** **wheels = Collection(Wheel, number=4)** **transmission = Reused(Transmission)** # class Engine: type = ENGINE_PETROL volume = 1.6 **transmission = Reused(Transmission)** # class Wheel: radius = 15 type = WHEEL_STAMPED **transmission = Reused(Transmission)** # class Body: type = BODY_SEDAN number = **Random()** #      .         . spoiler = **Maybe(Unique(Spoiler))** #     . - . #  class Transmission: type = TRANSMISSION_MANUAL # class Spoiler: foo = None 


परिणामस्वरूप, हमने वर्णन किया कि:


अब इस सब से क्या लेना-देना? अब हम एक कॉल में एक पूर्ण कार पा सकते हैं! आधार के लिए सही है।

 car = Builder(Chassis).build() 


वस्तु प्रकार चेसिस है, इसलिए:

 >>>car.engine.volume 1.6 >>>car.wheels[0].radius 15 >>>car.body.spoiler None 


अच्छी तरह से और इतने पर।

यही है, इसके अलावा, वास्तव में, प्रकार चेसिस की एक वस्तु, प्रकार चेसिस की एक वस्तु से जुड़ी सभी वस्तुओं को बनाया जाएगा: इंजन, व्हील एक्स 4, बॉडी, ट्रांसमिशन। Spoiler ऑब्जेक्ट नहीं बनाया जाएगा, क्योंकि डिफ़ॉल्ट रूप से Construct हो सकता है () ऑब्जेक्ट नहीं बनाता है, लेकिन किसी में भी कनवर्ट नहीं किया जाता है।

सामान्य तौर पर, हम अपनी किसी भी कक्षा को बिल्डर (typeToBuild) से गुजरते हुए किसी भी हिस्से (हमारे ग्राफ के ऊपर) से एक कार को इकट्ठा करना शुरू कर सकते हैं।
लेकिन चूंकि हमारे ग्राफ में किनारों का निर्देशन किया गया है, एक ग्राफ का निर्माण केवल उन शीर्षों के साथ किया जाएगा जो प्रारंभिक एक से पहुंचा जा सकता है।

वर्तमान कार्यान्वयन में, चेसिस शीर्ष से आप सभी चोटियों तक पहुंच सकते हैं, केवल इंजन शीर्ष से ट्रांसमिशन तक, और बॉडी टॉप से ​​केवल स्पॉयलर तक।

उदाहरण के लिए, यह है:

 >>>engine = Builder(Engine).build() >>>engine.trasmission.type == TRANSMISSION_MANUAL True >>>engine.transmission.engine AttributeError: Transmission instance has no attribute 'engine' 

इस स्थिति में, केवल इन दो वस्तुओं का निर्माण किया जाएगा।

किसी भी हिस्से से एक पूर्ण कार को इकट्ठा करना शुरू करने में सक्षम होने के लिए, हमारे संचार द्वि-दिशात्मक को पास करना आवश्यक है।
 # class Chassis: type = CHASSIS_LIGHT engine = Unique(Engine) body = Unique(Body) wheels = Collection(Wheel, number=4) transmission = Reused(Transmission) # class Engine: type = ENGINE_PETROL volume = 1.6 transmission = Reused(Transmission) **chassis = Uplink()** **Engine.chassis.linksTo(Chassis, Chassis.engine)** # class Wheel: radius = 15 type = WHEEL_STAMPED transmission = Reused(Transmission) **chassis = Uplink()** **Wheel.chassis.linksTo(Chassis, Chassis.wheels)** # class Body: type = BODY_SEDAN number = Random() #      .         . spoiler = Maybe(Unique(Spoiler)) #     . - . **chassis = Uplink()** **Body.chassis.linksTo(Chassis, Chassis.body)** #  class Transmission: type = TRANSMISSION_MANUAL **chassis = Uplink()** **engine = Uplink()** **Transmission.chassis.linksTo(Chassis, Chassis.transmission)** **Transmission.engine.linksTo(Engine, Engine.transmission)** # class Spoiler: foo = None **body = Uplink()** **Spoiler.body.linksTo(Body, Body.spoiler)** 


अब जब बिल्डर (typeToBuild) .build () बुलाते हैं तो हम किसी भी हिस्से से शुरू होने वाली कार को इकट्ठा कर सकते हैं! इस मामले में:
 >>>engine = Builder(Engine).build() >>>engine.transmission.chassis.engine == engine True >>>engine.transmission.chassis.wheels[0].transmission.engine == engine True 

बिल्डर, कनेक्शन (Uplinks सहित) से गुजर रहा है, सभी आवश्यक वस्तुओं का निर्माण करेगा, जो मॉडल के उपयोग को सरल करता है।
उदाहरण के लिए, यदि आप किसी प्रकार के इंजन का उपयोग करते हैं, तो आपके लिए बिल्डर (इंजन) का उपयोग करना अधिक सुविधाजनक है, लेकिन एक ही समय में सभी आवश्यक वस्तुओं को बनाया और एक-दूसरे से जोड़ा जाएगा।

इसलिए, हमने देखा कि किस तरह आप हमारे परीक्षण प्रणाली के ऑब्जेक्ट मॉडल का आसानी से वर्णन कर सकते हैं, इसके साथ पूरी कार बनाई गई है। लेकिन यह कार अभी भी बेस में है।

संशोधक


ObjectBuilders पुस्तकालय में हमारी वस्तुओं के ग्राफ के मूल विन्यास को संशोधित करने के लिए, कई प्रकार के संशोधक प्रदान किए जाते हैं। जैसा कि ऊपर उल्लेख किया गया है, संशोधक दो प्रकारों में विभाजित हैं: InstanceModifier और ConstructModifier।

सभी संशोधक और उनकी क्षमताओं की सूची पर विचार करें (उदाहरणों का उपयोग करके उनके काम का प्रदर्शन किया जाएगा)।

वर्ग InstanceModifier (classToRunOn)

वास्तव में पहला और एकमात्र (अब तक) InstanceModifier level संशोधक है। आपको वस्तुओं के क्षेत्र के मूल्यों को बदलने या उन पर कुछ क्रियाएं करने की अनुमति देता है। जब Builder.build() को कॉल किया जा रहा है, तो हमारे ऑब्जेक्ट्स ग्राफ में टाइप करें क्लासटूनऑन के प्रत्येक ऑब्जेक्ट पर निष्पादित किया जाएगा।

इसकी दो विधियाँ हैं।

कक्षा सक्षम (क्या)

ConstructModifier level संशोधक। शायद आपको एक सक्रिय स्थिति में रखने की अनुमति देता है ताकि जब आप Builder.build () कहते हैं, तो यह एक ऑब्जेक्ट बनाना शुरू कर देता है। क्या - शायद निर्माण। उदाहरण के लिए, एक कार के बारे में हमारे उदाहरण में Body.spoiler।

वर्ग दिया (निर्माण, मूल्य)

ConstructModifier level संशोधक। किसी भी निर्माण (क्या) को एक विशिष्ट वस्तु या मूल्य (मूल्य) से बदलने के लिए एक ग्राफ बनाने के चरण में अनुमति देता है।

वर्ग होने (क्या, * उदाहरण)

ConstructModifier level संशोधक। इसका उपयोग संग्रह निर्माण (क्या) पर किया जाता है, जिससे आप उन्हें विशिष्ट ऑब्जेक्ट्स (* इंस्टेंस) जोड़ सकते हैं।
या, यदि * इंस्टेंस तत्वों में से कोई एक अंतर है, तो इस इंट के मान से संग्रह का आकार बढ़ जाएगा। इस स्थिति में, संग्रह डिज़ाइन द्वारा उत्पन्न वस्तुओं की संख्या उन वस्तुओं की संख्या से घट जाएगी जिन्हें हमने स्पष्ट रूप से जोड़ा है।

वर्ग संख्या (क्या, राशि)

ConstructModifier level संशोधक। इसका उपयोग संग्रह निर्माणों (क्या) पर किया जाता है, जिससे आप राशि द्वारा उनका आकार बदल सकते हैं।

कक्षा वनऑफ (क्या, * संशोधक)

ConstructModifier level संशोधक। इसका उपयोग संग्रह निर्माण (क्या) पर किया जाता है, जिससे आप संग्रह में किसी एक वस्तु पर संशोधक * संशोधक का एक सेट लागू कर सकते हैं। दरअसल, फिलहाल - यह उपलब्ध संशोधक का पूरा सेट है। वे आपको हमारे ग्राफ के मूल विन्यास के लगभग किसी भी संशोधन का आसानी से वर्णन करने की अनुमति देते हैं।

बिल्डर.विथ (* संशोधक) पद्धति को कॉल करके विधानसभा स्तर पर वस्तुओं के हमारे ग्राफ पर संशोधक लागू होते हैं।

उदाहरणों के साथ संशोधक के काम पर विचार करें।

उदाहरण 1. हम बिगाड़ने वाली कार चाहते हैं!
 spoiler_option = Enabled(Body.spoiler) car = Builder(Chassis).withA(spoiler_option).build() >>>car.body.spoiler is not None True 


उदाहरण 2. हम छह लीटर डीजल इंजन वाली कार चाहते हैं!
 big_diesel = InstanceModifier(Engine).thatSets(type=ENGINE_DIESEL, volume=6.0) car = Builder(Chassis).withA(big_diesel).build() >>>car.engine.volume == 6.0 True >>>car.engine.type == ENGINE_DIESEL True 


वही InstanceModifier.thatDoes () का उपयोग करके किया जा सकता है:
 def make_big_engine(engine): engine.type = ENGINE_DIESEL engine.volume = 6.0 big_diesel = InstanceModifier(Engine).thatDoes(make_big_engine) 

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

उदाहरण 3. हम 6 पहिये और एक भारी प्लेटफार्म चाहते हैं।
 six_wheeled_heavy_chassis = [NumberOf(Chassis.wheels, 6), InstanceModifier(Chassis).thatSets(Chassis.type=CHASSIS_HEAVY)] car = Builder(Chassis).withA(six_wheeled_heavy_chassis).build() >>>len(car.chassis.wheels) == 6 True >>>car.chassis.type == CHASSIS_HEAVY True 


यह इस तथ्य पर ध्यान देने योग्य है कि Builder.withA () विधि एक संशोधक ऑब्जेक्ट, ऐसी वस्तुओं की एक सरणी, या किसी भी गहराई के नेस्टेड सरणियों के साथ एक सरणी के रूप में स्वीकार कर सकती है।
उदाहरण 4 हम एक बड़े शरीर और पहियों के एक समूह के साथ एक शक्तिशाली ऑल-टेरेन वाहन चाहते हैं।
 rover_capabilities = [big_diesel] + \ six_wheeled_heavy_chassis + \ [InstanceModifier(Body).thatSets(type=BODY_HEAVY)] rover = Builder(Chassis).withA(rover_capabilities).build() 


प्रमुख बिंदु यह है कि हमने पहले से लिखे गए big_diesel और छह_wheeled_heavy_chassis संशोधक का पुन: उपयोग किया। तो वास्तविक जीवन में - आप जितने अधिक परीक्षण लिखेंगे, आपके पास उतने अधिक तैयार किए गए संशोधक होंगे। इसके तीन फायदे हैं:
  1. आप संशोधक का पुन: उपयोग करने और परीक्षण को तेजी से लिखने में सक्षम होंगे, यह सोचने के लिए कि परीक्षण कैसे किया जाए, और आवश्यक डेटा कैसे तैयार किया जाए
  2. सही संशोधक नाम यह समझना आसान बनाते हैं कि परीक्षण में किस डेटा का उपयोग किया गया है और यह कैसे कॉन्फ़िगर किया गया है
  3. सिस्टम कॉन्फ़िगरेशन में परिवर्तन के साथ (उदाहरण के लिए, डेवलपर्स ने एक ऐसी सुविधा बनाई जिसमें वस्तुओं के बीच संबंध बदल जाते हैं) आप आसानी से एक ही स्थान पर सभी परीक्षणों की मरम्मत कर सकते हैं, केवल उन संशोधक को ठीक कर सकते हैं जो गलत हो गए हैं!

उदाहरण 5. हम यह जांचना चाहते हैं कि यदि एक पहिए का त्रिज्या 14 ", एक 16", और अन्य 4 - 15 "है तो ऑल-टेरेन वाहन कैसे जाएगा।
 def wheel_radius(radius): return OneOf(Chassis.wheels, InstanceModifier(Wheel).thatSets(radius=radius)) car = Builder(Chassis).withA(rover_capabilities) .withA(wheel_radius(14), wheel_radius(16)) .build() 


उदाहरण 6. हमारे पास अंतिम 2 अप्रयुक्त प्रकार के संशोधक हैं: होने और दिए गए। वे केवल उस होने में भिन्न होते हैं जिसका उपयोग संग्रह निर्माण पर किया जाता है, और अन्य सभी निर्माणों को उनके स्थान पर तैयार वस्तु को रखने के लिए दिया जाता है।

उदाहरण के लिए केवल होने पर विचार करें। मान लीजिए कि हमारे पास पिछली कार से पहले से ही एक पहिया बचा है और हम इसे एक नए पर रखना चाहते हैं।

 wheel = Wheel() car = Builder(Chassis).withA(HavingIn(Chassis.wheels, wheel)).build() 


दिया हुआ संशोधक इसी तरह काम करता है।

लेकिन: आपको इन संशोधकों का उपयोग करने में सावधानी बरतने की आवश्यकता है, क्योंकि वहां समाप्त वस्तु को पारित करने के बाद, इसमें कोई कनेक्शन नहीं बनाया जाएगा या कुछ भी बदल जाएगा, इसे ए-ग्राफ में प्रतिस्थापित किया जाएगा।

Builder(typeToBuild).withA(*modifiers).build() को कॉल करने के लिए एल्गोरिथ्म पर विचार करें।
  1. प्रकार typeToBuild वर्तमान ऑब्जेक्ट typeToBuild
  2. यदि निर्माण संशोधक * संशोधक सूची में हैं, तो उन्हें वर्तमान वस्तु के गुणों में संबंधित प्रकार की वस्तुओं के निर्माण के लिए लागू किया जाता है।
  3. प्रत्येक व्यक्ति के निर्माण के नियमों के अनुसार, वर्तमान वस्तु के गुणों में सभी निर्माण वस्तुएं वस्तुओं में परिवर्तित हो जाती हैं
  4. यदि InstanceModifier(currentObjectType).thatSets() * संशोधक सूची में है, तो वे वर्तमान ऑब्जेक्ट पर लागू होते हैं
  5. हम पुनरावर्ती रूप से परिवर्तित कंस्ट्रक्शंस से प्राप्त सभी ऑब्जेक्ट्स पर जाते हैं, और चरण 2 से एल्गोरिथ्म को दोहराते हैं
  6. यदि InstanceModifier(clazz).thatDoes() * संशोधक सूची में है, तो वे निर्मित ग्राफ़ में क्लैज़ प्रकार की वस्तुओं पर लागू होते हैं


वह सब है! इस सरल पुस्तकालय की मदद से हम अपने जीवन को बहुत सरल बनाने में सक्षम थे और बहुत सारे मानव-कार्यों को बचा सकते थे।

नीचे पंक्ति: हमारे पास क्या है और यह सब क्यों आवश्यक है?


  1. ObjectBuilders आसानी से और आसानी से वस्तु मॉडल द्वारा वर्णित डेटा के गुणों का वर्णन करने और गतिशील रूप से इस डेटा को उत्पन्न करने की क्षमता प्रदान करता है।
  2. वस्तुओं और उनके संशोधनों के बीच संबंधों के तर्क को एक अलग तार्किक स्तर पर आवंटित किया जाता है, जो आपको पहले वर्णित डेटा कॉन्फ़िगरेशन का पुन: उपयोग करने की अनुमति देता है और, यदि आवश्यक हो, तो उन सभी स्थानों पर जहां डेटा का उपयोग किया गया था, उनकी मरम्मत के बजाय आसानी से कनेक्शन और डेटा कॉन्फ़िगरेशन के तर्क को सुधारें।
  3. अब परीक्षणों में केवल उन गुणों का वर्णन करना संभव है जो वास्तव में परीक्षण के व्यवहार को प्रभावित करते हैं, सभी आवश्यक "साइड" सेटिंग्स और ऑब्जेक्ट्स के बारे में चिंता किए बिना, जो डेटा और सिस्टम व्यवहार (परीक्षण में परीक्षण) के बीच संबंधों को समझना आसान बनाता है।


हालांकि, कार्यान्वयन के वर्तमान चरण में हमारे दृष्टिकोण में इसकी कमियां हैं:

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

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

उदाहरण के लिए, हमने एक परत लागू की है जो हमें XMLRPC API या SQL कीमिया के माध्यम से सिस्टम में हमारे उत्पन्न डेटा को लोड करने की अनुमति देता है। हम JSON और XML प्रारूपों में सर्वर अनुरोधों के लिए डेटा उत्पन्न करने के लिए ObjectBuilders का भी उपयोग करते हैं। ObjectBuilders के उपयोग की काफी विस्तृत श्रृंखला है, क्योंकि यह डेटा में हेरफेर करने के लिए एक काफी सार तंत्र है। हम वास्तव में आशा करते हैं कि कुछ पाठक इस टूल को रोचक और उपयोगी पाएंगे और इसके विकास में शामिल होंगे। हम हमेशा नए विचारों और सुझावों का स्वागत करते हैं।

गिथब परियोजना कोड।
पिपी में तैयार पैकेज।

स्वास्थ्य पर प्रयोग करें!

Source: https://habr.com/ru/post/In204192/


All Articles