QA इंजीनियरों के लिए डेटाबेस टेस्टिंग
डेटाबेस टेस्टिंग जाँचती है कि ऐप्लिकेशन जो डेटा स्टोर करता है वह सही, पूरा, सुसंगत और सुरक्षित है — सिर्फ़ यह नहीं कि स्क्रीन पर “Saved” दिखा। UI या API टेस्ट बताता है कि ऐप्लिकेशन क्या रिपोर्ट करता है; डेटाबेस जाँच बताती है कि असल में क्या हुआ: कौन-सी rows लिखी गईं, कौन-से constraints टिके, जुड़े हुए records अब भी मेल खाते हैं या नहीं, और कुछ पीछे तो नहीं छूट गया।
यह पेज इस विषय का प्रवेश द्वार है। यह उन क्षेत्रों का नक्शा देता है जो टेस्टर कवर करता है — CRUD verification, constraints और referential integrity, stored logic, सिस्टमों के बीच consistency, test data, performance और security — और दो गहरे पेजों तक ले जाता है: SQL verification queries लिखना, और transactions, concurrency व migrations की टेस्टिंग।
डेटाबेस टेस्टिंग क्या कवर करती है
डेटाबेस तक पहुँचने वाले ज़्यादातर defects इंटरफ़ेस पर दिखते ही नहीं। ऑर्डर कन्फ़र्मेशन पेज बिलकुल सही दिख सकता है जबकि order row में गलत total हो, कोई payment गलत ऑर्डर से जुड़ा हो, या रद्द ऑर्डर के items अब भी reserved हों। यूज़र इन defects से बाद में मिलते हैं — मासिक स्टेटमेंट में, फ़ेल होते refund में, ऐसी रिपोर्ट में जिसके आँकड़े नहीं मिलते — जब इन्हें ढूँढना महँगा हो चुका होता है।
डेटाबेस टेस्टिंग कोई अलग test level नहीं है। यह जाँचों का एक सेट है जो integration, system और acceptance levels पर वहाँ लगाया जाता है जहाँ डेटा बनता या बदलता है। इसका oracle खुद डेटा मॉडल है: schema, उसके constraints, एक टेबल को दूसरी से जोड़ने वाले business rules, और वे आँकड़े जिन्हें मेल खाना चाहिए।
- Structural (schema) testing — टेबल, कॉलम, types, lengths, defaults, constraints और indexes डिज़ाइन और migration scripts से मेल खाते हैं।
- Functional data testing — UI या API जो भी create, read, update और delete करता है, वह ठीक वही rows छोड़ता है जो उसे छोड़नी चाहिए।
- Integrity testing — keys, uniqueness, NOT NULL, CHECK और foreign key नियम खराब डेटा को रोकते हैं, और जुड़े records सुसंगत रहते हैं।
- Logic testing — stored procedures, triggers, views और scheduled jobs सामान्य, boundary और error inputs पर सही नतीजे देते हैं।
- Transaction और concurrency testing — सब-या-कुछ-नहीं व्यवहार, और जब दो यूज़र एक ही डेटा एक साथ बदलें तब सही नतीजे (transactions और migrations पेज पर विस्तार से)।
- Migration testing — schema और data बदलाव वास्तविक वॉल्यूम पर सही चलते हैं और चल रहे ऐप्लिकेशन को काम करता रखते हैं।
- Non-functional testing — बड़ी टेबलों पर query performance, access control और personal data की सुरक्षा।
CRUD validation: प्रोडक्ट से करें, डेटाबेस में पुष्टि करें
मूल तकनीक सरल है: action वैसे ही करें जैसे यूज़र या क्लाइंट करता — UI या API से — फिर असर की पुष्टि के लिए डेटाबेस query करें। सीधे rows insert करके उसे फ़ीचर का टेस्ट कहना उसी ऐप्लिकेशन कोड को छोड़ देता है जिसे आपको टेस्ट करना है। सीधे inserts preconditions तैयार करने के लिए हैं, व्यवहार चलाने के लिए नहीं।
हर operation में target row और उसके आस-पास की rows जाँचें। Create को एक row लिखनी चाहिए जिसमें हर फ़ील्ड सही map हो, server-generated values (ids, timestamps, status) सेट हों और child rows बनी हों। Update को सिर्फ़ इच्छित कॉलम बदलने चाहिए और updated_at आगे बढ़ाना चाहिए। Delete को ठीक वही हटाना या soft-delete करना चाहिए जो requirement कहती है और child rows से डिज़ाइन के अनुसार निपटना चाहिए। Read को जो स्टोर है वही लौटाना चाहिए — अरबी टेक्स्ट, इमोजी, अधिकतम lengths और दशमलव सहित — बिना truncation या दोबारा rounding के।
उदाहरण के लिए, API से ऐसा ऑर्डर बनाने के बाद जिसमें 75.00 SAR वाले item की दो यूनिट और 40.00 SAR वाले item की एक यूनिट हो, इस section के अंत में दी गई query order row, item rows और server द्वारा सेट values की पुष्टि करती है। इस तरह की जाँच से पकड़े जाने वाले आम CRUD defects:
- Field mapping — API delivery_notes स्वीकार करती है, पर कॉलम NULL ही रहता है।
- Precision — 19.995 की राशि एक टेबल में 19.99 और दूसरी में 20.00 स्टोर होती है।
- Encoding — अरबी नाम “????” के रूप में सेव होते हैं क्योंकि कोई कॉलम या कनेक्शन non-Unicode character set इस्तेमाल करता है।
- Time zones — एक सर्विस created_at लोकल टाइम में और दूसरी UTC में स्टोर करती है।
- Partial writes — तीसरे item पर validation error के बाद order row मौजूद है पर उसके items नहीं।
-- After POST /api/orders returned order_id 1042 for customer 317
SELECT order_id, customer_id, status, total_amount, created_at, updated_at
FROM orders
WHERE order_id = 1042;
SELECT product_sku, quantity, unit_price
FROM order_items
WHERE order_id = 1042
ORDER BY order_item_id;
-- Expected: customer_id = 317, status = 'PENDING',
-- total_amount = 2 x 75.00 + 1 x 40.00 = 190.00,
-- exactly two item rows, created_at set by the server (UTC)फ़ीचर का टेस्ट
- टेस्ट
- प्रोडक्ट UI या API, ऐप्लिकेशन कोड
- डेटाबेस
- 1 टेस्ट से डेटाबेस तक: सिर्फ़ preconditions तैयार करें यहाँ सीधा insert ठीक है, जैसे customer 317 मौजूद हो
- 2 टेस्ट से प्रोडक्ट तक: ऑर्डर वैसे बनाएँ जैसे क्लाइंट बनाता है customer 317: 75.00 SAR वाले item की 2 यूनिट, 40.00 SAR वाले की 1
POST /api/orders - 3 प्रोडक्ट से डेटाबेस तक: ऐप्लिकेशन कोड rows लिखता है order row और उसकी item rows; server id, status और timestamps सेट करता है
- 4 प्रोडक्ट से टेस्ट तक: order_id 1042 लौटाता है जवाब
- 5 टेस्ट से डेटाबेस तक: लौटाई गई key पर query करें
orders, फिरorder_itemsWHERE order_id = 1042 - 6 डेटाबेस से टेस्ट तक: अपेक्षित values से मिलाने वाली rows जवाब customer 317, PENDING, total 190.00, ठीक दो item rows,
created_atserver द्वारा सेट (UTC)
परिणाम: action प्रोडक्ट से होकर जाता है; सिर्फ़ preconditions की तैयारी और जाँच सीधे डेटाबेस तक जाती हैं।
फ़ीचर का टेस्ट नहीं
- टेस्ट
- प्रोडक्ट UI या API, ऐप्लिकेशन कोड
- डेटाबेस
- 1 टेस्ट से डेटाबेस तक: order और item rows सीधे INSERT करें API से ऑर्डर बनाने के बजाय
- 2 टेस्ट से डेटाबेस तक: उन्हें वापस SELECT करें
- 3 डेटाबेस से टेस्ट तक: वही rows जो आपने लिखीं जवाब
परिणाम: प्रोडक्ट चलता ही नहीं: यह उसी ऐप्लिकेशन कोड को छोड़ देता है जिसे आपको टेस्ट करना है। सावधानी
Constraints: डेटाबेस जो नियम लागू करता है
Constraints डेटाबेस की आख़िरी रक्षा पंक्ति हैं: ऐप्लिकेशन validation में bug हो तब भी अच्छा schema मॉडल तोड़ने वाले डेटा को मना कर देता है। इन्हें टेस्ट करने के दो पहलू हैं। पहला, पुष्टि करें कि constraint मौजूद है — schema या migration पढ़ें, सिर्फ़ डिज़ाइन डॉक्युमेंट नहीं। दूसरा, पुष्टि करें कि ऐप्लिकेशन rejection को ठीक से संभालता है: साफ़ validation message या 4xx API response, न कि raw database error वाला 500।
Engines अलग होते हैं, इसलिए जिस पर आप ship करते हैं उसे जाँचें। MySQL CHECK constraints केवल 8.0.16 वर्ज़न से लागू करता है (पुराने वर्ज़न उन्हें parse करके अनदेखा करते थे), और SQLite foreign keys तभी लागू करता है जब कनेक्शन PRAGMA foreign_keys = ON से उन्हें चालू करे। जो constraint migration फ़ाइल में है पर runtime पर लागू नहीं होता, वह एक finding है।
- Primary key — हर row की अनूठी पहचान है। दोबारा भेजे गए request को उसी business event के लिए नए id वाली दूसरी row नहीं बनानी चाहिए; यह idempotency की समस्या है जिसे key अकेले हल नहीं करती।
- UNIQUE — एक ही ईमेल दो बार रजिस्टर करें, अलग letter case और आगे-पीछे spaces के साथ भी। टीम से तय करें कि Layla.H@example.com और layla.h@example.com एक ही ग्राहक हैं या नहीं, फिर जाँचें कि डेटाबेस भी यही मानता है।
- NOT NULL — फ़ील्ड को गायब, खाली और सिर्फ़ spaces के साथ भेजें। PostgreSQL और MySQL में खाली string NULL नहीं है, इसलिए NOT NULL अकेले खाली नाम नहीं रोकता।
- CHECK — quantity शून्य से ज़्यादा, राशियाँ ऋणात्मक नहीं, status अनुमत सेट में। Boundaries (0, 1, अधिकतम) और उनसे एक क़दम बाहर टेस्ट करें।
- FOREIGN KEY — ऐसे customer id के लिए ऑर्डर बनाएँ जो मौजूद नहीं है। Insert फ़ेल होना चाहिए और API को अर्थपूर्ण error लौटाना चाहिए।
- Defaults, types और lengths — छोड़े गए फ़ील्ड को documented default मिलता है, और हर कॉलम सबसे लंबा valid input संभालता है। जिस फ़ील्ड में UI 100 characters देता है उसके पीछे 50-character कॉलम truncation bug है जो होने का इंतज़ार कर रहा है।
आज़माएँ: बाउंड्री नंबर डेटासेट जनरेटर आज़माएँ: स्पेशल कैरेक्टर डेटासेट जनरेटर आज़माएँ: Unicode डेटासेट जनरेटर
Relationships, referential integrity और cascades
Referential integrity का मतलब है हर reference किसी मौजूद चीज़ की ओर इशारा करे: हर order item किसी मौजूद ऑर्डर का हो, हर payment किसी मौजूद ऑर्डर का, हर ऑर्डर किसी मौजूद ग्राहक का। Foreign keys घोषित हों तो डेटाबेस इसे लागू करता है। न हों — जो microservices में, sharded या legacy schemas में और batch jobs से भरी टेबलों में आम है — तो ऐप्लिकेशन को लागू करना पड़ता है, और orphan rows टेस्टर को मिलने वाले सबसे आम data defects में से एक बन जाती हैं।
Cascade नियम तय करते हैं कि parent डिलीट होने या उसकी key बदलने पर child rows का क्या हो। ON DELETE CASCADE parent के साथ children हटा देता है; RESTRICT या NO ACTION children रहते delete मना कर देता है; SET NULL child रखता है और reference खाली कर देता है। हर एक किसी relationship के लिए सही और किसी दूसरे के लिए ख़तरनाक है: ग्राहक का delete invoices तक cascade करना ऐसे records मिटा सकता है जिन्हें रखना व्यवसाय के लिए ज़रूरी है। हर relationship को requirement के सामने टेस्ट करें, इस बात के सामने नहीं कि schema संयोग से क्या करता है।
- प्रोडक्ट से कोई parent डिलीट करें, फिर हर child टेबल में ऐसी rows खोजें जो अब भी उसे reference करती हैं।
- Soft deletes जाँचें: deleted चिह्नित ग्राहक सूचियों से गायब होना चाहिए, जबकि उसके पुराने ऑर्डर पढ़ने लायक रहने पड़ सकते हैं।
- Batch imports, data migrations और फ़ेल jobs के बाद orphan-detection queries चलाएँ — SQL पेज LEFT JOIN … IS NULL पैटर्न दिखाता है।
Stored procedures, triggers और back-end नियम
जब business logic डेटाबेस में रहता है — महीना बंद करने वाली stored procedure, audit row लिखने वाला trigger, रिपोर्ट को डेटा देने वाला view — तो उसे किसी भी कोड की तरह मानें: inputs और expected outputs तय करें, और equivalence partitioning व boundary values से cases चुनें। तैयार डेटा वाले test database में procedures को सीधे call करें, और उन्हें उस ऐप्लिकेशन path से भी चलाएँ जो उन्हें इस्तेमाल करता है।
Triggers पर ख़ास ध्यान चाहिए क्योंकि वे चुपचाप काम करते हैं। जाँचें कि वे हर उस path पर fire होते हैं जहाँ होने चाहिए (insert, update और delete; single-row और bulk statements), दो बार fire नहीं होते, और trigger के अंदर की failure उस statement को rollback करती है जिसने उसे चलाया, न कि आधा काम छोड़ देती है।
- Audit trigger — ऑर्डर का status बदलने पर ठीक एक audit row लिखी जाती है जिसमें पुरानी और नई value, action करने वाला यूज़र और server timestamp हो।
- Procedure boundaries — month-end procedure को leap year के फ़रवरी के लिए और ऐसे महीने के लिए चलाएँ जिसमें कोई ऑर्डर नहीं।
- Back-end validation — UI में लागू नियम API में भी लागू है, और जहाँ डिज़ाइन कहता है वहाँ डेटाबेस में भी। पुष्टि के लिए सीधे API call करके UI को bypass करें।
सर्विसों, cache और search index के बीच data consistency
आधुनिक सिस्टम डेटा की कॉपियाँ रखते हैं: read replica, cache, search index, reporting warehouse, किसी दूसरी microservice का अपना डेटाबेस। हर कॉपी source of truth से भटक सकती है। डेटाबेस में अपडेट हुई कीमत जो अब भी cache में पुरानी है, ऑर्डर सर्विस में दिखता ऑर्डर जो invoicing में नहीं है, डिलीट हुआ ग्राहक जो search में अब भी मिलता है — ये consistency defects हैं, और ये कॉपियों की तुलना से मिलते हैं।
तकनीक है reconciliation: एक ही आबादी को दो जगह गिनें और जोड़ें, फिर तुलना करें। status और दिन के हिसाब से counts, राशियों के sums, और ऐसे ids की सूचियाँ जो एक तरफ़ हैं दूसरी तरफ़ नहीं — इनसे ज़्यादातर भटकाव पकड़ में आता है। जहाँ replication asynchronous है, वहाँ टीम से स्वीकार्य देरी तय करें — eventual consistency डिज़ाइन का फ़ैसला है, defect नहीं — और टेस्ट करें कि कॉपियाँ उसी अवधि में मिल जाती हैं, failures, retries और replays के बाद भी।
- आज बने ऑर्डर: ऑर्डर डेटाबेस, invoicing सर्विस और finance रिपोर्ट में count और total।
- Search index: डेटाबेस का हर active product id index में है, और कोई deleted id बचा नहीं है।
- Cache invalidation: कोई record अपडेट करें, फिर उसे API से तुरंत और cache की अवधि के बाद पढ़ें; अगर डिज़ाइन कहता है कि writes cache invalidate करते हैं, तो दोनों reads नई value लौटाएँ।
- Event-driven flows: consumer रोकें, records बनाएँ, उसे फिर चालू करें, और पुष्टि करें कि कुछ खोया नहीं और कुछ दो बार लागू नहीं हुआ।
Test data: सिंथेटिक, seeded और reset करने योग्य
डेटाबेस टेस्ट को ज्ञात डेटा चाहिए — ज्ञात ग्राहक, ज्ञात ऑर्डर, ज्ञात totals — ताकि टेस्ट चलने से पहले expected result लिखा जा सके। इसे सोच-समझकर बनाएँ: एक seed script baseline बनाती है, हर टेस्ट अपनी ज़रूरत का डेटा जोड़ता है और सफ़ाई करता है या ऐसे transaction में चलता है जिसे rollback किया जाता है, और पूरा डेटाबेस मिनटों में baseline पर reset हो सकता है।
Production का personal data टेस्ट environments में कॉपी न करें। सऊदी अरब का Personal Data Protection Law (PDPL), जिसकी निगरानी SDAIA करता है, personal data की processing को नियंत्रित करता है, और असली ग्राहकों के नाम, राष्ट्रीय ID, फ़ोन नंबर और पते वाला टेस्ट environment production से कमज़ोर नियंत्रणों में personal data की processing है। डिफ़ॉल्ट रूप से सिंथेटिक डेटा इस्तेमाल करें। अगर production जैसे डेटा की सचमुच ज़रूरत हो, उदाहरण के लिए migration rehearsal में, तो तरीका अपने data protection officer से तय करें: masked डेटा भी personal data रह सकता है अगर उसे किसी व्यक्ति से जोड़ा जा सके।
- Generated SQL से seed करें: वास्तविक baseline के लिए CREATE TABLE और INSERT scripts, migrations के साथ version control में रखी हुई।
- कठिन values जान-बूझकर शामिल करें: अधिकतम lengths, शून्य राशियाँ, अरबी और देवनागरी टेक्स्ट, इमोजी, नामों में apostrophe, महीने और साल के अंत की तारीख़ें।
- Reset strategy चुनें: हर टेस्ट के बाद rollback, हर suite के लिए truncate और re-seed, snapshot या template database से restore, या हर pipeline run के लिए नया containerised डेटाबेस।
- Performance और migration टेस्ट के लिए production जैसी row counts पर डेटा generate करें, दस rows पर नहीं।
आज़माएँ: SQL डेटासेट जनरेटर आज़माएँ: SQL INSERT जनरेटर आज़माएँ: CSV से SQL आज़माएँ: JSON से SQL
टेस्टर के स्तर पर performance: indexes, query plans और N+1
डेटाबेस के performance defects ढूँढने के लिए आपको उसे tune करने की ज़रूरत नहीं। आम कारण सीधा होता है: जो query 200 test rows पर तेज़ थी, वह production में लाखों rows scan करती है क्योंकि जिस कॉलम पर वह filter करती है उस पर index नहीं है। वास्तविक वॉल्यूम के साथ टेस्ट करें, डेटा बढ़ने के साथ response times देखें, और कुछ धीमा हो तो query plan माँगें।
EXPLAIN दिखाता है कि डेटाबेस query को कैसे चलाने वाला है — index इस्तेमाल करेगा या पूरी टेबल scan करेगा, और किस क्रम में joins करेगा। PostgreSQL और MySQL EXPLAIN और EXPLAIN ANALYZE देते हैं; SQLite EXPLAIN QUERY PLAN देता है। EXPLAIN ANALYZE statement को सचमुच execute करता है, इसलिए इसे UPDATE या DELETE पर कभी ऐसे transaction के बाहर न चलाएँ जिसे आप rollback करेंगे। Plan की हर लाइन पढ़ना ज़रूरी नहीं: जो query कुछ ही rows लौटानी चाहिए उसमें बड़ी टेबल का full scan bug report के लायक है।
- N+1 queries — list पेज एक query से 50 ऑर्डर लोड करता है, फिर हर ऑर्डर का ग्राहक लाने के लिए एक और query चलाता है: एक-दो की जगह 51 round trips। यह query log या APM trace में लगभग एक जैसे statements की बौछार के रूप में दिखता है।
- Deep pagination — OFFSET-आधारित paging बड़ी टेबलों के पिछले पेजों पर धीमी हो जाती है; पेज 1 और पेज 5,000 दोनों टेस्ट करें।
- Unbounded queries — बिना limit वाले exports और reports जो पूरी टेबल memory में लोड करते हैं।
- Foreign keys पर और अक्सर चलने वाली queries के WHERE, JOIN और ORDER BY में इस्तेमाल कॉलमों पर गायब indexes।
Security: injection, privileges और personal data
SQL injection तब होता है जब यूज़र input को SQL टेक्स्ट में जोड़ दिया जाता है। OWASP के अनुसार बचाव है हर जगह parameterised queries (prepared statements), और उन हिस्सों के लिए allow-list validation जो parameter नहीं बन सकते, जैसे sort कॉलम का नाम। टेस्टर के रूप में exploit लिखने के बजाय अपेक्षा की पुष्टि करें: हर input में quotes, semicolons, comment markers और दूसरे SQL metacharacters वाली values डालें और उम्मीद करें कि वे अक्षरशः स्टोर और प्रदर्शित हों, बिना database error, stack trace या व्यवहार में बदलाव के। किसी भी response में database error message अपने आप में एक finding है। गहरी injection testing अधिकृत security assessment का काम है।
Least privilege गड़बड़ी होने पर नुकसान सीमित करता है। ऐप्लिकेशन अकाउंट के पास केवल ज़रूरी अधिकार हों — आम तौर पर अपनी टेबलों पर read और write, schema बदलाव नहीं, admin rights नहीं — और migrations अलग अकाउंट से चलें। टेस्टर और reporting tools read-only अकाउंट इस्तेमाल करें।
- पता करें कि ऐप्लिकेशन किस अकाउंट से कनेक्ट होता है और उसे क्या करने की अनुमति है; database owner के रूप में चलता production ऐप्लिकेशन एक finding है।
- Personal data वहाँ खोजें जहाँ उसे नहीं होना चाहिए: ऐप्लिकेशन logs, error messages, analytics events, CSV exports, shared drives पर backups और test environments।
- जाँचें कि passwords किसी dedicated password-hashing algorithm (जैसे Argon2id, bcrypt, scrypt या PBKDF2, जो हर password को salt करते हैं) से स्टोर हैं, किसी सामान्य तेज़ hash से नहीं, और tokens व दूसरे secrets डिज़ाइन के अनुसार hashed या encrypted हैं, कभी plain text में नहीं।
- Audit columns — created_at, updated_at, created_by, updated_by — सर्वर सेट करता है और क्लाइंट से स्वीकार नहीं होते; updated_at और updated_by record बदलने पर हर बार बदलते हैं, जबकि created_at और created_by insert के बाद कभी नहीं बदलते।
आम defects, automation और CI
डेटाबेस जाँचें अच्छी तरह automate होती हैं क्योंकि उनके expected results सटीक होते हैं। Integrity और reconciliation queries को API tests वाली suite में ही रखें: API call करें, डेटाबेस query करें, assert करें। Suite को pipeline में ताज़ा migrate और ताज़ा seed किए डेटाबेस पर चलाएँ, ताकि हर build साबित करे कि migrations साफ़ लागू होते हैं और data rules अब भी टिके हैं। Read-only integrity queries का छोटा सेट — orphans, duplicates, न मिलने वाले totals — हर deployment के बाद और तय समय पर staging पर चलाने के लिए रखें।
जब कोई जाँच फ़ेल हो, तो उसकी लौटाई rows ही सबूत हैं: query और उसका output bug report में लगाएँ। इस तरह मिले defects आम तौर पर ऐसे दिखते हैं:
- “आख़िरी item पर discount लगने पर order total item lines के योग से अलग”
- “ग्राहक डिलीट करने पर orphan payment rows बचती हैं (payments.order_id पर foreign key नहीं)”
- “Register पर double-click करने से duplicate ग्राहक बनता है”
- “order_items.product_name में अरबी product नाम truncate होते हैं”
- “Search 24 घंटे से ज़्यादा पहले डिलीट हुए products अब भी लौटाता है”
- “Order history पेज हर ऑर्डर पर एक query चलाता है (N+1): 200 ऑर्डर पर 4 s”
- “ईमेल पहले से मौजूद होने पर API raw database error के साथ 500 लौटाती है”
अक्सर पूछे जाने वाले सवाल
क्या डेटाबेस टेस्टिंग के लिए टेस्टर को SQL आनी चाहिए?
हाँ, पढ़ने के स्तर पर: WHERE के साथ SELECT, inner और left joins, HAVING के साथ GROUP BY, COUNT और SUM जैसे aggregates, EXISTS, और बुनियादी transactions। ऐप्लिकेशन के लगभग हर बदलाव की पुष्टि के लिए इतना काफ़ी है। Schema design और tuning डेवलपर और DBA का काम रहता है।
UI से टेस्ट करूँ या सीधे डेटाबेस में?
दोनों, अलग उद्देश्यों के लिए। व्यवहार UI या API से चलाएँ ताकि ऐप्लिकेशन कोड चले, फिर असर की पुष्टि के लिए डेटाबेस query करें। सीधी SQL preconditions, verification और integrity checks के लिए इस्तेमाल करें — फ़ीचर के विकल्प के रूप में नहीं।
क्या टेस्टिंग के लिए production डेटा की कॉपी इस्तेमाल कर सकते हैं?
Personal data के साथ नहीं। सऊदी PDPL के तहत टेस्ट environment में असली ग्राहक डेटा भी processing हो रहा personal data ही है। डिफ़ॉल्ट रूप से सिंथेटिक डेटा लें, और production जैसा डेटा सचमुच ज़रूरी हो तो data protection officer को शामिल करें।
Referential integrity क्या है?
यह नियम कि टेबलों के बीच हर reference किसी मौजूद row की ओर इशारा करे — ऑर्डर के बिना order items नहीं, ऑर्डर के बिना payments नहीं। डेटाबेस में foreign keys इसे लागू करती हैं; उनके बिना deletes, imports और फ़ेल jobs के बाद orphan rows दिखती हैं।
डेटाबेस टेस्टिंग के लिए कौन-से tools चाहिए?
Read-only अकाउंट से जुड़ा SQL client, प्रोडक्ट का अपना UI और API, test data generate और reset करने का तरीका, और ऐसा test framework जो API calls के बाद SQL चला सके। Mutqan के SQL और dataset generators seed scripts और edge-case values में मदद करते हैं।
संदर्भ
- Constraints — PostgreSQL Documentationcheck, not-null, unique, primary key, foreign key और exclusion constraints — वे नियम जो डेटाबेस लागू करता है और टेस्टर जाँचता है।
- Transactions — PostgreSQL Documentationtransaction क्या है: कई क़दम जो BEGIN, COMMIT और ROLLBACK से सब-या-कुछ-नहीं लागू होते हैं, commit होने तक दूसरों को नहीं दिखते।
- Transaction Isolation Levels — MySQL 8.4 Reference Manualचार SQL isolation levels जैसे InnoDB उन्हें लागू करता है, जिसमें REPEATABLE READ डिफ़ॉल्ट है।
- OWASP Cheat Sheet Series: SQL Injection Preventionपैरामीटराइज़्ड क्वेरी, सुरक्षित रूप से इस्तेमाल stored procedure, allow-list वैलिडेशन और least privilege — वे बचाव जिन्हें डेटाबेस टेस्ट को पुष्ट करना चाहिए।
- SQL InjectionSQL इंजेक्शन कैसे काम करता है और स्ट्रिंग जोड़ने के बजाय पैरामीटराइज़्ड क्वेरी ही बचाव क्यों हैं।
- Personal Data Protection Law (English translation)व्यक्तिगत डेटा पर सऊदी कानून — टेस्ट वातावरण में असली ग्राहक रिकॉर्ड की प्रतियों के बजाय सिंथेटिक डेटा क्यों इस्तेमाल करना चाहिए।
- ISO/IEC 9075-1:2023 — Information technology — Database languages SQL — Part 1: Framework (SQL/Framework)अंतरराष्ट्रीय SQL मानक का framework भाग, जिसे PostgreSQL, MySQL, SQLite आदि की बोलियाँ अलग-अलग हद तक लागू करती हैं।
- ISTQB Certified Tester Foundation Level (CTFL) v4.0सॉफ़्टवेयर टेस्टिंग का फ़ाउंडेशन पाठ्यक्रम: टेस्ट स्तर और प्रकार, टेस्ट डिज़ाइन तकनीकें, टेस्ट प्रबंधन और टूल।
अभ्यास
डेटाबेस में ऑर्डर कैंसलेशन की जाँच करें
आप staging पर एक काल्पनिक ग्रोसरी डिलीवरी सर्विस टेस्ट करते हैं। Build 3.8.0 में ग्राहक अपना ऑर्डर तब तक कैंसल कर सकता है जब तक वह स्टोर से न निकला हो। आपने कार्ड से पेड एक टेस्ट ऑर्डर Android app से कैंसल किया, जैसे ग्राहक करता, और फिर read-only अकाउंट से डेटाबेस में नतीजा जाँचा। नीचे के material में छह requirements, schema का एक अंश, app और server पर जो हुआ वह, और आपकी पाँच queries का output है।
नतीजे को वैसे परखें जैसे गाइड सिखाती है: पहले expected state लिखें, फिर उसकी तुलना असली rows से करें, और target row से आगे देखें — उसकी पड़ोसी rows, app में दिखने वाली डेटा की कॉपी, और खुद schema। हर अंतर के लिए बताएँ कि वह डेटाबेस टेस्टिंग के किस क्षेत्र का है। काग़ज़ और पेन काफ़ी हैं; 40–50 मिनट रखें। App log में अरबी कारण JSON \u escapes के रूप में है, जिसे Unicode डिकोडर वापस टेक्स्ट में बदल देता है; अगर आप स्टेप 4 में SQL लिखते हैं, तो उसका syntax गाइड की tool सूची के SQL क्वेरी वैलिडेटर से जाँच सकते हैं।
काल्पनिक ग्रोसरी डिलीवरी सर्विस: web shop, Android और iOS apps।
Staging, build 3.8.0, PostgreSQL डेटाबेस। सिर्फ़ synthetic test accounts।
Design rule: हर timestamp UTC में स्टोर होता है। रियाद का समय UTC+3 है।
3.8 में नया: ग्राहक अपना ऑर्डर स्टोर से निकलने तक कैंसल कर सकता है।
Requirements: ऑर्डर कैंसल करना
CN-1 सिर्फ़ PENDING या PACKING वाला ऑर्डर कैंसल हो सकता है। किसी और status पर
API 409 ORDER_NOT_CANCELLABLE लौटाती है और कुछ नहीं बदलता।
CN-2 ऑर्डर की row रहती है: status CANCELLED, cancelled_at = cancel का समय,
cancel_reason = ग्राहक का टेक्स्ट जैसा लिखा, और updated_at आगे बढ़ता है।
order_items की rows रहती हैं, ताकि order history में वे दिखती रहें।
CN-3 ऑर्डर का हर stock reservation डिलीट होता है, ताकि stock फिर बिक सके।
CN-4 अगर ऑर्डर कार्ड से पेड था, तो एक refund row बनती है: amount = ऑर्डर का
total, status PENDING, card payment से जुड़ी।
CN-5 हर status बदलाव order_status_audit में ठीक एक row लिखता है।
CN-6 Apps में "My orders" एक cache से पढ़ता है, जिसकी entries 5 मिनट रहती हैं।
Status बदलने पर ऑर्डर की entry हटती है, ताकि अगली read ताज़ा हो।
Schema का अंश (सभी टेबल एक ही डेटाबेस में हैं)
CREATE TABLE stock_reservations (
reservation_id BIGINT PRIMARY KEY,
order_id BIGINT NOT NULL,
product_sku VARCHAR(40) NOT NULL,
qty INTEGER NOT NULL CHECK (qty > 0)
);
CREATE TABLE refunds (
refund_id BIGINT PRIMARY KEY,
order_id BIGINT NOT NULL REFERENCES orders (order_id),
payment_id BIGINT NOT NULL REFERENCES payments (payment_id),
amount NUMERIC(10,2) NOT NULL CHECK (amount > 0),
status VARCHAR(20) NOT NULL,
created_at TIMESTAMP NOT NULL
);
orders: order_id PRIMARY KEY, customer_id, status, total_amount, cancel_reason,
created_at, updated_at, cancelled_at
order_items.order_id REFERENCES orders (order_id) ON DELETE CASCADE
payments: payment_id PRIMARY KEY, order_id, amount, status
order_status_audit: audit_id, order_id, old_status, new_status, changed_by, changed_at
order_status_audit को एक trigger भरता है: AFTER UPDATE OF status ON orders।
stock_reservations पर primary key के अलावा कोई index नहीं है।
टेस्ट, Android app से, जैसे ग्राहक करता
ग्राहक 5021 (test account)। ऑर्डर 88412, 2026-10-05 07:12:40 UTC पर बना,
status PACKING, कार्ड से पेड: payment 77031, 55.00 SAR।
दो stock reservations, हर item का एक:
MLK-1L ताज़ा दूध 1 L: 2 x 6.50 SAR
RCE-5KG बासमती चावल 5 kg: 1 x 42.00 SAR
07:15:04 UTC: "Cancel order", कारण अरबी में लिखा ("गलती से ऑर्डर किया")
App का network log (app किसी request पर 10 s बाद हार मानता है और एक बार retry करता है)
07:15:04 POST /api/orders/88412/cancel
{"reason": "\u0637\u0644\u0628\u062a \u0628\u0627\u0644\u062e\u0637\u0623"}
10 s में कोई response नहीं
07:15:15 POST /api/orders/88412/cancel
अपने आप retry, वही body
200 {"orderId": 88412, "status": "CANCELLED"}
Server log
07:15:04 POST /api/orders/88412/cancel 200 (10.6 s)
07:15:15 POST /api/orders/88412/cancel 200 (0.5 s)
App, "My orders": 07:15:16 पर ऑर्डर 88412 "Packing" दिखाता है;
07:20:30 पर "Cancelled"।
बाद में डेटाबेस की जाँच (read-only अकाउंट)
Q1 SELECT status, cancel_reason, created_at, updated_at, cancelled_at
FROM orders WHERE order_id = 88412;
status: CANCELLED
cancel_reason: ???? ??????
created_at: 2026-10-05 07:12:40
updated_at: 2026-10-05 07:12:40
cancelled_at: 2026-10-05 10:15:15
Q2 SELECT product_sku, quantity, unit_price FROM order_items
WHERE order_id = 88412 ORDER BY order_item_id;
MLK-1L | 2 | 6.50
RCE-5KG | 1 | 42.00
Q3 SELECT reservation_id, product_sku, qty FROM stock_reservations
WHERE order_id = 88412;
5530182 | RCE-5KG | 1
Q4 SELECT refund_id, payment_id, amount, status, created_at FROM refunds
WHERE order_id = 88412 ORDER BY refund_id;
9101 | 77031 | 55.00 | PENDING | 2026-10-05 07:15:14
9102 | 77031 | 55.00 | PENDING | 2026-10-05 07:15:15
Q5 SELECT audit_id, old_status, new_status, changed_by, changed_at
FROM order_status_audit WHERE order_id = 88412 ORDER BY audit_id;
70411 | PENDING | PACKING | system | 2026-10-05 07:13:05
70415 | PACKING | CANCELLED | customer:5021 | 2026-10-05 07:15:14
70416 | CANCELLED | CANCELLED | customer:5021 | 2026-10-05 07:15:15- Q1 से Q5 पढ़ने से पहले, ऑर्डर 88412 के एक सही cancel के बाद की expected state लिखें: orders की row, order_items, stock_reservations, refunds और order_status_audit, दोबारा भेजे गए request का response, और "My orders" में क्या दिखना चाहिए।
- Queries का output, दोनों logs और app की सूची अपनी expected state से मिलाएँ। हर अंतर के लिए सबूत (टेबल, row, values), वह requirement जो टूटी, और डेटाबेस टेस्टिंग का वह क्षेत्र लिखें जिससे वह जुड़ा है।
- जो सही है और रिपोर्ट नहीं करना है, उसे कारण के साथ लिखें।
- Schema के अंश की review करें: कौन-से नियम किसी constraint से सुरक्षित नहीं हैं, आप क्या प्रस्ताव देंगे, और हर deployment के बाद चलने वाली जाँचों में कौन-सी read-only integrity queries जोड़ेंगे?
- अपनी findings को क्रम दें, और पहली bug report का title, expected result और सबूत लिखें।
मॉडल उत्तर दिखाएँ
स्टेप 1 — expected state। orders: 88412 की row मौजूद रहती है, status CANCELLED, cancel_reason ठीक طلبت بالخطأ, cancelled_at 2026-10-05 को लगभग 07:15 UTC, updated_at उसी पल तक आगे बढ़ा हुआ, और created_at 07:12:40 पर अपरिवर्तित। order_items: दोनों rows अपरिवर्तित (MLK-1L 2 × 6.50 और RCE-5KG 1 × 42.00)। stock_reservations: 88412 की कोई row नहीं (CN-3)। refunds: ठीक एक row, payment 77031 के लिए, amount 55.00 (2 × 6.50 + 42.00) और status PENDING (CN-4)। order_status_audit: पहले वाली row PENDING → PACKING और ठीक एक नई row PACKING → CANCELLED (CN-5)। दोबारा भेजे गए request को ऑर्डर पहले से CANCELLED मिलता है, जो न PENDING है न PACKING, इसलिए CN-1 के अनुसार उसे 409 ORDER_NOT_CANCELLABLE मिलता है और कुछ नहीं बदलता। Cancel के बाद पहली बार पढ़ने पर "My orders" Cancelled दिखाता है (CN-6)।
स्टेप 2 — अंतर। F1, सबसे गंभीर: cancel दो बार हुआ। पहला request असल में सफल रहा, लेकिन server को 10.6 s लगे, इसलिए app ने 10 s बाद हार मानी और retry किया; retry को ऐसा ऑर्डर मिला जो पहले से CANCELLED था, और उसे 409 मिलना चाहिए था। उसे 200 मिला और उसने सारा काम दोबारा किया। Q4 एक ही payment 77031 के लिए 55.00 के दो refunds, 9101 और 9102, दिखाता है: 55.00 SAR के ऑर्डर पर 110.00 SAR refund की कतार में है। Audit row 70416 (CANCELLED → CANCELLED) और दूसरे request से मेल खाता cancelled_at उसी दूसरे run के निशान हैं। यह CN-1 और CN-4 तोड़ता है। क्षेत्र: functional data testing (create को ठीक एक row लिखनी चाहिए) और idempotency की वह समस्या जो गाइड primary key के तहत बताती है: दोबारा भेजे गए request को उसी business event के लिए दूसरी row नहीं बनानी चाहिए। Fix कैसे हो, यह टीम का फ़ैसला है: वह status जाँच जो CN-1 पहले से माँगता है, और क्योंकि app ख़ुद retry करता है, बेहतर है साथ में एक idempotency key भी, ताकि retry को error की जगह पहले request का नतीजा मिले। एक ही पल में भेजे गए दो एक जैसे requests के लिए ऐसी जाँच चाहिए जो concurrency में भी टिके, जिसे transactions और migrations वाला पेज कवर करता है। हर हाल में डेटाबेस में अंत में एक ही refund होना चाहिए।
F2: एक stock reservation release नहीं हुआ। Q3 अब भी RCE-5KG के लिए reservation 5530182 लौटाता है, जबकि MLK-1L का reservation हट गया, यानी CN-3 आधा ही हुआ। क्षेत्र: CRUD — delete को ठीक वही हटाना चाहिए जो requirement कहती है, और यह partial write है। चावल का बैग reserved रहता है और बिक नहीं सकता, और दूसरे कैंसल ऑर्डर भी इसी तरह stock रोक सकते हैं। डेवलपर को पैटर्न दिखाने के लिए एक, दो और पाँच items वाले ऑर्डरों से दोबारा टेस्ट करें।
F3: अरबी में लिखा cancel का कारण खो गया। जहाँ ग्राहक ने طلبت بالخطأ लिखा, वहाँ Q1 ???? ?????? दिखाता है: चार और छह अक्षरों वाले दो शब्दों के लिए चार और छह प्रश्नचिह्न — गाइड के CRUD section वाला encoding defect। यह CN-2 तोड़ता है। पहले पक्का करें कि ये प्रश्नचिह्न सच में स्टोर हुए हैं, सिर्फ़ आपका SQL client टेक्स्ट को ऐसे नहीं दिखा रहा: SELECT octet_length(cancel_reason) FROM orders WHERE order_id = 88412 11 लौटाता है अगर value दस प्रश्नचिह्न और एक space है, जबकि अरबी कारण UTF-8 में 21 bytes लेता है। स्टोर हुए प्रश्नचिह्न वापस ग्राहक के शब्दों में नहीं बदले जा सकते। Request body decode करने पर ठीक طلبت بالخطأ बनता है (\u0637 पहला अक्षर ط है), यानी टेक्स्ट app से सही निकला और server की तरफ़, डेटाबेस तक पहुँचते हुए बिगड़ा। पूछें कि service टेक्स्ट को कैसे convert करती है और कनेक्शन व डेटाबेस कौन-सा encoding इस्तेमाल करते हैं (PostgreSQL में encoding पूरे डेटाबेस के लिए तय होता है, हर कॉलम के लिए नहीं), वही कारण web shop और iOS app से सेव करके देखें कि एक path प्रभावित है या सभी, और दोबारा टेस्ट में देवनागरी टेक्स्ट और एक emoji जोड़ें।
F4: cancelled_at रियाद के समय में है। इसमें 10:15:15 है, जबकि server log, audit row 70416 और refund 9102 उसी पल को 07:15:15 UTC बताते हैं। ठीक तीन घंटे आगे होने का मतलब है कि value लोकल टाइम में स्टोर हुई, design rule के ख़िलाफ़। क्षेत्र: CRUD (time zones)। इससे निकाली गई हर चीज़ गलत है: ऑर्डर बनने के 2 min 35 s बाद की जगह 3 h 2 min 35 s बाद कैंसल हुआ लगता है, और घंटेवार cancellations की रिपोर्ट उसे गलत घंटे में रखती है।
F5: updated_at आगे नहीं बढ़ता। यह अब भी created_at (07:12:40) के बराबर है, जबकि ऑर्डर दो बार बदला: 07:13:05 पर PACKING में (audit row 70411) और फिर CANCELLED में। गाइड का नियम है कि हर update updated_at को आगे बढ़ाए और उसे server सेट करे। क्योंकि PACKING वाले पहले बदलाव में भी यह छूट गया, defect शायद updated_at को सँभालने के सामान्य तरीके में है, सिर्फ़ cancel के कोड में नहीं। पूछें कि क्या कोई job updated_at के आधार पर बदलाव पढ़ता है, जैसे reporting के लिए incremental export: ऐसा job यह cancel कभी नहीं उठाएगा।
F6: "My orders" ने पुराना status दिखाया। 07:15:16 पर, server के CANCELLED जवाब देने के बाद भी, app ने 88412 को Packing दिखाया, और Cancelled सिर्फ़ 07:20:30 पर दिखाया, जब तक 5 मिनट वाली cache entry expire हो चुकी थी। CN-6 कहता है कि बदलाव entry हटाता है, इसलिए cancel के बाद पहली read ही ताज़ा होनी चाहिए। क्षेत्र: कॉपियों के बीच data consistency (cache invalidation)। Packing देखने वाला ग्राहक फिर cancel करने की कोशिश कर सकता है या support को कॉल कर सकता है। पुष्टि के लिए cancel के तुरंत बाद API response की तुलना app से करें और दोनों मिलने तक हर 30 सेकंड पढ़ें; पूछें कि क्या सूची किसी read replica से भी पढ़ती है, जो देर कर सकने वाली एक और कॉपी होगी।
F7, छोटा, stored logic में: audit row 70416 में CANCELLED → CANCELLED दर्ज है, जो status का बदलाव है ही नहीं। AFTER UPDATE OF status के रूप में घोषित trigger तब भी fire होता है जब कोई UPDATE उस कॉलम को सेट करे, भले उसी value पर जो उसमें पहले से है; PostgreSQL में WHEN (OLD.status IS DISTINCT FROM NEW.status) जैसी शर्त उसे असली बदलावों तक सीमित करती है। F1 ठीक होने से यह row छिप जाएगी पर trigger नहीं बदलेगा, इसलिए इसे अलग रिपोर्ट करें। एक और observation दर्ज करें, अभी उसे defect कहे बिना: पहले request को 10.6 s लगे, जो app के timeout से ज़्यादा है, और retry इसी से हुआ। Request का trace और cancel की queries के query plans माँगें। stock_reservations.order_id पर गायब index एक संभावना है, लेकिन तेज़ दूसरा request (0.5 s) बताता है कि समय शायद कहीं और गया, जैसे किसी दूसरी सर्विस की call में।
स्टेप 3 — सही, रिपोर्ट नहीं करना। ऑर्डर की row रखी गई, डिलीट नहीं हुई (CN-2)। order_items की दोनों rows मौजूद हैं, जो order history के लिए CN-2 माँगता है; अगर आपने उनके हटने की उम्मीद की थी, तो आपने requirement की जगह एक धारणा के सामने टेस्ट किया, और order_items पर ON DELETE CASCADE यहाँ मायने नहीं रखता, क्योंकि कुछ डिलीट नहीं हुआ। हर refund की राशि सही है, 55.00 SAR (2 × 6.50 + 42.00), और payment भी सही; defect यह है कि वे दो हैं। Audit row 70411 PACKING में पहले हुआ जायज़ बदलाव है। changed_by में customer:5021 है, जिसे server ने सेट किया: request body में सिर्फ़ कारण था।
स्टेप 4 — schema क्या सुरक्षित नहीं करता। (a) उसी payment के दूसरे refund को कुछ नहीं रोकता। अगर नियम हर payment पर एक refund है, तो refunds पर UNIQUE (payment_id) refund 9102 को रिजेक्ट कर देता; अगर partial refunds की योजना है, तो सुरक्षा API की idempotency और यह जाँच होनी चाहिए कि refunds कभी payment से ज़्यादा न हों। पूछें कि कौन-सा नियम लागू है, और टेस्ट करें कि रिजेक्ट हुआ insert app तक 409 के रूप में पहुँचे, raw database error वाले 500 के रूप में नहीं। (b) refunds.status कोई भी टेक्स्ट स्वीकार करता है: अनुमत values वाला CHECK प्रस्तावित करें और उनकी सूची माँगें। (c) stock_reservations.order_id पर foreign key नहीं है, जबकि दोनों टेबल एक ही डेटाबेस में हैं, इसलिए कोई reservation ऐसे ऑर्डर की ओर इशारा कर सकता है जो मौजूद नहीं; foreign key प्रस्तावित करें, और जब तक वह न बने, orphan rows जाँचें। (d) उस कॉलम पर index भी नहीं है, इसलिए किसी ऑर्डर के reservations ढूँढना पूरी टेबल पढ़ता है; production जैसे वॉल्यूम पर query plan माँगें। (e) F7 के पीछे की trigger definition पढ़ने को माँगें।
हर deployment के बाद की जाँचों के लिए read-only integrity queries; किसी को भी कोई row नहीं लौटानी चाहिए। Duplicate refunds, जब टीम हर payment पर एक refund का नियम पक्का कर दे: SELECT payment_id, COUNT(*) AS refund_count, SUM(amount) AS refunded FROM refunds GROUP BY payment_id HAVING COUNT(*) > 1। Payment से ज़्यादा refunds: SELECT p.payment_id, p.amount, SUM(r.amount) AS refunded FROM payments p JOIN refunds r ON r.payment_id = p.payment_id GROUP BY p.payment_id, p.amount HAVING SUM(r.amount) > p.amount। कैंसल ऑर्डरों पर बचे reservations: SELECT r.reservation_id, r.order_id FROM stock_reservations r JOIN orders o ON o.order_id = r.order_id WHERE o.status = 'CANCELLED'। Orphan reservations: SELECT r.reservation_id, r.order_id FROM stock_reservations r LEFT JOIN orders o ON o.order_id = r.order_id WHERE o.order_id IS NULL। बिना बदलाव वाली audit rows: SELECT audit_id, order_id FROM order_status_audit WHERE old_status = new_status। ऐसे कैंसल ऑर्डर जिनका updated_at आगे नहीं बढ़ा: SELECT order_id FROM orders WHERE status = 'CANCELLED' AND (cancelled_at IS NULL OR updated_at < cancelled_at)। यही queries pipeline में API tests के बाद, ताज़ा migrate और seed किए डेटाबेस पर भी चलाएँ।
स्टेप 5 — क्रम और पहली रिपोर्ट। F1 सबसे पहले: यह पैसा दो बार लौटाता है और तब-तब होता है जब धीमा response app से retry करवाए। फिर F2 (कैंसल ऑर्डरों से रुका stock), F3 (ग्राहक के शब्द हमेशा के लिए खोए), F6 (ग्राहक पाँच मिनट तक गलत status देखता है), F4 और F5 (गलत और पुराने timestamps जो reports और किसी भी sync job को गुमराह करते हैं), और F7। पहली रिपोर्ट का title: "Cancel का retry दूसरा refund बनाता है: ऑर्डर 88412 पर एक card payment के लिए 55.00 SAR के दो refunds"। Steps: PACKING वाले, कार्ड से पेड ऑर्डर को API से कैंसल करें, फिर वही cancel request दोबारा भेजें; या app में तब कैंसल करें जब पहले response को 10 s से ज़्यादा लगें। Expected: दूसरे request को 409 ORDER_NOT_CANCELLABLE मिले (या पहले request का नतीजा, अगर टीम idempotency key जोड़ती है), और डेटाबेस में एक refund और एक नई audit row हो। सबूत: staging पर build 3.8.0, test account 5021, ऑर्डर 88412, दोनों logs के अंश, Q4 और Q5 अपने output के साथ, और CN-1 व CN-4। प्रस्तावित severity: Critical।
अपना काम जाँचें। आपके पास छह defects (F1 से F6), छोटा F7, धीमा पहला request एक खुले सवाल के रूप में, और कम से कम तीन सही नतीजे होने चाहिए। अगर आपको सिर्फ़ F1 और F2 मिले, तो target row के बाक़ी कॉलम (F3 से F5) और app में डेटा की कॉपी (F6) फिर देखें: response ने CANCELLED कहा और मुख्य rows ठीक दिखीं, और ठीक इसीलिए गाइड कहती है कि डेटाबेस में और target row से आगे पुष्टि करें। ध्यान दें कि टेस्ट कैसे चला: cancel app से हुआ, जैसे ग्राहक करता है, और डेटाबेस को सिर्फ़ read-only अकाउंट से पढ़ा गया; SQL से status को CANCELLED सेट करना उस ऐप्लिकेशन कोड को छोड़ देता जिसमें इनमें से ज़्यादातर defects हैं। Fixes के बाद यही टेस्ट, retry समेत, confirmation testing के रूप में दोहराएँ, और ऊपर की queries को regression जाँचों के रूप में रखें।
इस topic में आपकी प्रगति
प्रगति ट्रैक करने के लिए साइन इन करें
Topics पूर्ण चिह्नित करें, जहाँ छोड़ा था वहीं से जारी रखें और specialisation परीक्षाएँ खोलें।
QA रोडमैप का हिस्सा
इस topic से पहले
अगले topics
इस गाइड में बताए गए टूल्स
SQL स्टेटमेंट में सिंटैक्स की समस्याएँ, असंतुलित कोष्ठक, बिना बंद की स्ट्रिंग और जोखिम भरे पैटर्न जाँचें।
SQL क्वेरी को एकसमान कीवर्ड केसिंग, इंडेंटेशन और लाइन ब्रेक के साथ फ़ॉर्मेट करें (MySQL, PostgreSQL, SQL Server, Oracle, SQLite)।
किसी भी dialect के लिए वास्तविक जैसी टेस्ट पंक्तियों के साथ SQL seed स्क्रिप्ट (CREATE TABLE + INSERT) जनरेट करें।
टेबल परिभाषा, नमूना पंक्तियों, CSV या JSON से INSERT स्टेटमेंट बनाएँ।
CSV डेटा को अनुमानित कॉलम टाइप के साथ CREATE TABLE और INSERT स्टेटमेंट में बदलें।
ऑब्जेक्ट के JSON ऐरे को SQL INSERT स्टेटमेंट में बदलें (चाहें तो CREATE TABLE के साथ)।
सीमाओं के आसपास की वैल्यू जनरेट करें: किसी भी चौड़ाई के पूर्णांकों के लिए min-1, min, min+1, max-1, max, max+1।
स्पेशल कैरेक्टर से भरे इनपुट जनरेट करें: कोट, SQL/HTML/JSON मेटाकैरेक्टर, कंट्रोल कैरेक्टर, emoji और path की चालाकियाँ।
कई लिपियों (अरबी, देवनागरी, CJK, सिरिलिक), emoji, संयोजक चिह्न और normalisation रूपों में Unicode टेस्ट स्ट्रिंग जनरेट करें।
वैल्यू की सूची (हर लाइन पर एक या कॉमा से अलग) को कोट किए SQL IN (...) क्लॉज़ में बदलें।