Go REST হ্যান্ডলার পরীক্ষা: httptest ও টেবিল-চালিত টেস্ট
httptest ও টেবিল-চালিত কেস দিয়ে Go REST হ্যান্ডলার টেস্ট করলে আপনি auth, ভ্যালিডেশন, স্ট্যাটাস কোড এবং এজ-কেসগুলি রিলিজের আগে পুনরাবৃত্তিপূর্ণভাবে যাচাই করতে পারবেন।

রিলিজের আগে কোন ব্যাপারে নিশ্চিত থাকা উচিত
একটি REST হ্যান্ডলার কম্পাইল হতে পারে, দ্রুত ম্যানুয়াল চেক পাস করতে পারে, তারপরও প্রোডকশনে ব্যর্থ হতে পারে। অধিকাংশ ব্যর্থতা সিনট্যাক্সের সমস্যা নয়। এগুলো কনট্র্যাক্টের সমস্যা: হ্যান্ডলার সেই ইনপুট গ্রহণ করে যা উচিত নয়, ভুল স্ট্যাটাস কোড ফেরত দেয়, বা এরোরে অতিরিক্ত ডিটেইল ফাঁস করে।
ম্যানুয়াল টেস্টিং সাহায্য করে, কিন্তু এজ কেস এবং রিগ্রেশন মিস করা সহজ। আপনি আনন্দদায়ক পথটি (happy path) পরীক্ষা করেন, সম্ভবত একটি স্পষ্ট এরর পরীক্ষা করেন, এবং চলে যান। পরে ভ্যালিডেশন বা মিডলওয়্যারের ছোট একটা পরিবর্তন চুপিচুপি এমন ব্যবহার ভাঙতে পারে যা আপনি স্থির ধরে নিয়েছিলেন।
হ্যান্ডলার টেস্টের লক্ষ্য সহজ: হ্যান্ডলার যে প্রতিশ্রুতি দেয় তা পুনরাবৃত্তিপূর্ণ করা। এতে রয়েছে authentication নিয়ম, ইনপুট ভ্যালিডেশন, পূর্বানুমানযোগ্য স্ট্যাটাস কোড, এবং ক্লায়েন্ট নির্ভরযোগ্য করতে পারে এমন এরর বডি।
Go-র httptest প্যাকেজ এই কাজের জন্য খুব উপযোগী কারণ আপনি একটি বাস্তব সার্ভার চালু না করে সরাসরি হ্যান্ডলার পরীক্ষা করতে পারেন। আপনি একটি HTTP রিকোয়েস্ট বানাবেন, সেটি হ্যান্ডলারে পাঠাবেন, এবং রেসপন্স বডি, হেডার, ও স্ট্যাটাস কোড পরীক্ষা করবেন। টেস্ট দ্রুত, বিচ্ছিন্ন, এবং প্রতিটি কমিটে চালানোর জন্য সহজ থাকে।
রিলিজের আগে আপনার উচিত জানা (ভরসা নয়):
- auth আচরণ কনসিস্টেন্ট কি না: মিসিং টোকেন, অবৈধ টোকেন, এবং ভুল রোলের ক্ষেত্রে।
- ইনপুটগুলি যাচাই করা হচ্ছে: আবশ্যক ফিল্ড, টাইপ, রেঞ্জ, এবং (যদি আপনি প্রয়োগ করেন) অচেনা ফিল্ড।
- স্ট্যাটাস কোড কনট্র্যাক্টের সাথে মিলছে কি (উদাহরণ: 401 বনাম 403, 400 বনাম 422)।
- এরর রেসপন্স নিরাপদ এবং কনসিস্টেন্ট (কোনো স্ট্যাক ট্রেস নেই, প্রতিবার একই শেইপ)।
- নন-হ্যাপি পাথ হ্যান্ডেল হচ্ছে: টাইমআউট, ডাউনস্ট্রিম ব্যর্থতা, এবং খালি ফলাফল।
"টিকিট তৈরি" এন্ডপয়েন্টটি perfect JSON পাঠালে ঠিক কাজ করতে পারে যখন আপনি অ্যাডমিন হিসেবে পাঠাচ্ছেন। টেস্টগুলো ধরবে যা আপনি ভুলে যাচ্ছেন পরীক্ষা করতে: একটি এক্সপায়ার্ড টোকেন, ক্লায়েন্টের অজান্তেই পাঠানো অতিরিক্ত ফিল্ড, নেগেটিভ প্রায়োরিটি, অথবা ডিপেন্ডেন্সি ফেল হওয়ার সময় "not found" বনাম "internal error" এর পার্থক্য।
প্রতিটি এন্ডপয়েন্টের জন্য কনট্র্যাক্ট নির্ধারণ করুন
টেস্ট লেখার আগে লিখে ফেলুন হ্যান্ডলার কী প্রতিশ্রুতি দিচ্ছে। একটি পরিষ্কার কনট্র্যাক্ট টেস্টগুলোকে ফোকাসেড রাখে এবং তা অনুমানের পরীক্ষা থেকে বিরত রাখে যে কোড "কী বোঝাতে চেয়েছিল"। এটি রিফ্যাক্টরকে নিরাপদ করে কারণ আপনি ইন্টারনাল বদলাতে পারেন কিন্তু আচরণ বদলানো ছাড়াও।
ইনপুট দিয়ে শুরু করুন। প্রতিটি মান কোথা থেকে আসে এবং কি প্রয়োজন সেসব সম্পর্কে নির্দিষ্ট হন। একটি এন্ডপয়েন্টে id পাথ থেকে, limit কুয়েরি স্ট্রিং থেকে, Authorization হেডার থেকে, এবং একটি JSON বডি থাকতে পারে। মানসমূহের ফরম্যাট, মিন/ম্যাক্স, আবশ্যক ফিল্ড, এবং কিছু মিসিং হলে কী হবে—এসব নোট করুন।
তারপর আউটপুট নির্ধারণ করুন। শুধুই "JSON রিটার্ন করে" বলেই শেষ করবেন না। ঠিক করুন সফলতা কেমন দেখায়, কোন হেডার গুরুত্বপূর্ণ, এবং এরর কেমন আকারে আসবে। ক্লায়েন্ট যদি স্থির এরর কোড ও JSON শেইপের ওপর নির্ভর করে, সেটাকেও কনট্র্যাক্টের অংশ ধরুন।
একটি ব্যবহারিক চেকলিস্ট:
- ইনপুট: পাথ/কুয়েরি মান, আবশ্যক হেডার, JSON ফিল্ড, এবং ভ্যালিডেশন রুল
- আউটপুট: স্ট্যাটাস কোড, রেসপন্স হেডার, সাফল্য ও এররের জন্য JSON শেইপ
- সাইড-ইফেক্ট: কোন ডাটা পরিবর্তিত হচ্ছে এবং কী তৈরি হচ্ছে
- ডিপেন্ডেন্সি: ডাটাবেস কল, এক্সটার্নাল সার্ভিস, বর্তমান সময়, জেনারেটেড ID
এও নির্ধারণ করুন কোথায় হ্যান্ডলার টেস্ট শেষ হয়। হ্যান্ডলার টেস্ট সবচেয়ে শক্ত HTTP বাউন্ডারিতে: auth, পার্সিং, ভ্যালিডেশন, স্ট্যাটাস কোড, ও এরর বডি। গভীরে যাওয়া বিষয়গুলোকে ইন্টিগ্রেশন টেস্টে রাখুন: রিয়েল ডাটাবেস কুয়েরি, নেটওয়ার্ক কল, এবং ফুল রাউটিং।
যদি আপনার ব্যাকেন্ড জেনারেটেড হয় (উদাহরণ: AppMaster প্রোডিউস করে Go হ্যান্ডলার ও বিজনেস লজিক), কনট্র্যাক্ট-ফার্স্ট অ্যাপ্রোচ আরও উপকারী। আপনি কোড রিজেনারেট করলেও পাবলিক আচরণ একই আছে কিনা যাচাই করতে পারবেন।
একটি মিনিমাল httptest হারনেস সেটআপ করুন
একটি ভালো হ্যান্ডলার টেস্টটি বাস্তব রিকোয়েস্ট পাঠানোর মতো হওয়া উচিত, তবে সার্ভার চালাতে হবে না। Go-তে সাধারণভাবে অর্থ হচ্ছে: httptest.NewRequest দিয়ে রিকোয়েস্ট বানানো, httptest.NewRecorder দিয়ে রেসপন্স ক্যাপচার করা, এবং হ্যান্ডলার কল করা।
হ্যান্ডলার সরাসরি কল করলে টেস্ট দ্রুত এবং ফোকাসেড থাকে। এটি আদর্শ যখন আপনি হ্যান্ডলারের ভিতরের আচরণ যাচাই করতে চান: auth চেক, ভ্যালিডেশন রুল, স্ট্যাটাস কোড, এবং এরর বডি। যখন কনট্র্যাক্ট পাথ প্যারাম, রাউটিং, বা মিডলওয়্যার অর্ডারের ওপর নির্ভর করে, তখন টেস্টে রাউটার ব্যবহার কাজে লাগে। সরাসরি কল করে শুরু করুন এবং শুধু প্রয়োজনে রাউটার যোগ করুন।
হেডারগুলি অনেক সময় প্রত্যাশার চেয়ে বেশি গুরুত্বপূর্ণ। Content-Type অনুপস্থিত থাকলে হ্যান্ডলার বডি পড়ার ভিন্ন উপায় নিতে পারে। প্রতিটি কেসে প্রত্যাশিত হেডার সেট করুন যাতে ব্যর্থতা লজিক্যাল ভুল না হয়ে টেস্ট সেটআপের ভুল বোঝায়।
এখানে একটি মিনিমাল প্যাটার্ন আছে যা আপনি পুনরায় ব্যবহার করতে পারেন:
req := httptest.NewRequest(http.MethodPost, "/v1/widgets", strings.NewReader(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")
rec := httptest.NewRecorder()
handler.ServeHTTP(rec, req)
res := rec.Result()
defer res.Body.Close()
অ্যাসার্শনগুলো কনসিসটেন্ট রাখতে একটি ছোট হেল্পার ব্যবহার করা সুবিধাজনক, যা রেসপন্স বডি পড়বে এবং ডিকোড করবে। অধিকাংশ টেস্টে প্রথমে স্ট্যাটাস কোড চেক করুন (ফেইল হলে স্ক্যান করা সহজ হয়), তারপর আপনি যে হেডারগুলো প্রতিশ্রুতি দিয়েছেন সেগুলো (প্রায়ই Content-Type), এবং শেষে বডি চেক করুন।
আপনার ব্যাকেন্ড জেনারেটেড হলে (AppMaster দ্বারা তৈরি Go ব্যাকেন্ডসহ), এই হারনেস এখনও প্রযোজ্য। আপনি যে HTTP কনট্র্যাক্ট ব্যবহারকারীরা নির্ভর করে সেটাই টেস্ট করছেন, কোডের স্টাইল নয়।
টেবিল-চালিত কেসগুলো এমনভাবে ডিজাইন করুন যাতে পড়তে সহজ থাকে
টেবিল-চালিত টেস্ট সবচেয়ে ভালো কাজ করে যখন প্রতিটি কেস একটি ছোট গল্পের মতো পড়ে: আপনি কী রিকোয়েস্ট পাঠাচ্ছেন এবং প্রত্যাশা কী। টেবিলটি স্ক্যান করে কভারেজ বোঝা উচিত, ফাইলে ঝাঁপিয়ে না পড়েই।
একটি শক্ত কেস সাধারণত থাকে: স্পষ্ট নাম, রিকোয়েস্ট (মেথড, পাথ, হেডার, বডি), প্রত্যাশিত স্ট্যাটাস কোড, এবং রেসপন্স চেক। JSON বডির ক্ষেত্রে পুরো JSON স্ট্রিং মিলানোর বদলে কয়েকটি স্থিতিশীল ফিল্ড (যেমন এরর কোড) asserted করা ভালো, যদি না আপনার কনট্র্যাক্ট কঠোরভাবে পুরো আউটপুট মিলানোর দাবি করে।
একটি সহজ কেস শেইপ যা আপনি পুনরায় ব্যবহার করতে পারেন
কেস struct-টি ফোকাসেড রাখুন। এক-অফ সেটআপগুলো হেল্পারে রাখুন যাতে টেবিল ছোট থাকে।
type tc struct {
name string
method string
path string
headers map[string]string
body string
wantStatus int
wantBody string // substring or compact JSON
}
বিভিন্ন ইনপুটের জন্য ছোট বডি স্ট্রিং ব্যবহার করুন যাতে পার্থক্য এক নজরে বোঝা যায়: একটি ভ্যালিড পে-লোড, একটি মিসিং ফিল্ড, একটি ভুল টাইপ, এবং একটি খালি স্ট্রিং। টেবিলে জটিল ফরম্যাটিং সঙ্গে JSON তৈরি করা এড়ান — খুব দ্রুত গোলমেলে হয়ে যায়।
যখন আপনি বার-বার একই সেটআপ দেখেন (টোকেন ক্রিয়েশন, সাধারণ হেডার, ডিফল্ট বডি), সেগুলো newRequest(tc) বা baseHeaders() মতো হেল্পারে রেখে দিন।
যদি একটি টেবিলে অনেক আইডিয়া মিশে যায়, সেটি ভাগ করে নিন। সাফল্য-পাথের জন্য একটি টেবিল এবং এরর-পাথের জন্য আরেকটি টেবিল সাধারণত পড়তে এবং ডিবাগ করতে সহজ হয়।
Auth চেক: যে কেসগুলো সাধারণত বাদ পড়ে যায়
Auth টেস্টগুলো প্রায়ই হ্যাপি পাথে ঠিক থাকে, তারপর প্রোডকশনে ব্যর্থ হয় কারণ একটি ছোট কেস কখনো’exercice করা হয়নি। auth-কে কনট্র্যাক্ট হিসেবে বিবেচনা করুন: ক্লায়েন্ট কী পাঠায়, সার্ভার কী রিটার্ন করে, এবং কী কখনো ফাঁস করা যাবে না।
টোকেন উপস্থিতি ও বৈধতা দিয়ে শুরু করুন। একটি প্রটেক্টেড এন্ডপয়েন্ট হেডার অনুপস্থিত হলে আলাদা আচরণ করা উচিত বনাম হেডার আছে কিন্তু ভুল হলে। যদি আপনি শর্ট-লিভড টোকেন ব্যবহার করেন, এক্সপায়ারি টেস্টও যুক্ত করুন — আপনি মক বা ইনজেক্ট করা ভ্যালিডেটর দিয়েও তা সিমুলেট করতে পারেন।
অধিকাংশ গ্যাপ নিচের কেসগুলো কভার করে:
- No
Authorizationheader -> 401 সঙ্গে স্থির এরর রেসপন্স - Malformed header (wrong prefix) -> 401
- Invalid token (bad signature) -> 401
- Expired token -> 401 (অথবা আপনার নির্বাচিত কোড) সঙ্গে পূর্বানুমানযোগ্য মেসেজ
- Valid token কিন্তু ভুল রোল/পারমিশন -> 403
401 বনাম 403 বিভাজন গুরুত্বপূর্ণ। 401 তখন ব্যবহার করুন যখন কলার প্রমাণীকৃত নয়। 403 ব্যবহার করুন যখন তারা প্রমাণীকৃত কিন্তু অনুমোদিত নয়। যদি আপনি এগুলো মিশিয়ে দেন, ক্লায়েন্টরা অনর্থকভাবে রিট্রাই করবে বা ভুল UI দেখাবে।
রোল চেক যথেষ্ট নয় এমন "ইউজার-অউনড" এন্ডপয়েন্টগুলোর ক্ষেত্রে (যেমন GET /orders/{id})। মালিকানাও টেস্ট করুন: ব্যবহারকারী A কখনো ব্যবহারকারী B-র অর্ডার দেখতে পারবে না, এমনকি বৈধ টোকেন থাকলেই না। সেটি পরিষ্কারভাবে 403 হওয়া উচিত (অথবা আপনি যদি অস্তিত্ব লুকাতে চান তবে 404), এবং বডি কোনো তথ্য ফাঁস করা উচিত না। এরর সাধারণ রাখুন — "order belongs to user 42" বলে ইঙ্গিত দেবেন না।
ইনপুট নিয়ম: যাচাই করুন, প্রত্যাখ্যান করুন, এবং স্পষ্টভাবে ব্যাখ্যা করুন
প্রি-রিলিজ বাগগুলো সাধারণত ইনপুট সম্পর্কিত: মিসিং ফিল্ড, ভুল টাইপ, অপ্রত্যাশিত ফরম্যাট, বা অতিরিক্ত বড় পে-লোড।
আপনার হ্যান্ডলার যে প্রতিটি ইনপুট নেয় সেগুলোর নাম দিন: JSON বডি ফিল্ড, কুয়েরি প্যারাম, এবং পাথ প্যারাম। প্রতিটির জন্য সিদ্ধান্ত নিন কী হবে যখন তা মিসিং, খালি, ম্যালফর্মড, বা রেঞ্জের বাইরে। তারপর এমন কেস লিখুন যা প্রমাণ করে হ্যান্ডলার খারাপ ইনপুট দ্রুত প্রত্যাখ্যান করে এবং প্রতিবার একই ধরনের এরর রিটার্ন করে।
একটি ছোট সেট ভ্যালিডেশন কেস সাধারণত ঝুঁকির বেশিরভাগ অংশ কভার করে:
- Required fields: missing vs empty string vs null (যদি আপনি null.Allow করেন)
- Types and formats: number vs string, email/date/UUID ফরম্যাট, boolean পার্সিং
- Size limits: max length, max items, payload too large
- Unknown fields: ignored vs rejected (যদি আপনি strict decoding প্রয়োগ করেন)
- Query and path params: missing, parseable নয়, এবং ডিফল্ট আচরণ
উদাহরণ: একটি POST /users হ্যান্ডলার { "email": "...", "age": 0 } গ্রহণ করে। টেস্ট করুন email মিসিং, email হিসেবে 123, email হিসেবে "not-an-email", age হিসেবে -1, এবং age হিসেবে "20"। যদি আপনি strict JSON চান, তাহলে { "email":"[email protected]", "extra":"x" } টেস্ট করে নিশ্চিত করুন এটি ব্যর্থ হয়।
ভ্যালিডেশন ব্যর্থতা পূর্বানুমানযোগ্য রাখুন। ভ্যালিডেশন এররের জন্য একটি স্ট্যাটাস কোড নির্বাচন করুন (কিছু দল 400 ব্যবহার করে, অন্যরা 422) এবং এরর বডির শেইপ কনসিস্টেন্ট রাখুন। টেস্টগুলো স্ট্যাটাস এবং একটি মেসেজ (অথবা details ফিল্ড) উভয়ই assert করবে যাতে বোঝা যায় কোন ইনপুট ব্যর্থ হয়েছে।
স্ট্যাটাস কোড ও এরর বডি: এগুলো পূর্বানুমানযোগ্য করুন
হ্যান্ডলার টেস্ট সহজ হয় যখন API এররগুলো বিরক্তিকরভাবে সাদামাটা এবং কনসিস্টেন্ট হয়। প্রতিটি এররকে একটি স্পষ্ট স্ট্যাটাস কোডে ম্যাপ করুন এবং প্রতিবার একই JSON শেইপ রিটার্ন করুন, যাই হোক হ্যান্ডলারকে কে লেখে।
একটি ছোট, সম্মত নিষ্পত্তি তালি (mapping) থেকে শুরু করুন:
- 400 Bad Request: malformed JSON, মিসিং আবশ্যক কুয়েরি প্যারাম
- 404 Not Found: রিসোর্স ID পাওয়া যায়নি
- 409 Conflict: unique constraint বা state conflict
- 422 Unprocessable Entity: বৈধ JSON কিন্তু বিজনেস রুল ভেঙে
- 500 Internal Server Error: অপ্রত্যাশিত ব্যর্থতা (DB down, nil pointer, থার্ড-পার্টি আউটেজ)
তারপর এরর বডি কনসিস্টেন্ট রাখুন। মেসেজ টেক্সট পরে বদলালেও ক্লায়েন্টরা নির্ভরযোগ্য ফিল্ড পাবে:
{ "code": "user_not_found", "message": "User was not found", "details": { "id": "123" } }
টেস্টে পুরো শেইপ assert করুন, শুধু স্ট্যাটাস নয়। একটি সাধারণ ভুল হচ্ছে এরর হলে HTML, প্লেইন টেক্সট বা খালি বডি রিটার্ন করা — যা ক্লায়েন্ট ভাঙে এবং বাগ লুকায়।
এরর রেসপন্সের জন্য হেডার ও এনকোডিংও টেস্ট করুন:
Content-Typeহলapplication/json(আপনি যদি charset সেট করেন সেটিও কনসিস্টেন্ট)- বডি ব্যর্থতার সময়ও বৈধ JSON
code,message, এবংdetailsথাকে (details খালি হতে পারে, কিন্তু এলোমেলো নয়)- panic এবং অপ্রত্যাশিত এররগুলো একটি নিরাপদ 500 রিটার্ন করে, স্ট্যাক ট্রেস ফাঁস না করে
যদি আপনি recover middleware যোগ করেন, একটি টেস্ট যোগ করুন যা panic জেনারেট করে এবং নিশ্চিত করে যে আপনি এখনও ক্লিন JSON এরর পাচ্ছেন।
এজ কেস: ব্যর্থতা, সময়, এবং নন-হ্যাপি পাথ
হ্যাপি-পাথ টেস্ট হ্যান্ডলার কাজ করে কিনা প্রমাণ করে। এজ-কেস টেস্ট সেটা প্রমাণ করে যে জগৎ নীরস হলে ও আচরণ ঠিক থাকে।
ডিপেন্ডেন্সিগুলোকে নির্দিষ্ট, পুনরাবৃত্তিযোগ্যভাবে ব্যর্থ করান। যদি আপনার হ্যান্ডলার ডাটাবেস, ক্যাশ, বা এক্সটার্নাল API কল করে, আপনি দেখতে চান ঐ লেয়ারগুলো যখন কন্ট্রোল করা যায় না তখন কী হয়।
প্রতিটি এন্ডপয়েন্টের জন্য অন্তত একবার সিমুলেট করা উচিত:
- ডাউনস্ট্রিম কল থেকে টাইমআউট (
context deadline exceeded) - স্টোরেজ থেকে না পাওয়া (Not found) যখন ক্লায়েন্ট ডেটা আশা করে
- তৈরি করার সময় unique constraint violation (duplicate email, duplicate slug)
- নেটওয়ার্ক বা ট্রান্সপোর্ট এরর (connection refused, broken pipe)
- অপ্রত্যাশিত ইন্টার্নাল এরর (generic “something went wrong”)
টেস্টগুলো স্থিতিশীল রাখুন—যা রানের মধ্যে পরিবর্তনশীল হতে পারে তা কন্ট্রোল করুন। একটি ফ্লেকি টেস্ট কোনো টেস্ট না থাকার চেয়ে খারাপ কারণ মানুষ তা উপেক্ষা করতে শেখে।
সময় ও র্যান্ডমনেস পূর্বানুমানযোগ্য করুন
যদি হ্যান্ডলার time.Now(), IDs, বা র্যান্ডম ভ্যালু ব্যবহার করে, সেগুলো ইনজেক্ট করুন। হ্যান্ডলার বা সার্ভিসে ক্লক ফাংশন এবং ID জেনারেটরপাস করুন। টেস্টে স্থির মান রিটার্ন করুন যাতে আপনি এক্সাক্ট JSON ফিল্ড ও হেডার assert করতে পারেন।
ছোট ফেইক ব্যবহার করুন, এবং "কোন সাইড-ইফেক্ট হয়নি" assert করুন
ফুল মক এর বদলে ছোট ফেইক বা স্টব ব্যবহার করা ভালো। একটি ফেইক কলগুলো রেকর্ড করতে পারে এবং আপনি assert করতে পারবেন যে ব্যর্থতার পরে কিছু হয়নি।
উদাহরণ: "create user" হ্যান্ডলার-এ, যদি ডাটাবেস ইনসার্ট unique constraint এর কারণে ব্যর্থ হয়, assert করুন স্ট্যাটাস কোড সঠিক, এরর বডি স্থিতিশীল, এবং কোনো ওয়েলকাম ইমেইল পাঠানো হয়নি। আপনার ফেইক মেইলার একটি কাউন্টার (sent=0) প্রকাশ করতে পারে যাতে ব্যর্থতার পথ দেখায় ইমেইল ট্রিগার হয়নি।
সাধারণ ভুলগুলো যা হ্যান্ডলার টেস্টকে অননুমোদনীয় করে তোলে
হ্যান্ডলার টেস্টগুলো প্রায়ই ভুল কারণে ফেইল করে। টেস্টে তৈরি করা রিকোয়েস্ট বাস্তব ক্লায়েন্ট রিকোয়েস্টের মত নয়। তা noisy ফেইল এবং ভুল আত্মবিশ্বাস সৃষ্টি করে।
একটি সাধারণ সমস্যা হচ্ছে JSON পাঠানো হলেও হেডার রাখা হয়নি যা হ্যান্ডলার আশা করে। আপনার কোড যদি Content-Type: application/json চেক করে, তা না রাখলে হ্যান্ডলার JSON ডিকোডিং স্কিপ করতে পারে, আলাদা স্ট্যাটাস রিটার্ন করতে পারে, বা এমন শাখা নিতে পারে যা প্রোডকশনে কখনো ঘটে না। একইভাবে auth: একটি মিসিং Authorization হেডার ও একটি অবৈধ টোকেন আলাদা কেস হওয়া উচিত।
অন্য একটি ফাঁদ হচ্ছে পুরো JSON রেসপন্স কাঁচা স্ট্রিং হিসেবে assert করা। ফিল্ড অর্ডার, স্পেসিং, বা নতুন ফিল্ডের মতো ছোট পরিবর্তনগুলো টেস্ট ভাঙ্গে এমনকি API সঠিক থাকলেও। বডি একটি struct বা map[string]any এ ডিকোড করুন, তারপর গুরুত্বপূর্ণ জিনিসগুলো assert করুন: স্ট্যাটাস, এরর কোড, মেসেজ, এবং কয়েকটি কী ফিল্ড।
টেস্টগুলো অননির্ভরযোগ্য হয় যখন কেসগুলো শেয়ার করা মিউটেবল স্টেট ব্যবহার করে। একই ইন-মেমোরি স্টোর, গ্লোবাল ভ্যারিয়েবল, বা সিঙ্গলটন রাউটার টেবিলের সারিতে অনাকাঙ্খিত ডেটা লিক করতে পারে। প্রতিটি টেস্ট কেস পরিষ্কার শুরু করা উচিত, বা t.Cleanup-এ স্টেট রিসেট করা উচিত।
সাধারণভাবে ভ্রান্ত প্যাটার্নগুলো:
- বাস্তব ক্লায়েন্ট ব্যবহার করা হেডার ও এনকোডিং ছাড়া রিকোয়েস্ট তৈরি করা
- পুরো JSON স্ট্রিং মিলানো বদলে ডিকোড করে ফিল্ড চেক না করা
- কেসগুলোর মধ্যে শেয়ার করা ডাটাবেস/ক্যাশ/গ্লোবাল স্টেট ব্যবহার করা
- auth, ভ্যালিডেশন, এবং বিজনেস লজিক এক বড় টেস্টে একসাথে প্যাক করা
প্রতিটি টেস্ট ফোকাসেড রাখুন। একটি কেস ফেইল করলে আপনি দুই সেকেন্ডের মধ্যেই বুঝতে পারবেন সেটা auth, ইনপুট রুল, না কি এরর ফরম্যাটিং—এ কোনটা।
রিলিজের আগে দ্রুত চেকলিস্ট যা আপনি পুনরায় ব্যবহার করতে পারেন
রিলিজের আগে টেস্টগুলো দুটি জিনিস প্রমাণ করা উচিত: এন্ডপয়েন্টটি তার কনট্র্যাক্ট পালন করে, এবং নিরাপদ, পূর্বানুমানযোগ্যভাবে ব্যর্থ হয়।
নিচেগুলো টেবিল-চালিত কেস হিসেবে চালান এবং প্রতিটি কেস রেসপন্স ও যেকোনো সাইড-ইফেক্ট দুটোই assert করুক:
- Auth: নো টোকেন, বাজে টোকেন, ভুল রোল, সঠিক রোল (এবং নিশ্চিত করুন "ভুল রোল" কেস কোনো ডিটেইল ফাঁস করে না)
- ইনপুট: মিসিং আবশ্যক ফিল্ড, ভুল টাইপ, বাউন্ডারি সাইজ (min/max), অচেনা ফিল্ড যেগুলো আপনি প্রত্যাখ্যান করতে চান
- আউটপুট: স্ট্যাটাস কোড, গুরুত্বপূর্ণ হেডার (যেমন
Content-Type), আবশ্যক JSON ফিল্ড, কনসিস্টেন্ট এরর শেইপ - ডিপেন্ডেন্সি: একটি ডাউনস্ট্রিম ব্যর্থতা জোর করে (DB, queue, payment, email), নিরাপদ মেসেজ যাচাই করুন, কোনো পার্শিয়াল রাইট হয়নি তা নিশ্চিত করুন
- Idempotency: একই রিকোয়েস্ট পুনরায় পাঠিয়ে (বা টাইমআউটের পরে রিট্রাই করে) দুইবার তৈরি না হয় তা নিশ্চিত করুন
তারপরে একটি স্যানিটি অ্যাসার্শন যোগ করুন যা স্কিপ করা যেতে পারে: নিশ্চিত করুন হ্যান্ডলার অপ্রাসঙ্গিক কিছু স্পর্শ করেনি। উদাহরণস্বরূপ, ভ্যালিডেশন ব্যর্থতার কেসে নিশ্চিত করুন কোনো রেকর্ড তৈরি হয়নি এবং কোনো ইমেইল পাঠানো হয়নি।
যদি আপনি AppMaster-এর মত টুল ব্যবহার করে API বানান, এই একই চেকলিস্ট প্রযোজ্য। পয়েন্টটা একই: পাবলিক আচরণ স্থির রাখা যাচাই করা।
উদাহরণ: একটি এন্ডপয়েন্ট, একটি ছোট টেবিল, এবং কি ধরা পড়ে
ধরা যাক একটি সিম্পল এন্ডপয়েন্ট আছে: POST /login. এটি JSON গ্রহণ করে email এবং password সহ। সফল হলে 200 টোকেন দেয়, ইনভ্যালিড ইনপুট হলে 400, ভুল ক্রেডেনশিয়াল হলে 401, এবং auth সার্ভিস ডাউন হলে 500।
নিচের কম্প্যাক্ট টেবিল প্রোডকশনে ভেঙে পড়া খুব কিছুই কভার করে।
func TestLoginHandler(t *testing.T) {
// Fake dependency so we can force 200/401/500 without hitting real systems.
auth := &FakeAuth{ /* configure per test */ }
h := NewLoginHandler(auth)
tests := []struct {
name string
body string
authHeader string
setup func()
wantStatus int
wantBody string
}{
{"success", `{"email":"[email protected]","password":"secret"}`, "", func() { auth.Mode = "ok" }, 200, `"token"`},
{"missing password", `{"email":"[email protected]"}`, "", func() { auth.Mode = "ok" }, 400, "password"},
{"bad email format", `{"email":"not-an-email","password":"secret"}`, "", func() { auth.Mode = "ok" }, 400, "email"},
{"invalid JSON", `{`, "", func() { auth.Mode = "ok" }, 400, "invalid JSON"},
{"unauthorized", `{"email":"[email protected]","password":"wrong"}`, "", func() { auth.Mode = "unauthorized" }, 401, "unauthorized"},
{"server error", `{"email":"[email protected]","password":"secret"}`, "", func() { auth.Mode = "error" }, 500, "internal"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.setup()
req := httptest.NewRequest(http.MethodPost, "/login", strings.NewReader(tt.body))
req.Header.Set("Content-Type", "application/json")
if tt.authHeader != "" {
req.Header.Set("Authorization", tt.authHeader)
}
rr := httptest.NewRecorder()
h.ServeHTTP(rr, req)
if rr.Code != tt.wantStatus {
t.Fatalf("status = %d, want %d, body=%s", rr.Code, tt.wantStatus, rr.Body.String())
}
if tt.wantBody != "" && !strings.Contains(rr.Body.String(), tt.wantBody) {
t.Fatalf("body %q does not contain %q", rr.Body.String(), tt.wantBody)
}
})
}
}
"missing password" কেসটি অগোছালোভাবে রাউট করা থেকে শুরু করে: আপনি শুধু email পাঠান, Content-Type সেট করেন, ServeHTTP চালান, তারপর 400 এবং এমন একটি এরর যা স্পষ্টভাবে password-এর দিকে নির্দেশ করে—এটি একাই প্রমাণ করে আপনার ডিকোডার, ভ্যালিডেটর, এবং এরোর রেসপন্স ফরম্যাট একসাথে কাজ করছে।
যদি আপনি কনট্র্যাক্ট, auth মডিউল, এবং ইন্টিগ্রেশনগুলো স্ট্যান্ডার্ডাইজ করতে দ্রুত একটি উপায় চান, AppMaster এ জন্য উপযোগী। তা থাকার পরেও এই টেস্টগুলো মূল্যবান থাকে কারণ সেগুলো ক্লায়েন্টদের ওপর নির্ভরযোগ্য আচরণ লক করে।


