টেস্ট জেনারেটর


টেস্ট জেনারেটর হলো ট্র্যাক-ভিত্তিক একটি সফটওয়্যার, যা স্বয়ংক্রিয়ভাবে একটি প্র্যাকটিস অনুশীলনীর টেস্ট তৈরি করে। এটি অনুশীলনীর JSON টেস্ট কেসগুলোকে ট্র্যাকের ভাষার টেস্টে রূপান্তর করে এই কাজটি করে।

সুবিধা

একটি টেস্ট জেনারেটর থাকার কিছু সুবিধা হলো:

  1. দ্রুত নতুন অনুশীলনী যোগ করা যায়
  2. অনুশীলনী যোগ করার "একঘেয়ে" অংশগুলো স্বয়ংক্রিয় হয়ে যায়
  3. সর্বশেষ ক্যানোনিকাল ডেটার সাথে টেস্ট সিঙ্ক করা সহজ

ব্যবহারের ক্ষেত্র

সাধারণভাবে, একটি টেস্ট জেনারেটর চালানো হয় দুটি কাজের যেকোনো একটির জন্য:

  1. একটি নতুন অনুশীলনীর টেস্ট তৈরি করা
  2. একটি বিদ্যমান অনুশীলনীর টেস্ট হালনাগাদ করা

নতুন অনুশীলনীর টেস্ট তৈরি

নতুন অনুশীলনীর জন্য একটি টেস্ট জেনারেটর যোগ করলে তার টেস্ট ফাইল(গুলো) তৈরি করা যায়। টেস্ট জেনারেটরটি আগেই তৈরি করা থাকলে, নতুন অনুশীলনীর টেস্ট তৈরি করা শূন্য থেকে লেখার চেয়ে (অনেক) কম পরিশ্রমের কাজ হবে।

বিদ্যমান অনুশীলনীর টেস্ট হালনাগাদ

একটি অনুশীলনীর টেস্ট জেনারেটর হয়ে গেলে আপনি সেটি আবার চালিয়ে অনুশীলনীটিকে তার সর্বশেষ ক্যানোনিকাল ডেটার সাথে হালনাগাদ/সিঙ্ক করতে পারেন। নিয়মিত এটি করার পরামর্শ দিই, যাতে দেখা যায় হালনাগাদ করা দরকার এমন সমস্যাযুক্ত টেস্ট কেস আছে কি না, বা আপনি যুক্ত করতে চান এমন নতুন টেস্ট আছে কি না।

শুরুর বিন্দু

কোনো অনুশীলনীর জন্য একটি টেস্ট জেনারেটর বাস্তবায়ন করার সময় দুটি সম্ভাব্য শুরুর বিন্দু থাকতে পারে:

  1. অনুশীলনীটি নতুন, তাই এর কোনো টেস্ট নেই
  2. অনুশীলনীটি আগে থেকেই আছে, তাই এর বিদ্যমান টেস্ট আছে
Caution

বিদ্যমান টেস্ট থাকলে, টেস্ট জেনারেটরটি এমনভাবে বাস্তবায়ন করুন যাতে এটি যে টেস্টগুলো তৈরি করে সেগুলো বিদ্যমান সলিউশনগুলো নষ্ট না করে।

ডিজাইন

মোটামুটি বলতে গেলে, টেস্ট ফাইল তৈরি করা হয় দুটি উপায়ে:

  • কোড: টেস্ট ফাইলগুলো (বেশিরভাগ) কোডের মাধ্যমে তৈরি হয়
  • টেমপ্লেট: টেস্ট ফাইলগুলো (বেশিরভাগ) টেমপ্লেট ব্যবহার করে তৈরি হয়

আমাদের অভিজ্ঞতায়, কোড-ভিত্তিক পদ্ধতিতে টেস্ট জেনারেটরের কোড বেশ জটিল হয়ে যায়, অন্যদিকে টেমপ্লেট-ভিত্তিক পদ্ধতি সরল হয়।

আমরা নিম্নলিখিত ধারাবাহিকতাটি সুপারিশ করি:

  1. অনুশীলনীর ক্যানোনিকাল ডেটা পড়ুন
  2. অনুশীলনীর tests.toml ফাইল-এ include = false হিসেবে চিহ্নিত টেস্ট কেসগুলো বাদ দিন
  3. অনুশীলনীর ক্যানোনিকাল ডেটাকে টেমপ্লেটে ব্যবহারযোগ্য একটি ফরম্যাটে রূপান্তর করুন
  4. অনুশীলনীর ক্যানোনিকাল ডেটা একটি অনুশীলনী-নির্দিষ্ট টেমপ্লেটে পাঠান

এই ব্যবস্থার মূল সুবিধা হলো প্রতিটি অনুশীলনীর নিজস্ব টেমপ্লেট থাকে, যা:

  • টেস্ট ফাইলগুলো কীভাবে তৈরি হয় তা স্পষ্ট করে
  • এগুলো ডিবাগ করা সহজ করে
  • অন্য কোনো অনুশীলনী নষ্ট করার ঝুঁকি ছাড়াই এগুলো সম্পাদনা করা নিরাপদ করে তোলে
Caution

টেস্ট জেনারেটর ডিজাইন করার সময় চেষ্টা করুন:

  • টেস্ট জেনারেটরের ভেতরে ক্যানোনিকাল ডেটার প্রি-প্রসেসিং যতটা সম্ভব কমান
  • টেমপ্লেটগুলোর মধ্যে কাপলিং কমান

বাস্তবায়ন

টেস্ট জেনারেটর সাধারণত (বেশিরভাগ ক্ষেত্রে) ট্র্যাকের ভাষায় লেখা হয়।

Caution

অন্য ভাষা ব্যবহার করতে আপনি স্বাধীন, তবে প্রতিটি অতিরিক্ত ভাষা ট্র্যাক মেইনটেইন করা বা এতে কন্ট্রিবিউট করা আরও কঠিন করে তোলে। তাই আমরা যেখানে সম্ভব ট্র্যাকের ভাষা ব্যবহার করার পরামর্শ দিই, কারণ এতে মেইনটেইন বা কন্ট্রিবিউট করা সহজ হয়।

ফরম্যাটিং

আপনার ট্র্যাকের কোড ফরম্যাট করার টুল থাকলে, টেমপ্লেট রেন্ডার করার পরে একটি পোস্ট-প্রসেসিং ধাপ হিসেবে এটি চালানোর কথা ভাবতে পারেন।

ক্যানোনিকাল ডেটা

টেস্ট জেনারেটর যে মূল ডেটা নিয়ে কাজ করে তা হলো একটি অনুশীলনীর canonical-data.json ফাইল। এই ফাইলটি exercism/problem-specifications রিপোতে সংজ্ঞায়িত, যা Exercism-এর অনেক অনুশীলনীর জন্য শেয়ার করা মেটাডেটা নির্ধারণ করে।

Caution

সব অনুশীলনীর canonical-data.json ফাইল থাকে না! যেগুলোতে থাকে না, সেগুলোর টেস্ট আপনাকে নিজ হাতে তৈরি করতে হবে, কারণ টেস্ট জেনারেটর কাজ করার জন্য কোনো ডেটা থাকবে না।

গঠন

ক্যানোনিকাল ডেটা একটি JSON অবজেক্টে সংজ্ঞায়িত। এই অবজেক্টে একটি "cases" ফিল্ড থাকে, যাতে টেস্ট কেসগুলো থাকে। এই টেস্ট কেসগুলো (সাধারণত) আপনার ট্র্যাকের টেস্টের সাথে এক-এক করে মিলে যায়।

প্রতিটি টেস্ট কেসে কয়েকটি প্রপার্টি থাকে, যার মধ্যে description, property, input মান এবং expected মান সবচেয়ে গুরুত্বপূর্ণ। এখানে leap অনুশীলনীর canonical-data.json ফাইলের একটি (আংশিক) উদাহরণ দেওয়া হলো:

{
  "exercise": "leap",
  "cases": [
    {
      "uuid": "6466b30d-519c-438e-935d-388224ab5223",
      "description": "year not divisible by 4 in common year",
      "property": "leapYear",
      "input": {
        "year": 2015
      },
      "expected": false
    },
    {
      "uuid": "4fe9b84c-8e65-489e-970b-856d60b8b78e",
      "description": "year divisible by 4, not divisible by 100 in leap year",
      "property": "leapYear",
      "input": {
        "year": 1996
      },
      "expected": true
    }
  ]
}

টেস্ট জেনারেটরের মূল দায়িত্ব এই JSON ডেটাকে ট্র্যাক-নির্দিষ্ট টেস্টে রূপান্তর করা। উপরের JSON-টি কীভাবে Nim টেস্ট কোডে রূপান্তরিত হতে পারে, তা এখানে দেখানো হলো:

import unittest
import leap

suite "Leap":
  test "year not divisible by 4 in common year":
    check isLeapYear(2015) == false

  test "year divisible by 4, not divisible by 100 in leap year":
    check isLeapYear(1996) == true

canonical-data.json ফাইলের গঠন ভালোভাবে নথিভুক্ত এবং এর একটি JSON স্কিমা সংজ্ঞাও আছে।

নেস্টিং

কিছু অনুশীলনী তাদের ক্যানোনিকাল ডেটায় নেস্টিং ব্যবহার করে। এর অর্থ হলো cases অ্যারের প্রতিটি এলিমেন্ট হয়তো:

  1. একটি সাধারণ টেস্ট কেস (কোনো চাইল্ড টেস্ট কেস নেই)
  2. টেস্ট কেসের একটি গ্রুপ (এক বা একাধিক চাইল্ড টেস্ট কেস)
Note

কোনো এলিমেন্টের ধরন শনাক্ত করতে পারেন সেই এলিমেন্ট-ধরনের জন্য একান্তভাবে থাকা ফিল্ডগুলো আছে কি না দেখে। সম্ভবত এটির সবচেয়ে ভালো উপায় হলো "cases" কী (key) ব্যবহার করা, যা শুধু টেস্ট কেস গ্রুপেই থাকে।

নেস্টেড টেস্ট কেসের একটি উদাহরণ এখানে:

{
  "cases": [
    {
      "uuid": "e9c93a78-c536-4750-a336-94583d23fafa",
      "description": "data is retained",
      "property": "data",
      "input": {
        "treeData": ["4"]
      },
      "expected": {
        "data": "4",
        "left": null,
        "right": null
      }
    },
    {
      "description": "insert data at proper node",
      "cases": [
        {
          "uuid": "7a95c9e8-69f6-476a-b0c4-4170cb3f7c91",
          "description": "smaller number at left node",
          "property": "data",
          "input": {
            "treeData": ["4", "2"]
          },
          "expected": {
            "data": "4",
            "left": {
              "data": "2",
              "left": null,
              "right": null
            },
            "right": null
          }
        }
      ]
    }
  ]
}
Caution

আপনার ট্র্যাক যদি গ্রুপ করা টেস্ট সমর্থন না করে, তাহলে আপনাকে করতে হবে:

  • cases হায়ারার্কি ট্রাভার্স/ফ্ল্যাটেন করা, যাতে শেষে শুধু সবচেয়ে ভেতরের (লিফ) টেস্ট কেসগুলো থাকে
  • অভিন্ন একটি টেস্ট নাম তৈরি করতে টেস্ট কেসের description-এর সাথে তার প্যারেন্ট description(গুলো) মিলিয়ে নেওয়া

ইনপুট ও expected মান

input ও expected টেস্ট কেস কী (key)গুলোর বিষয়বস্তু ব্যাপকভাবে ভিন্ন হয়। বেশিরভাগ ক্ষেত্রে এগুলো স্কেলার মান (যেমন সংখ্যা, বুলিয়ান বা স্ট্রিং) বা সরল অবজেক্ট হয়। তবে মাঝেমধ্যে আরও জটিল মানও পাবেন, যেগুলোর জন্য সম্ভবত একটু প্রি-প্রসেসিং লাগবে, যেমন সিউডো কোডে ল্যাম্বডা, শিক্ষার্থীদের কোডের ওপর চালানোর জন্য অপারেশনের তালিকা, ইত্যাদি।

সিনারিও

টেস্ট কেসে একটি ঐচ্ছিক scenarios ফিল্ড থাকে। এই ফিল্ডটি টেস্ট জেনারেটর নির্দিষ্ট কিছু টেস্ট কেসকে বিশেষভাবে সামলাতে ব্যবহার করতে পারে। সবচেয়ে কমন ব্যবহারের ক্ষেত্র হলো কিছু ধরনের টেস্ট বাদ দেওয়া, যেমন "unicode" সিনারিওর টেস্ট, কারণ আপনার ট্র্যাকের ভাষা হয়তো Unicode সমর্থন করে না।

সিনারিওগুলোর সম্পূর্ণ তালিকা এখানে পাওয়া যাবে।

canonical-data.json ফাইল পড়া

canonical-data.json ফাইলগুলো পড়ার কয়েকটি উপায় আছে:

  1. সরাসরি problem-specifications রিপো থেকে এগুলো আনা (যেমন https://raw.githubusercontent.com/exercism/problem-specifications/main/exercises/leap/canonical-data.json)।
  2. problem-specifications রিপোকে ট্র্যাক রিপোতে একটি Git সাবমডিউল হিসেবে যোগ করা।
  3. configlet ক্যাশ থেকে এগুলো পড়া। অবস্থানটি ব্যবহারকারীর সিস্টেমের ওপর নির্ভর করে, তবে প্রোগ্রামের মাধ্যমে অবস্থানটি বের করতে আপনি configlet info -o -v d | head -1 | cut -d " " -f 5 ব্যবহার করতে পারেন।

ট্র্যাক-নির্দিষ্ট টেস্ট কেস

আপনার ট্র্যাক যদি কিছু অতিরিক্ত, ট্র্যাক-নির্দিষ্ট টেস্ট কেস যোগ করতে চায় (যা ক্যানোনিকাল ডেটায় পাওয়া যায় না), তার একটি উপায় হলো একটি additional-test-cases.json ফাইল তৈরি করা, যা টেস্ট জেনারেটর পরে রেন্ডারিংয়ের জন্য টেমপ্লেটে পাঠানোর আগে canonical-data.json ফাইলের সাথে মার্জ করতে পারে।

টেমপ্লেট

ব্যবহার করার টেমপ্লেট ইঞ্জিন সম্ভবত ট্র্যাক-নির্দিষ্ট হবে। আদর্শভাবে আপনি চাইবেন আপনার টেমপ্লেটগুলো যতটা সম্ভব সরল হোক, তাই কোড ডুপ্লিকেশন ইত্যাদি নিয়ে চিন্তা করবেন না।

টেমপ্লেটগুলো তাদের ডেটা পায় টেস্ট জেনারেটরের কাছ থেকে, যা সেগুলো রেন্ডার করার জন্য ডেটার ওপর ইটারেশন করে।

Note

টেমপ্লেটগুলো সরল রাখতে সাহায্য করার জন্য টেস্ট জেনারেটরের দিকে একটু প্রি-প্রসেসিং করা কাজে লাগতে পারে, অথবা আপনার টেমপ্লেট যেসব এক্সটেনশন ব্যবস্থা সমর্থন করে সেগুলো, যেমন কিছু "ফিল্টার", সংজ্ঞায়িত করা যেতে পারে।

configlet ব্যবহার

configlet হলো ট্র্যাক মেইনটেইন করার প্রধান টুল, যা ব্যবহার করা যায়:

  • নতুন অনুশীলনীর জন্য অনুশীলনী ফাইলগুলো তৈরি করতে: bin/configlet create --practice-exercise <slug> চালান
  • বিদ্যমান অনুশীলনীর tests.toml ফাইল সিঙ্ক করতে: bin/configlet sync --tests --update --exercise <slug> চালান
  • অনুশীলনীর ক্যানোনিকাল ডেটা ডিস্কে আনতে (এটি উপরের যেকোনো কমান্ডের একটি সাইড-ইফেক্ট)

এ কারণে configlet টেস্ট জেনারেটরের সাথে মিলিয়ে ব্যবহার করার জন্য দারুণ একটি টুল, যা সত্যিই শক্তিশালী কিছু ওয়ার্কফ্লো তৈরি করতে পারে।

কমান্ড-লাইন ইন্টারফেস

আপনি চাইবেন টেস্ট জেনারেটর ব্যবহার করা যেন সহজ এবং শক্তিশালী হয়। এর জন্য আমরা এক বা একাধিক স্ক্রিপ্ট ফাইল তৈরির পরামর্শ দিই।

Note

আপনার ট্র্যাকের জন্য যে স্ক্রিপ্ট ফাইল ফরম্যাট সবচেয়ে ভালো মানায়, তা বেছে নিতে আপনি স্বাধীন। শেল স্ক্রিপ্ট ও PowerShell স্ক্রিপ্ট কমন এমন দুটি অপশন, যেগুলো দুটোই ভালো কাজ করে।

দ্রুত একটি নতুন অনুশীলনী স্ক্যাফোল্ড করতে configlet ও একটি টেস্ট জেনারেটর একসাথে ব্যবহার করা একটি শেল স্ক্রিপ্টের উদাহরণ এখানে:

bin/fetch-configlet
bin/configlet create --practice-exercise <slug>
path/to/test-generator <slug>

শূন্য থেকে তৈরি করা

টেস্ট জেনারেটর তৈরি শুরু করার আগে, অন্য ট্র্যাকগুলো কীভাবে এগুলো বাস্তবায়ন করেছে তার একটি ধারণা পেতে আমরা কয়েকটি বিদ্যমান টেস্ট জেনারেটর দেখে নেওয়ার পরামর্শ দিই:

কোনো প্রশ্ন থাকলে তা জিজ্ঞাসা করার সেরা জায়গা হলো ফোরাম। Rust নিয়ে এবং JavaScript টেস্ট জেনারেটর নিয়ে হওয়া ফোরাম আলোচনাগুলোও সহায়ক হতে পারে।

মিনিমাম ভায়েবল প্রোডাক্ট

আমরা একটি মিনিমাম ভায়েবল প্রোডাক্ট দিয়ে শুরু করে ধাপে ধাপে টেস্ট জেনারেটর তৈরির পরামর্শ দিই। একেবারে ন্যূনতম একটি সংস্করণ একটি অনুশীলনীর canonical-data.json পড়বে আর সেই ডেটা সরাসরি টেমপ্লেটে পাঠাবে।

শুরুতে একটি মাত্র অনুশীলনীতে মনোযোগ দিন, বরং leap-এর মতো সরল একটি অনুশীলনীতে। সেটি কাজ করতে শুরু করলেই ধীরে ধীরে আরও অনুশীলনী যোগ করুন।

আর টেস্ট জেনারেটরকে যতটা সরল রাখা যায়, ততটাই সরল রাখার চেষ্টা করুন।

Note

আদর্শভাবে একজন কন্ট্রিবিউটর টেস্ট জেনারেটর ভেতরে কীভাবে কাজ করে তা না বুঝেই একটি বিদ্যমান টেমপ্লেট পেস্ট/সংশোধন করতে পারবেন।

ব্যবহার বা কন্ট্রিবিউশন

টেস্ট জেনারেটর কীভাবে ব্যবহার করবেন বা এতে কন্ট্রিবিউট করবেন তা ট্র্যাক-নির্দিষ্ট। নির্দেশনা খুঁজুন ট্র্যাকের README.md, CONTRIBUTING.md অথবা টেস্ট জেনারেটর কোডের ডিরেক্টরিতে।