टेस्ट ऑटोमेशन में एसर्शन

Assertion automated test की वह line है जो actual result की तुलना expected result से करती है और दोनों अलग हों तो test को fail कर देती है। await expect(page.getByTestId('order-total')).toHaveText('172.50 SAR') एक assertion है: अगर page 150.00 SAR दिखाए, तो test fail होता है और report दोनों values दिखाती है। इससे पहले के steps सिर्फ़ application चलाते हैं; script को test assertions ही बनाते हैं।

यह lesson बताता है कि क्या जाँचना है, hard और soft assertions, retry करने वाले (web-first) assertions और failure messages। Examples पिछले lessons की तरह TypeScript के साथ Playwright Test में हैं; यही ideas Cypress, JUnit, TestNG और pytest पर भी लागू होते हैं, और जहाँ फ़र्क़ मायने रखता है वहाँ बताया गया है।

यह क्यों ज़रूरी है

Test सिर्फ़ वही defects पकड़ता है जिन्हें उसके assertions ढूँढते हैं। जो checkout test सिर्फ़ यह assert करता है कि "कोई total दिख रहा है", वह VAT न जुड़ने पर भी green रहता है, और green run team को बताता है कि feature काम कर रहा है। ISTQB ऐसे result को, जो मौजूद defect को नहीं पकड़ पाता, false-negative result कहता है; weak assertions चुपचाप यही पैदा करते हैं।

उल्टी गलती भी महँगी है। जो assertions page को बहुत जल्दी पढ़ते हैं, या ऐसी details जाँचते हैं जो किसी ने specify नहीं कीं, वे बिना किसी खराबी के fail होते हैं: false-positive results। हर false alarm की जाँच में समय लगता है, और जो team ऐसे बहुत alarms देखती है वह red builds पढ़ने की बजाय उन्हें दोबारा चलाने लगती है। सटीक, retry करने वाले assertions और साफ़ messages suite को भरोसेमंद रखते हैं: fail होने पर message ही बता देता है कि defect खोलना है या नहीं।

क्या जाँचें: नतीजा, और वह भी सटीक

हर assertion को एक expected result चाहिए, जो test oracle से आता है: requirement, acceptance criteria, API documentation या test case। इसे कभी उससे न लें जो application आज दिखा रहा है; current output को assertion में copy करने से मौजूदा defect "expected" behaviour बन जाता है।

Strong assertion वह नतीजा जाँचता है जो oracle बताता है, और उतनी ही सटीकता से जितनी oracle तय करता है। Weak assertion ऐसी चीज़ जाँचता है जो feature टूटा होने पर भी सच रहती है: कोई element मौजूद है, कोई value खाली नहीं है, response "OK" है। इस lesson का चलता हुआ example: 150.00 SAR के दो items पर 15% VAT जुड़कर total 172.50 SAR होना चाहिए।

  • जहाँ requirement value तय करती है वहाँ exact value: total पर toBeVisible() नहीं, toHaveText('172.50 SAR')। Money, counts, statuses और messages की तुलना exact होती है।
  • Patterns सिर्फ़ उन हिस्सों के लिए जो सच में बदलते हैं: ORD-418305 जैसे order number को /^ORD-\d{6}$/ से जाँचें, toBeTruthy() से या किसी एक run के number से नहीं।
  • Count, "कम से कम एक" नहीं: toHaveCount(2), यह check नहीं कि count() 0 से बड़ा है।
  • असर, सिर्फ़ error का न होना नहीं: order के बाद cart badge 0 दिखाता है; अगर API documentation कहता है कि order बनने पर 201 और body में नया order लौटता है, तो वही status और वही values जाँचें, कोई भी 2xx status नहीं।
  • Anchor के साथ absence: "कोई error banner नहीं" खाली page पर भी सच है, इसलिए पहले कोई positive signal assert करें, फिर absence।
  • ज़रूरत से ज़्यादा नहीं: CSS classes, page structure, timestamps और generated ids ऐसे हानिरहित बदलावों पर भी test fail कर देते हैं जिनसे कुछ नहीं टूटा। हर test में एक behaviour assert करें, उतने assertions के साथ जितने उसे साबित करने के लिए चाहिए।
172.50 SAR total वाला order confirmation (150.00 SAR और 15% VAT): VAT न जुड़ने पर weak assertions green रहते हैं; strong assertions exact values की तुलना करते हैं, retry करते हैं और दोनों values के साथ fail होते हैं।

कमज़ोर assertionसावधानी

ऐसी चीज़ जाँचता है जो feature टूटा होने पर भी सच रहती है

Order total:
isVisible() true है: कोई total दिख रहा है
Order number:
toBeTruthy(): कोई भी text, ORD- भी
Ordered items:
count() 0 से बड़ा
Order बनाने वाली API:
res.ok(): कोई भी 2xx status
Page कैसे पढ़ता है:
एक बार: update से पहले पढ़ सकता है
Failure message:
Expected: true Received: false
Defect: VAT नहीं जुड़ा:
Green रहता है: defect छूट जाता हैपास

मज़बूत assertion

Requirement में तय नतीजे को सटीक रूप से जाँचता है

Order total:
toHaveText('172.50 SAR'): सही total
Order number:
/^ORD-\d{6}$/: ORD और छह digits
Ordered items:
toHaveCount(2)
Order बनाने वाली API:
Status 201 और body में नई values
Page कैसे पढ़ता है:
Pass होने या timeout ख़त्म होने तक retry
Failure message:
Expected: "172.50 SAR" Received: "150.00 SAR"
Defect: VAT नहीं जुड़ा:
दोनों values के साथ red हो जाता हैफ़ेल

Checks Playwright Test में लिखे गए हैं; Cypress, JUnit, TestNG और pytest में इनके समकक्ष हैं।

आज़माएँ: REST API टेस्टर आज़माएँ: JSON Path टेस्टर

Hard और soft assertions

Hard assertion पहले failure पर test रोक देता है: expect error throw करता है, runner उसे दर्ज करता है, और उसके बाद की lines कभी नहीं चलतीं। हर framework में यही default है, और यह तब सही है जब आगे के steps इस check पर निर्भर हों: अगर confirmation page खुला ही नहीं, तो उसके total पर assertion सिर्फ़ शोर बढ़ाता है।

Soft assertion failure दर्ज करता है और test को आगे चलने देता है; अंत में test फिर भी failed mark होता है, हर fail हुए check की list के साथ। इसे एक ही result के independent facts के लिए इस्तेमाल करें, जैसे एक page या एक response के fields, ताकि हर fix-and-rerun cycle में एक-एक mismatch की बजाय एक ही run में सारे mismatches दिख जाएँ। Preconditions को hard रखें।

  • JUnit 5 और बाद के versions: assertAll(...) assertions का एक group चलाता है और उस group के हर failure की report देता है; AssertJ में SoftAssertions है।
  • TestNG: SoftAssert तभी report करता है जब अंत में assertAll() call हो; यह call भूल गए तो failures खो जाते हैं।
  • pytest: सादा assert hard होता है; soft checks plugins से आते हैं।
// Hard: the precondition. If it fails, the checks below would mean nothing.
await expect(page.getByRole('heading', { name: 'Order confirmed' })).toBeVisible();

// Soft: independent facts about the same page, all reported together.
await expect.soft(page.getByTestId('order-status')).toHaveText('Paid');
await expect.soft(page.getByTestId('order-item')).toHaveCount(2);
await expect.soft(page.getByTestId('order-total')).toHaveText('172.50 SAR');

Retry करने वाले (web-first) assertions

हर action के बाद page बदलता है: click लौट आता है, request server तक पहुँचती है, और उसके बाद ही text update होता है। जो assertion page को एक बार पढ़ता है, वह उसे बहुत जल्दी पढ़ सकता है। Retry करने वाले assertions, जिन्हें Playwright documentation auto-retrying या web-first assertions कहता है, value की जगह locator लेते हैं और तब तक दोबारा जाँचते हैं जब तक condition पूरी न हो या timeout ख़त्म न हो जाए (Playwright में default 5 seconds, configurable)। Cypress इसी तरह .should() से पहले वाली query को retry करता है। Selenium में retry करने वाले assertions नहीं हैं: WebDriverWait से condition का इंतज़ार करें, फिर assert करें।

फ़र्क़ इस बात में है कि await कहाँ है। await expect(locator).toHaveText('Paid') locator को assertion के हवाले करता है, जो page से बार-बार पूछता रहता है। expect(await locator.textContent()).toBe('Paid') एक बार पढ़ता है और ऐसी string से तुलना करता है जो कभी नहीं बदलेगी, इसलिए नतीजा timing पर निर्भर करता है, जो flaky tests की एक आम वजह है। toBe और toEqual जैसे generic matchers खुद से retry नहीं करते; इन्हें उन values के लिए इस्तेमाल करें जो पहले से पूरी हैं, जैसे parse की हुई response body। Page के बाहर की values के लिए expect.poll() एक function को बार-बार call करता है और हर नए result पर matcher लगाता है।

  • Playwright में retry करने वाले: locator पर toBeVisible, toHaveText, toContainText, toHaveValue और toHaveCount; page पर toHaveURL और toHaveTitle।
  • Negative checks भी retry करते हैं: not.toBeVisible() element के गायब होने का इंतज़ार करता है, और अगर element कभी दिखा ही नहीं तो तुरंत pass हो जाता है, इसलिए उसे anchor चाहिए।
  • Retry सही जवाब का इंतज़ार करता है; गलत expectation को सही नहीं करता। Timeout सिर्फ़ किसी जाने-पहचाने धीमे step के लिए, उसी एक assertion पर बढ़ाएँ। Waits और synchronization वाला lesson इसमें गहराई से जाता है।
// Reads once: may compare the text from before the server answered
expect(await page.getByTestId('order-status').textContent()).toBe('Paid');

// Retries until the text is "Paid" or the timeout ends (5 s by default)
await expect(page.getByTestId('order-status')).toHaveText('Paid');

// A value outside the page: poll the API until the order is paid
await expect.poll(async () => {
  const res = await request.get('/api/orders/' + orderId);
  return (await res.json()).status;
}).toBe('PAID');

ऐसे failure messages जो खुद सब बता दें

जब कोई test रात के run में fail होता है, तो उसका failure message अक्सर पहली चीज़ होती है जो कोई पढ़ता है। उपयोगी message code खोले बिना तीन सवालों का जवाब देता है: क्या जाँचा गया, क्या expected था और क्या मिला। असली values की तुलना करने वाले matchers आख़िरी दो जवाब बिना मेहनत के दे देते हैं; boolean उन्हें खो देता है। नीचे के outputs छोटे किए गए हैं; exact layout runner के version पर निर्भर करता है।

  • Values साथ रखने वाले matchers (toHaveText, toEqual, toHaveCount) को toBe(true) और toBeTruthy() पर तरजीह दें।
  • जहाँ matcher context नहीं जान सकता वहाँ custom message जोड़ें: Playwright में expect का दूसरा argument, JUnit 5 और बाद के versions में assertEquals का आख़िरी argument, pytest में comma के बाद का text: assert total == 172.5, "order total with VAT"।
  • Message को business fact की तरह, bug title जैसा लिखें: "card payment के बाद order status", "check 3" नहीं। जब test data पर loop करता है, तो case को message में शामिल करें।
  • Objects के लिए toEqual अलग fields का diff print करता है; toMatchObject सिर्फ़ आपके बताए fields की तुलना करता है, जिससे timestamps तुलना से बाहर रहते हैं।
Weak:    expect(await status.isVisible()).toBe(true);

         Error: expect(received).toBe(expected)
         Expected: true
         Received: false

Strong:  await expect(status, 'order status after card payment').toHaveText('Paid');

         Error: order status after card payment
         Expected string: "Paid"
         Received string: "Pending"

आज़माएँ: टेक्स्ट डिफ़ आज़माएँ: JSON डिफ़

Worked example: checkout test को मज़बूत बनाना

Requirement: "Payment के बाद confirmation page order number (ORD- और छह digits), status Paid, ordered items और 15% VAT सहित total दिखाता है। Cart खाली हो जाता है।" Test cart में 150.00 SAR के दो items हैं, इसलिए expected total 172.50 SAR है।

  • पहले वाले version में शुरुआती दो checks एक बार पढ़ते हैं और कुछ भी स्वीकार कर लेते हैं: 150.00 SAR का total, या ORD- जैसा बिना digits का order number, green ही रहता है। नए version में exact total और number pattern की तुलना होती है, retry के साथ।
  • "Order confirmed" heading hard है: confirmation page के बिना बाकी किसी चीज़ का मतलब नहीं। Requirement के पाँच facts soft हैं, ताकि एक run उनमें से हर गलत fact की report दे।
  • "Error" वाला check हटा दिया गया: वह खाली page पर भी pass होता था, और heading व values साबित होने के बाद कुछ नहीं जोड़ता।
  • Test का नया title behaviour बताता है, इसलिए report requirement जैसी पढ़ी जाती है। Order का समय और CSS classes assert नहीं किए गए: requirement में उनका ज़िक्र नहीं है।
// Before: stays green as long as the page opens
test('place order', async ({ page }) => {
  const checkout = new CheckoutPage(page);
  await checkout.payWithTestCard();
  expect(await page.locator('.total').isVisible()).toBeTruthy();
  expect(await page.locator('.order-no').textContent()).toBeTruthy();
  await expect(page.getByText('Error')).not.toBeVisible();
});

// After: each assertion proves one part of the requirement
test('card payment confirms order with VAT and empties cart', async ({ page }) => {
  const checkout = new CheckoutPage(page);
  await checkout.payWithTestCard();
  await expect(page.getByRole('heading', { name: 'Order confirmed' })).toBeVisible();
  await expect.soft(page.getByTestId('order-number')).toHaveText(/^ORD-\d{6}$/);
  await expect.soft(page.getByTestId('order-status')).toHaveText('Paid');
  await expect.soft(page.getByTestId('order-item')).toHaveCount(2);
  await expect.soft(page.getByTestId('order-total'), 'total = 150.00 + 15% VAT')
    .toHaveText('172.50 SAR');
  await expect.soft(page.getByTestId('cart-count')).toHaveText('0');
});

आज़माएँ: Regex टेस्टर

आम गलतियाँ

  • जहाँ requirement exact value तय करती है वहाँ सिर्फ़ existence या truthiness assert करना, जैसे toBeVisible(), toBeTruthy() या response.ok()।
  • Application का current output expected value में copy कर देना, जिससे मौजूदा defect ही expected result बन जाता है।
  • expect(...) के अंदर await लगाकर value एक बार पढ़ना और plain value की तुलना करना, बजाय locator को retry करने वाले assertion को देने के; test flaky हो जाता है।
  • बिना positive anchor के negative assertions, जो खाली या अभी load हो रहे page पर pass हो जाते हैं।
  • Preconditions के लिए soft assertions इस्तेमाल करना, जिससे एक failed sign-in बेमतलब failures की list बना देता है; या TestNG में assertAll() भूल जाना, जिससे soft failures कभी report नहीं होते।
  • CSS classes, page structure, timestamps या generated ids पर ज़रूरत से ज़्यादा assertions, जो हानिरहित बदलावों पर fail होते हैं और team को red builds अनदेखा करना सिखाते हैं।

QA का नज़रिया

Automated tests review करते समय पहले assertions पढ़ें। हर assertion से पूछें: यह test case या acceptance criteria का कौन-सा expected result साबित करता है, और क्या feature टूटा होने पर यह fail होगा? जाँचने का आसान तरीका है test को जानबूझकर fail करवाना, expected value बदलकर या किसी जाने-पहचाने defect वाले build पर चलाकर: जिस test को कभी fail होते नहीं देखा, उसने यह साबित नहीं किया कि वह fail हो सकता है।

कोई check automate करने से पहले पक्का करें कि oracle सटीक है। "Page total दिखाता है" से strong assertion नहीं बन सकता; "Total, subtotal और 15% VAT का जोड़ है, दो decimal places तक rounded" से बन सकता है, इसलिए product owner से exact rule पूछें। जब कोई test fail हो, तो assertion message को expected और received values के साथ, test के नाम और run के trace या screenshot समेत bug report में डालें। Team के साथ conventions भी तय करें: UI के लिए retry करने वाले assertions, soft assertions सिर्फ़ grouped facts के लिए, और messages business facts की भाषा में।

अभ्यास

Profile test के assertions मज़बूत बनाएँ

आपका काम

Requirement: जब signed-in user 05 के बाद आठ digits वाले format में mobile number save करता है, तो PATCH /api/profile नए number को phone में रखकर 200 लौटाता है, page "Phone number updated" message दिखाता है और field में saved number दिखता है।

नीचे का test आज pass होता है। page.waitForResponse(...) save से पहले profile response का इंतज़ार शुरू करता है, और response आने पर await saved उसे लौटाता है; waits और synchronization वाला lesson इस pattern को समझाता है। हर numbered assertion के लिए समझाएँ कि वह weak या unreliable क्यों है, फिर test को ऐसे दोबारा लिखें कि वह requirement साबित करे।

सामग्री
test('profile', async ({ page }) => {
  const profile = new ProfilePage(page);
  await profile.open();
  const saved = page.waitForResponse('**/api/profile');
  await profile.savePhone('0551234567');
  const response = await saved;
  expect(response.ok()).toBeTruthy();                                        // 1
  expect(await page.getByRole('status').isVisible()).toBe(true);             // 2
  expect(await page.getByLabel('Mobile number').inputValue()).toBeTruthy();  // 3
  await expect(page.getByText('Something went wrong')).not.toBeVisible();    // 4
});
चरण
  1. Requirement से expected results की list बनाएँ: यही आपका oracle है।
  2. हर numbered line के लिए ऐसा defect बताएँ जिसके बावजूद वह green रहे, और बताएँ कि वह एक बार पढ़ती है या retry करती है।
  3. हर assertion दोबारा लिखें और तय करें कि वह hard हो या soft।
  4. एक custom failure message जोड़ें और test का नाम behaviour के हिसाब से बदलें।
मॉडल उत्तर दिखाएँ
मॉडल उत्तर

Oracle: status 200; body में phone की value 0551234567; message का text "Phone number updated"; field की value 0551234567।

Line 1 ऐसा response जाँचती है जो पहले से पूरा है, इसलिए उसे retry की ज़रूरत नहीं, लेकिन ok() किसी भी 2xx status पर true है: 204, या ऐसा 200 जिसकी body में अब भी पुराना number हो, pass हो जाता है। Line 2 एक बार पढ़ती है, इसलिए message आने से पहले चल सकती है, और status region में error दिखने पर भी pass होती है। Line 3 एक बार पढ़ती है और कोई भी non-empty value स्वीकार करती है, पुराना number भी। Line 4 retry करती है, लेकिन not.toBeVisible() खाली या अभी load हो रहे page पर तुरंत pass हो जाता है।

दोबारा लिखा हुआ: expect(response.status()).toBe(200) hard assertion के रूप में, क्योंकि save fail हुआ तो page checks सिर्फ़ शोर बढ़ाएँगे। फिर soft: expect.soft((await response.json()).phone).toBe('0551234567'), await expect.soft(page.getByRole('status')).toHaveText('Phone number updated') और await expect.soft(page.getByLabel('Mobile number'), 'mobile number after save').toHaveValue('0551234567')। Response वाले दोनों checks generic matchers इस्तेमाल करते हैं, जो सही है क्योंकि response पहले से पूरा है; page वाले दोनों checks retry करते हैं।

Line 4 हटा दी जाती है, या message check के बाद रखी जाती है, जहाँ message उसका anchor बनता है। Test का अच्छा नाम: saving a valid mobile number updates the profile।

अक्सर पूछे जाने वाले सवाल

एक test में कितने assertions होने चाहिए?

जितने एक behaviour साबित करने के लिए चाहिए। "हर test में एक assertion" वाली सलाह एक behaviour के बारे में है, एक expect line के बारे में नहीं: एक ही order का status, total और item count जाँचना एक साथ ठीक है। अगर test दो असंबंधित behaviours जाँचता है तो उसे बाँट दें, ताकि उसका नाम और failure एक ही कारण की ओर इशारा करें।

सारे failures एक साथ देखने के लिए हर assertion को soft क्यों न बना दें?

क्योंकि आगे के checks अक्सर पिछले checks पर निर्भर होते हैं। अगर sign-in fail हुआ, तो dashboard पर बीस soft failures उस एक failure को छिपा देते हैं जो मायने रखता है। Preconditions को hard रखें, और soft assertions में सिर्फ़ एक result के independent facts को group करें।

क्या retry करने वाले assertions tests को धीमा करते हैं?

जब application सही हो तब नहीं: retry करने वाला assertion condition पूरी होते ही pass हो जाता है, और पूरा timeout तभी लगाता है जब check fail होने वाला हो। Fixed sleep हर run में अपनी पूरी अवधि लेता है।

संदर्भ

  • Playwright DocumentationMicrosoft (Playwright)Playwright का आधिकारिक दस्तावेज़ — Chromium, WebKit और Firefox चलाने वाला end-to-end टेस्ट फ़्रेमवर्क।
  • Best Practices — PlaywrightMicrosoft (Playwright)यूज़र को दिखने वाला व्यवहार टेस्ट करें, टेस्ट अलग-थलग रखें, मज़बूत locator और web-first assertions अपनाएँ, और trace से डिबग करें।
  • ISTQB GlossaryISTQBटेस्टिंग शब्दों की मानक परिभाषाएँ — टेस्ट केस, रिग्रेशन टेस्टिंग, सेवेरिटी, प्रायोरिटी, बाउंड्री वैल्यू एनालिसिस आदि।
  • Cypress DocumentationCypress.ioCypress से ब्राउज़र में एंड-टू-एंड और कंपोनेंट टेस्टिंग का आधिकारिक दस्तावेज़।

Mutqan tools से अभ्यास करें

रेगुलर एक्सप्रेशन को लाइव टेस्ट करें — मैच हाइलाइटिंग, कैप्चर ग्रुप और रिप्लेस प्रीव्यू के साथ।

यूटिलिटीज़

दो JSON डॉक्यूमेंट की सिमैंटिक तुलना करें और जोड़ी गई, हटाई गई तथा बदली गई वैल्यू हाइलाइट करें।

JSON और डेटा

दो टेक्स्ट की लाइन-दर-लाइन या शब्द-दर-शब्द तुलना करें और अंतर हाइलाइट करें।

फ़ॉर्मेटिंग

JSON पर JSONPath एक्सप्रेशन ($.store.book[*].author) चलाएँ और मैच लाइव देखें।

JSON और डेटा

method, हेडर और JSON बॉडी पर पूरे नियंत्रण के साथ REST endpoint टेस्ट करें, फिर स्टेटस, हेडर और JSON path पर assertion लगाएँ।

API टेस्टिंग

और गहराई से

इस topic में आपकी प्रगति

QA रोडमैप का हिस्सा