टेस्ट रनर इंटरफेस


टेस्ट रनर का एक ही काम है: एक हल लेना, उसके सारे टेस्ट चलाना और एक मानकीकृत आउटपुट लौटाना। Exercism वेबसाइट के साथ होने वाली सारी बातचीत अपने आप संभाली जाती है और वह इस स्पेक का हिस्सा नहीं है।

चलाना

  • टेस्ट रनर में एक चलाने योग्य स्क्रिप्ट होनी चाहिए। इस बारे में अधिक जानकारी आप docker.md फाइल में पा सकते हैं।
  • स्क्रिप्ट को तीन पैरामीटर मिलेंगे:
    • अभ्यास का स्लग (जैसे two-fer).
    • इनपुट डायरेक्टरी का पाथ (अंत में स्लैश सहित), जिसमें जमा किए गए हल की फाइलें और अभ्यास की बाकी फाइलें होंगी। इस डायरेक्टरी को रीड-ओनली माना जाना चाहिए। तकनीकी रूप से इसमें लिखा जा सकता है, लेकिन अस्थायी फाइलों के लिए (जैसे सोर्स कंपाइल करने के लिए) /tmp इस्तेमाल करना बेहतर है।
    • आउटपुट डायरेक्टरी का पाथ (अंत में स्लैश सहित)। इस डायरेक्टरी में लिखा जा सकता है।
  • स्क्रिप्ट को आउटपुट डायरेक्टरी में एक results.json फाइल लिखनी होगी।
  • अगर रनर सफलतापूर्वक चल चुका है, तो उसे एग्ज़िट कोड 0 के साथ बाहर निकलना चाहिए, चाहे टेस्ट का स्टेटस कुछ भी हो।

चलने का अनुमत समय

हर हल के लिए टेस्ट रनर को 20 सेकंड की अवधि के लिए 100% CPU और 3GB मेमोरी मिलती है। 20 सेकंड बाद प्रोसेस को रोक दिया जाता है और टाइम-आउट की सूचना दी जाती है।

Note

टाइम-आउट की संभावना कम करने के लिए हम प्रदर्शन संबंधी सर्वोत्तम प्रथाओं वाले दस्तावेज़ का पालन करने की पूरी सलाह देते हैं।

आउटपुट का प्रारूप

results.json फाइलों में ये फील्ड समर्थित हैं:

टॉप लेवल

वर्ज़न

कुंजी: version, प्रकार: number, उपस्थिति: आवश्यक

वर्ज़न: 1, 2, 3

यह फाइल जिस स्पेक वर्ज़न का पालन करती है:

  • 1: उन ट्रैकों के लिए जिनके टेस्ट रनर अलग-अलग टेस्ट की जानकारी नहीं दे सकते।
  • 2: उन ट्रैकों के लिए जिनके टेस्ट रनर अलग-अलग टेस्ट की जानकारी दे सकते हैं। कॉन्सेप्ट अभ्यास वाले ट्रैकों के लिए यह कम से कम आवश्यक वर्ज़न है।
  • 3: उन ट्रैकों के लिए जिनके टेस्ट रनर अलग-अलग टेस्ट को किसी टास्क से जोड़ सकते हैं।

स्टेटस

कुंजी: status, प्रकार: string, उपस्थिति: आवश्यक

वर्ज़न: 1, 2, 3

ये समग्र स्टेटस मान्य हैं:

  • pass: सारे टेस्ट पास हुए
  • fail: कम से कम एक टेस्ट का स्टेटस fail या error है
  • error: कोई भी टेस्ट नहीं चला (आमतौर पर इसका मतलब कंपाइल एरर या सिंटैक्स एरर है)

error स्टेटस का उपयोग केवल तब करना चाहिए जब सारे टेस्ट में एरर हुआ हो। कंपाइल होने वाली भाषाओं में यह आमतौर पर कोड के कंपाइल न हो पाने का नतीजा होता है। इंटरप्रेट होने वाली भाषाओं में यह रनटाइम एरर होती है, जैसे कोई सिंटैक्स एरर जिसकी वजह से फाइल पार्स नहीं हो पाती।

संदेश

कुंजी: message, प्रकार: string, उपस्थिति: आवश्यक यदि status = error, या जब status = fail और version = 1

वर्ज़न: 1, 2, 3

जब स्टेटस error हो (यानी कोई भी टेस्ट ठीक से नहीं चला), तब टॉप लेवल की message कुंजी देनी चाहिए। इसमें उपयोगकर्ता को हुई एरर बतानी चाहिए। चूँकि अपनी समस्या का पता लगाने के लिए उपयोगकर्ता को यही एकमात्र जानकारी मिलेगी, इसलिए यह जितनी साफ हो सके उतनी साफ होनी चाहिए:

  • पाथ को सरल बनाइए और <solution-dir>/relative/path जैसा कुछ लिखिए, /full/path/to जैसा नहीं, क्योंकि उसमें ECR से जुड़ी बेकार जानकारी शामिल होगी
  • जहाँ संभव या उपयुक्त हो, उपयोगकर्ता के कोड के बाहर के स्टैक छोटे कर दीजिए
  • संदर्भ (यानी एरर संदेश) के बिना कॉल स्टैक कभी न दिखाइए
  • एरर संदेश को (जहाँ संभव हो) न बदलिए, क्योंकि इससे एरर खोजना आसान रहता है

Ruby में सिंटैक्स एरर होने पर हम रनटाइम एरर और स्टैक ट्रेस देते हैं। कंपाइल होने वाली भाषाओं में कंपाइलेशन एरर दी जानी चाहिए।

टॉप लेवल message वैल्यू अधिकतम 65535 अक्षरों तक सीमित है। अगर वैल्यू में मल्टीबाइट अक्षर हों तो असल अधिकतम लंबाई इससे कम रहती है।

जब स्टेटस error न हो, तो या तो वैल्यू को null रखिए या कुंजी को पूरी तरह छोड़ दीजिए।

टेस्ट

कुंजी: tests, प्रकार: array, उपस्थिति: आवश्यक यदि status = fail या status = pass

वर्ज़न: 2, 3

यह टेस्ट नतीजों का ऐरे है, जिसका ब्यौरा नीचे "हर टेस्ट" वाले हिस्से में दिया गया है।

टेस्ट अवश्य उसी क्रम में लौटाए जाने चाहिए जिस क्रम में वे टेस्ट फाइल में लिखे हैं। जो भाषाएँ टेस्ट किसी भी क्रम में चलाती हैं, उनमें इसका मतलब हो सकता है कि नतीजों को टेस्ट फाइल में दिए क्रम के मुताबिक फिर से लगाना पड़े।

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

हर टेस्ट

नाम

कुंजी: name, प्रकार: string, उपस्थिति: आवश्यक

वर्ज़न: 2, 3

यह टेस्ट का नाम है, ऐसे रूप में जिसे इंसान पढ़ और समझ सके।

टेस्ट कोड

कुंजी: test_code, प्रकार: string, उपस्थिति: आवश्यक यदि अभ्यास कॉन्सेप्ट अभ्यास हो

वर्ज़न: 2, 3

कॉन्सेप्ट अभ्यासों में यह अवश्य मौजूद होना चाहिए और प्रैक्टिस अभ्यासों में यह मौजूद होना चाहिए। इस अंतर की वजह यह है कि कॉन्सेप्ट अभ्यासों में छात्रों को टेस्ट नहीं दिखाए जाते, इसलिए test_code दिखाए बिना अभ्यास हल करना नामुमकिन हो सकता है, जबकि प्रैक्टिस अभ्यासों में टेस्ट दिखाए जाते हैं।

यह उस कमांड का मुख्य हिस्सा है जिसकी जाँच हो रही है। उदाहरण के लिए, नीचे दिया गया Ruby टेस्ट:

def test_duplicate_items_uniqs_list
  cart = ShoppingCart.new
  cart.add(:STARIC)
  cart.add(:MEDNEW)
  cart.add(:MEDNEW)
  assert_equal 'Newspaper, Rice', cart.items_list
end

से test_code की यह वैल्यू लौटनी चाहिए:

"cart = ShoppingCart.new
cart.add(:STARIC)
cart.add(:MEDNEW)
cart.add(:MEDNEW)
assert_equal 'Newspaper, Rice', cart.items_list"

(JSON को मान्य बनाने के लिए लाइनब्रेक की जगह \n रखा गया है)।

स्टेटस

कुंजी: status, प्रकार: string, उपस्थिति: आवश्यक

वर्ज़न: 2, 3

हर टेस्ट के लिए ये स्टेटस मान्य हैं:

  • pass: टेस्ट पास हुआ
  • fail: टेस्ट विफल हुआ
  • error: टेस्ट में एरर आया, यानी उसने कोई वैल्यू नहीं लौटाई

संदेश

कुंजी: message, प्रकार: string, उपस्थिति: आवश्यक यदि status fail या error हो

वर्ज़न: 2, 3

हर टेस्ट की message कुंजी का उपयोग ऐसे टेस्ट का नतीजा लौटाने के लिए होता है जिसका status fail या error हो। यह जितनी इंसानी पढ़ने लायक हो सके, उतनी होनी चाहिए। यहाँ जो कुछ लिखा जाएगा, वह छात्र को तब दिखाया जाएगा जब उसका टेस्ट पास नहीं होगा। अगर टेस्ट विफलता का संदेश या एरर संदेश न हो, तो या तो वैल्यू null रखिए या कुंजी पूरी तरह छोड़ दीजिए। यहाँ टेस्ट सूट का आउटपुट देना भी मान्य है। message वैल्यू की लंबाई पर कोई सीमा नहीं है।

आउटपुट

कुंजी: output, प्रकार: string, उपस्थिति: वैकल्पिक

वर्ज़न: 2, 3

हर टेस्ट की output कुंजी का उपयोग उस सब कुछ को संग्रहीत करने और दिखाने के लिए करना चाहिए जो उपयोगकर्ता किसी टेस्ट के लिए जानबूझकर आउटपुट करता है।

  • इसे हर उस टेस्ट नतीजे के साथ जोड़ा जाना चाहिए जिसमें उपयोगकर्ता का आउटपुट हो।
  • सिर्फ वही सामग्री दिखनी चाहिए जो उपयोगकर्ता ने खुद आउटपुट की हो, टेस्ट रनर का अपने आप दिया आउटपुट नहीं।
  • आप या तो सामान्य तरीकों से आउटपुट हुई सामग्री पकड़ सकते हैं (जैसे Ruby में puts, Python में print या C# में Debug.WriteLine), या ऐसा मेथड दे सकते हैं जिसका उपयोग उपयोगकर्ता कर सके (जैसे Ruby टेस्ट रनर उपयोगकर्ता को एक ऐसा debug मेथड देता है जो हर जगह उपलब्ध है और जिसकी विशेषताएँ मानक puts मेथड जैसी ही हैं)।
  • आउटपुट अवश्य 500 अक्षरों तक सीमित होना चाहिए। इस स्थिति में या तो "Output was truncated. Please limit to 500 chars" संदेश के साथ आउटपुट काट देना, या एरर लौटाना, दोनों मान्य हैं।

टास्क ID

कुंजी: task_id, प्रकार: number, उपस्थिति: वैकल्पिक

वर्ज़न: 3

टेस्ट को टास्क की ID के ज़रिए किसी खास टास्क से जोड़िए; यह ID टास्क के शीर्षक की शुरुआत में इस्तेमाल हुई संख्या होती है। टेस्ट को किसी टास्क से तभी जोड़िए जब उसे ठीक एक टास्क से जोड़ा जा सकता हो।

फिलहाल सिर्फ कॉन्सेप्ट अभ्यासों में ही ऐसे सुस्पष्ट टास्क होते हैं जिनसे आप टेस्ट जोड़ सकते हैं, लेकिन आगे यह बदल सकता है।

उदाहरण के लिए, नीचे दी गई instructions.md फाइल देखिए:

# Instructions

You're going to write some code to help Lucian cook an exquisite lasagna from his favorite cook book.

## 1. Define the expected oven time in minutes

...

## 2. Calculate the remaining oven time in minutes

...

इन निर्देशों में दो टास्क तय किए गए हैं:

  1. मिनटों में ओवन का अपेक्षित समय तय कीजिए
  2. मिनटों में बचा हुआ ओवन समय निकालिए

तब results.json फाइल में ऐसी एंट्री हो सकती है:

{
  "name": "Expected oven time in minutes",
  "status": "pass",
  "task_id": 1,
  "test_code": "Assert.Equal(40, Lasagna.ExpectedMinutesInOven());"
}

अब यह टेस्ट पहले टास्क से जुड़ा है: "मिनटों में ओवन का अपेक्षित समय तय कीजिए"। ध्यान दीजिए कि नाम का टास्क के विवरण से मेल खाना ज़रूरी नहीं है।

ट्रैक इसे कई तरह से लागू कर सकते हैं:

  • टेस्ट फाइल के भीतर टेस्ट में मेटाडेटा जोड़िए (जैसे एट्रिब्यूट, एनोटेशन या कमेंट के ज़रिए) और टेस्ट चलाते समय टेस्ट रनर से यह मेटाडेटा पढ़वाइए।
  • टेस्ट नाम और टास्क ID की मैपिंग किसी अलग फाइल में रखिए (जैसे अभ्यास की .meta/config.json फाइल) और इस जानकारी को बनी हुई results.json फाइल में मिला दीजिए।

उदाहरण

अलग-अलग वर्ज़न के लिए एक मान्य results.json फाइल कैसी दिख सकती है, इसके उदाहरण ये हैं:

v1 का उदाहरण

{
  "version": 1,
  "status": "fail",
  "message": "Failed: test_answer\nExpected: 42, actual: 3"
}

v2 का उदाहरण

{
  "version": 2,
  "status": "fail",
  "message": null,
  "tests": [
    {
      "name": "Test that the thing works",
      "status": "fail",
      "message": "Expected 42 but got 123123",
      "output": "Debugging information output by the user",
      "test_code": "assert_equal 42, answerToTheUltimateQuestion()"
    }
  ]
}

v3 का उदाहरण

{
  "version": 3,
  "status": "fail",
  "message": null,
  "tests": [
    {
      "name": "Test that the thing works",
      "status": "fail",
      "message": "Expected 42 but got 123123",
      "output": "Debugging information output by the user",
      "test_code": "assert_equal 42, answerToTheUltimateQuestion()",
      "task_id": 1
    }
  ]
}

UI/UX से जुड़ी बातें

टेस्ट विफल होने पर

जब छात्र का हल किसी टेस्ट में विफल हो, तो कुछ ऐसा दिखना चाहिए:

Test Code:
  <test_code>

Test Result:
  <message>

टेस्ट पास होने पर

जब हल किसी टेस्ट को पास करे, तो कुछ ऐसा दिखना चाहिए:

Test Code:
  <test_code>

अपनी भाषा के टेस्ट सूट के लिए मेटाडेटा कैसे जोड़ें

हर रास्ता रोम की ओर जाता है और इस तक पहुँचने का कोई तय तरीका नहीं है। अब तक कई तरीके अपनाए गए हैं:

  • हाथ से बनाई गई सहायक JSON फाइलें, जिन्हें टेस्ट चलने के दौरान टेस्ट नतीजों के साथ मिलाया जाता है।
  • टेस्ट सूट का अपने आप किया गया स्टैटिक विश्लेषण, जिसे टेस्ट चलने के दौरान टेस्ट नतीजों के साथ मिलाया जाता है।
    • यह AST विश्लेषण या टेक्स्ट पार्सिंग के ज़रिए किया जा सकता है