कैसे करें टेस्ट ड्रिवेन डेवलपमेंट


15

मुझे एप्लीकेशन डेवलपमेंट में सिर्फ 2+ साल का अनुभव है। उन दो वर्षों में विकास के प्रति मेरा दृष्टिकोण निम्नलिखित था

  1. आवश्यकताओं का विश्लेषण करें
  2. पहचान कोर घटक / ऑब्जेक्ट्स, आवश्यक कार्य, व्यवहार, प्रक्रिया और उनके अवरोध
  3. कक्षाएं बनाएं, उनके बीच संबंध, वस्तुओं के व्यवहार और राज्यों पर बाधाएं
  4. कार्यों को बनाएं, आवश्यकताओं के अनुसार व्यवहार संबंधी बाधाओं के साथ प्रक्रिया करें
  5. मैन्युअल रूप से आवेदन का परीक्षण करें
  6. यदि आवश्यकता परिवर्तन घटक / कार्यों को संशोधित करते हैं, तो मैन्युअल रूप से आवेदन का परीक्षण करें

हाल ही में मैंने TDD से परिचय प्राप्त किया और महसूस किया कि यह विकास करने का बहुत अच्छा तरीका है क्योंकि विकसित कोड के मौजूद होने का मजबूत कारण है और बहुत से पोस्ट परिनियोजन समस्याएँ कम हो जाती हैं।

लेकिन मेरी समस्या यह है कि मैं पहले परीक्षण बनाने में सक्षम नहीं हूं, बल्कि मैं घटकों की पहचान कर रहा हूं और वास्तव में घटक लिखने से पहले मैं उनके लिए परीक्षण लिख रहा हूं। मेरा सवाल यह है कि

  1. क्या मैं कर रहा हूं यह सही है? अगर नहीं तो मुझे क्या बदलना है
  2. क्या कोई तरीका है जिससे आप पहचान सकते हैं कि आपके द्वारा लिखा गया परीक्षण पर्याप्त है?
  3. क्या यह बहुत ही सरल कार्यक्षमता के लिए टेस्ट लिखने के लिए अच्छा अभ्यास है जो 1 + 1 = 2 के बराबर हो सकता है या यह सिर्फ एक ओवरप्ले है?
  4. क्या कार्यक्षमता में परिवर्तन करना अच्छा है और यदि आवश्यकता बदलती है तो तदनुसार परीक्षण करें?

2
"मैं घटकों की पहचान कर रहा हूं और सिर्फ उनके लिए परीक्षण लिख रहा हूं इससे पहले कि मैं वास्तव में घटक लिखूं।": मुझे यह सही लगता है: आप पहले अपने सिस्टम की मोटे वास्तुकला की पहचान करें और फिर कोडिंग शुरू करें। कोडिंग (टीडीडी) के दौरान आप अलग-अलग घटकों के विवरणों पर काम करते हैं और संभवतः अपनी वास्तुकला के साथ समस्याओं की खोज करते हैं जिसे आप रास्ते में ठीक कर सकते हैं। लेकिन मुझे यह ठीक लगता है कि आप बिना किसी पूर्व विश्लेषण के कोडिंग शुरू नहीं करते हैं।
जियोर्जियो

आप टीडीडी किए बिना भी स्वचालित इकाई / एकीकरण परीक्षण कर सकते हैं। दोनों अक्सर भ्रमित होते हैं, लेकिन वे एक ही चीज नहीं हैं।
एंड्रेस एफ।

जवाबों:


19

क्या मैं कर रहा हूं यह सही है? अगर नहीं तो मुझे क्या बदलना है

यह केवल उस संक्षिप्त विवरण से कहना कठिन है, लेकिन मुझे संदेह है कि, नहीं, आप इसे सही नहीं कर रहे हैं । नोट: मैं यह नहीं कह रहा हूं कि आप जो कर रहे हैं वह काम नहीं कर रहा है या किसी तरह खराब है, लेकिन आप टीडीडी नहीं कर रहे हैं। मध्य "डी" का अर्थ है "प्रेरित", परीक्षण सब कुछ ड्राइव करते हैं, विकास प्रक्रिया, कोड, डिजाइन, वास्तुकला, सब कुछ

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

इससे कोई फर्क नहीं पड़ता कि आप वास्तव में कोड को लिखते हैं, या क्या यह पूरी तरह से समाप्त हो गया है: आप अपने सिर में कोड (कंकाल) कोड लिख रहे हैं, फिर उस कोड के लिए परीक्षण लिख रहे हैं। वह टीडीडी नहीं है।

उस आदत को छोड़ना कठिन है । वास्तव में, वास्तव में कठिन है। यह अनुभवी प्रोग्रामरों के लिए विशेष रूप से कठिन लगता है।

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

नियम हैं:

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

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

जब लोगों का एक समूह छद्म-टीडीडी का उपयोग करके एक टिक टीएसी को पैर की अंगुली के खेल की तरह कुछ लागू करता है, तो वे आमतौर पर Board3 × 3 सरणी के साथ वर्ग के कुछ प्रकार को शामिल करने वाले समान डिजाइनों के साथ समाप्त होते हैं Integer। और कम से कम प्रोग्रामर्स के एक हिस्से ने वास्तव में इस वर्ग को इसके लिए परीक्षण के बिना लिखा होगा क्योंकि वे "जानते हैं कि उन्हें इसकी आवश्यकता है" या "उनके खिलाफ परीक्षण लिखने के लिए कुछ चाहिए"। हालाँकि, जब आप उस समूह को टीडीडी के रूप में लागू करने के लिए बाध्य करते हैं यदि आप इसका मतलब रखते हैं, तो वे अक्सर बहुत अलग डिजाइनों की एक विस्तृत विविधता के साथ समाप्त हो जाएंगे, अक्सर दूर के समान भी कुछ भी नियोजित नहीं करते हैं Board

क्या कोई तरीका है जिससे आप पहचान सकते हैं कि आपके द्वारा लिखा गया परीक्षण पर्याप्त है?

जब वे सभी व्यावसायिक आवश्यकताओं को कवर करते हैं। टेस्ट सिस्टम आवश्यकताओं की एक एन्कोडिंग हैं।

क्या यह बहुत ही सरल कार्यक्षमता के लिए टेस्ट लिखने के लिए अच्छा अभ्यास है जो 1 + 1 = 2 के बराबर हो सकता है या यह सिर्फ एक ओवरप्ले है?

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

क्या कार्यक्षमता में परिवर्तन करना अच्छा है और यदि आवश्यकता बदलती है तो तदनुसार परीक्षण करें?

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

ऐसा करना कठिन है। आपको एक बिंदु पर पहुंचने के लिए किसी तरह की "मांसपेशियों की स्मृति" के निर्माण के लिए दर्जनों, शायद सैकड़ों घंटे के जानबूझकर अभ्यास की आवश्यकता होती है , जहां समय सीमा समाप्त हो जाती है और आप दबाव में होते हैं, आपको इसके बारे में सोचने की ज़रूरत नहीं है , और यह काम करने का सबसे तेज़ और सबसे स्वाभाविक तरीका बन जाता है ।


1
एक बहुत स्पष्ट जवाब वास्तव में! व्यावहारिक दृष्टिकोण से, एक लचीला और शक्तिशाली परीक्षण रूपरेखा टीडीडी का अभ्यास करते समय बहुत सुखद है। TDD से स्वतंत्र रहते हुए, परीक्षणों को स्वचालित रूप से चलाने की क्षमता एक अनुप्रयोग को डीबग करने के लिए अमूल्य है। टीडीडी के साथ आरंभ करने के लिए, गैर-अंतःक्रियात्मक कार्यक्रम (यूनिक्स-शैली) संभवतः सबसे आसान हैं, क्योंकि एक उपयोग-मामले को बाहर निकलने की स्थिति और कार्यक्रम के आउटपुट की तुलना करके उम्मीद की जा सकती है। इस दृष्टिकोण का एक ठोस उदाहरण OCaml के लिए मेरी गैसोलीन लाइब्रेरी में पाया जा सकता है ।
माइकल ले बारबियर ग्रुएनवाल्ड 13

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

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

1
@AndresF .: वास्तव में, आपके द्वारा लिंक किया गया ब्लॉग पोस्ट कीथ को किए गए अनुभवों को प्रतिध्वनित करता प्रतीत होता है जब टीडीडी के रूप में यदि आप इसका अर्थ करते हैं: जब टिक-टैक-टो के लिए "छद्म-टीडीडी" करते हैं, तो वे एक Boardवर्ग बनाने की शुरुआत करते हैं। 3x3 की सरणी int(या ऐसा कुछ)। जबकि, यदि आप उन्हें TDDAIYMI करने के लिए मजबूर करते हैं, तो वे अक्सर डोमेन ज्ञान पर कब्जा करने के लिए एक मिनी-डीएसएल बनाते हैं। यह सिर्फ एक किस्सा है। एक सांख्यिकीय और वैज्ञानिक रूप से ध्वनि का अध्ययन अच्छा होगा, लेकिन जैसा कि अक्सर इस तरह के अध्ययनों के साथ होता है, वे या तो बहुत छोटे या वैसे भी बहुत महंगा होते हैं।
डब्ल्यू

@ JörgWMittag मुझे ठीक करें अगर मैंने आपको गलत समझा, लेकिन क्या आप कह रहे हैं कि रॉन जेफ्रीस "छद्म-टीडीडी" कर रहे थे? क्या यह "नहीं सच स्कॉट्समैन" की एक रूप है? (मैं अधिक वैज्ञानिक अध्ययन की आवश्यकता के बारे में आपसे सहमत हूं; मैं जिस ब्लॉग से जुड़ा हूं वह टीडीडी उपयोग की एक विशिष्ट आवृत्ति की शानदार विफलता के बारे में सिर्फ एक रंगीन किस्सा है। दुर्भाग्य से, ऐसा लगता है जैसे टीडीडी इंजीलवादी बाकी के लिए बहुत जोर से हैं। हमें इस मेथोडोली और इसके कथित लाभों का वास्तविक विश्लेषण करना है)।
एंड्रेस एफ।

5

आप अपने विकास के दृष्टिकोण को "टॉप-डाउन-ओनली" प्रक्रिया के रूप में वर्णित करते हैं - आप एक उच्च अमूर्त स्तर से शुरू करते हैं और अधिक से अधिक विवरण में जाते हैं। टीडीडी, कम से कम रूप में यह लोकप्रिय है, एक "बॉटम-अप" तकनीक है। और किसी ऐसे व्यक्ति के लिए जो ज्यादातर "टॉप-डाउन" काम कर रहा है, यह वास्तव में "नीचे-ऊपर" काम करने के लिए बहुत ही असामान्य हो सकता है।

तो, आप अपनी विकास प्रक्रिया में अधिक "टीडीडी" कैसे ला सकते हैं? पहले, मुझे लगता है कि आपकी वास्तविक विकास प्रक्रिया हमेशा "टॉप-डाउन" नहीं होती है जैसा कि आपने ऊपर वर्णित किया है। चरण 2 के बाद, आपने संभवतः कुछ घटकों की पहचान की होगी जो अन्य घटकों से स्वतंत्र हैं। कभी-कभी आप उन घटकों को पहले लागू करने का निर्णय लेते हैं। उन घटकों के सार्वजनिक एपीआई का विवरण संभवतः आपकी आवश्यकताओं का पालन नहीं करता है, विवरण आपके डिजाइन निर्णयों का भी पालन करते हैं। यह वह बिंदु है जहां आप टीडीडी के साथ शुरू कर सकते हैं: कल्पना करें कि आप घटक का उपयोग कैसे करने जा रहे हैं, और आप वास्तव में एपीआई का उपयोग कैसे करेंगे। और जब आप परीक्षण के रूप में इस तरह के एपीआई उपयोग को कोड करना शुरू करते हैं, तो आपने सिर्फ टीडीडी के साथ शुरुआत की।

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


3

क्या मैं कर रहा हूं यह सही है? अगर नहीं तो मुझे क्या बदलना है

आप ठीक कर रहे हैं।

क्या कोई तरीका है जिससे आप पहचान सकते हैं कि आपके द्वारा लिखा गया परीक्षण पर्याप्त है?

हां, परीक्षण / कोड कवरेज टूल का उपयोग करें । मार्टिन फाउलर टेस्ट कवरेज पर कुछ अच्छी सलाह देते हैं।

क्या यह बहुत ही सरल कार्यक्षमता के लिए टेस्ट लिखने के लिए अच्छा अभ्यास है जो 1 + 1 = 2 के बराबर हो सकता है या यह सिर्फ एक ओवरप्ले है?

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

क्या कार्यक्षमता में परिवर्तन करना अच्छा है और यदि आवश्यकता बदलती है तो तदनुसार परीक्षण करें?

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


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

3

पहले परीक्षण लिखना सॉफ्टवेयर लिखने के लिए एक पूरी तरह से अलग दृष्टिकोण है। टेस्ट न केवल उचित कोड कार्यक्षमता सत्यापन (वे सभी पास) का एक उपकरण है, बल्कि बल जो डिजाइन को परिभाषित करता है। जबकि परीक्षण कवरेज एक उपयोगी मीट्रिक हो सकता है, यह अपने आप में लक्ष्य नहीं होना चाहिए - TDD का लक्ष्य कोड कवरेज का एक अच्छा% प्राप्त करना नहीं है, बल्कि इसे लिखने से पहले अपने कोड की परीक्षणशीलता के बारे में सोचना है।

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

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

क्या कोई तरीका है जिससे आप पहचान सकते हैं कि आपके द्वारा लिखा गया परीक्षण पर्याप्त है?

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

क्या यह बहुत ही सरल कार्यक्षमता के लिए टेस्ट लिखने के लिए अच्छा अभ्यास है जो 1 + 1 = 2 के बराबर हो सकता है या यह सिर्फ एक ओवरप्ले है?

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

क्या कार्यक्षमता में परिवर्तन करना अच्छा है और यदि आवश्यकता बदलती है तो तदनुसार परीक्षण करें?

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

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