API टेस्ट ऑटोमेशन
API test automation, API tests को code या चलाए जा सकने वाले collections में बदल देता है: machine requests भेजती है, assertions जाँचती है और हर test को pass या fail बताती है, बिना किसी इंसान के steps दोहराए। Test framework ज़रूरी हिस्से देता है: tests ढूँढकर चलाने वाला runner, HTTP client, assertions, setup और cleanup के लिए hooks, और results लिखने वाले reporters।
API response validation वाले lesson से POST /api/v1/orders की checklist लीजिए। Automate होने पर यह एक test बन जाती है जो order बनाता है, 201, Location header, status pending और calculated total को assert करता है, और फिर order को दोबारा पढ़ता है। CI pipeline में यह हर pull request पर चलता है, और fail होने पर change तब तक merge नहीं होता जब तक कोई उसे देख न ले।
यह क्यों ज़रूरी है
APIs अक्सर बदलते हैं, और हर बदलाव कहीं न कहीं किसी client को तोड़ सकता है: rename हुआ field, काम करना बंद कर चुका validation rule, या जहाँ 201 का वादा था वहाँ 200। हर merge के बाद दर्जनों request checks हाथ से दोहराना धीमा है, और practice में ऐसा होता ही नहीं। Automated regression suite इन्हें हर change पर दोहराता है, इसलिए defect उसी pipeline run में मिल जाता है जो उसे लाने वाले commit के साथ चला, जब change developer के दिमाग़ में अभी ताज़ा होता है।
API tests सबसे उपयोगी automated tests में से भी हैं। वे user interface को छोड़ देते हैं, इसलिए UI tests से तेज़ चलते हैं और कम टूटते हैं, और ऐसे business rules और error paths तक पहुँचते हैं जिन्हें कोई screen trigger नहीं कर सकती। Tester के लिए automation मेहनत को checks दोबारा चलाने से हटाकर उन्हें design करने पर लगाता है। लेकिन यह तभी काम करता है जब team suite पर भरोसा करे: product से असंबंधित वजहों से fail होने वाले tests, जैसे shared data या code में लिखा URL, जल्द ही नज़रअंदाज़ होने लगते हैं।
Frameworks: क्या देते हैं और कैसे चुनें
हर API automation framework, किसी भी language में हो, यही हिस्से जोड़ता है: test runner जो tests ढूँढता है, उन्हें अक्सर parallel में चलाता है और results बताता है; HTTP client जो requests भेजता है और status, headers और body दिखाता है; assertions; hooks या fixtures जो tests से पहले तैयारी और बाद में cleanup करते हैं; और reporters, जिनमें JUnit XML भी है, वह result format जिसे ज़्यादातर CI systems पढ़ सकते हैं। Frameworks में मुख्य फ़र्क tests लिखने के तरीके का है, और कोई भी framework आपके लिए tests design नहीं करता: techniques और expected results अब भी पिछले lessons से आते हैं।
- Code-based: tests किसी programming language की files होते हैं। उदाहरण: Playwright Test अपने
requestfixture के साथ (TypeScript या JavaScript), REST Assured, JUnit या TestNG के साथ (Java), और pytest,requestslibrary के साथ (Python)। इनसे data, reuse और structure पर पूरा control मिलता है, और tests का review बाकी code की तरह होता है। - Collection-based: API client में saved requests, test scripts के साथ, जैसे Postman collection। वही collection command line से Newman या Postman CLI से चलता है, और इसी तरह CI तक पहुँचता है। शुरुआत जल्दी होती है; collection बढ़ने पर helpers share करना और changes review करना मुश्किल होता जाता है।
- वही चुनें जो आपकी team के developers पहले से इस्तेमाल करते हैं, ताकि tests उसी repository में रहें और कोई भी उन्हें चला और ठीक कर सके। इस lesson के concepts एक framework से दूसरे में साथ चलते हैं।
API tests के लिए code का छोटा primer
Simple API tests पढ़ने और लिखने के लिए developer होना ज़रूरी नहीं। आपको "टेस्टर्स के लिए कोड पढ़ना" वाले lesson की values, objects, loops और functions चाहिए, और कुछ और features जो हर API test इस्तेमाल करता है। इस section के आख़िर का code Playwright Test के लिए TypeScript में एक पूरी test file है, अभी बिना layers के: POST /api/v1/orders के लिए एक positive test और दो data-driven negative tests; यह endpoint 1 से 99 तक की qty स्वीकार करता है। नीचे के notes इसे हिस्सा-हिस्सा पढ़ते हैं। यह file अपने बनाए orders delete नहीं करती; layers और cleanup आगे के sections जोड़ते हैं। आगे गहराई के लिए "टेस्ट ऑटोमेशन के लिए प्रोग्रामिंग" वाला बाद का lesson modules, classes और asynchronous code विस्तार से समझाता है; वह further reading है, इस lesson की शर्त नहीं।
- Test file structure: test file एक module है।
import { test, expect } from '@playwright/test'framework package से दो functions लेता है:testएक test declare करता है औरexpectएक assertion लिखता है। कोई file अपने functionsexportसे दूसरों के साथ share करती है, और दूसरी files उन्हेंimportसे लेती हैं, जैसा नीचे की layers करती हैं। Runner test folder में उन files को ढूँढता है जिनके नाम.spec.tsया.test.tsपर ख़त्म होते हैं, और उनके हरtestको चलाता है। - Test:
test('creates a pending order', async ({ request }) => { … })test को एक title देता है, जो report में दिखता है, और एक function जिसे runner call करता है।=>function लिखने का छोटा तरीका (arrow function) है, और{ request }वहrequestfixture लेता है जो runner देता है: configuration के base URL और headers के साथ पहले से set up HTTP client। - async और await: request में समय लगता है, इसलिए
request.post(…)एक promise लौटाता है, यानी ऐसी value जो बाद में आती है।awaitअगली line चलने से पहले उसका इंतज़ार करता है, औरawaitसिर्फ़asyncmarked function में इस्तेमाल हो सकता है।awaitभूलना beginners का सबसे आम bug है: अगली line response की जगह अधूरे promise के साथ काम करती है, और test उलझाने वाले message के साथ fail होता है या request पूरी होने से पहले ख़त्म हो जाता है। - Request call:
request.post(path, { data })path परPOSTभेजता है, और path base URL के हिसाब से resolve होता है। Playwrightdataobject को JSON body में बदलता है औरContent-Type: application/jsonset करता है, जब तक आपने उसे खुद set न किया हो।request.get,put,patchऔरdeleteभी इसी तरह काम करते हैं। - Status, headers और body पर assertions:
expect(actual).toBe(expected)exact तुलना करता है।res.status()status code है,res.headers()lower-case names वाले headers लौटाता है, औरawait res.json()body parse करता है, जिसके बाद fields किसी भी object की तरह पढ़े जाते हैं (order.status)।toContaintext का कोई हिस्सा जाँचता है,toHaveLengthlist की length। पहला fail हुआexpecttest रोक देता है और expected और actual value दिखाता है। - Data-driven rows:
for (const qty of [0, 100])हर value के लिए एक test register करता है। Title एक template literal है, यानी back-quote characters के बीच का text, जिसमें${qty}value डालता है, इसलिए report में "rejects qty 0" और "rejects qty 100" दो अलग tests दिखते हैं। - Environment configuration: file में न कोई host name है, न कोई key। Base URL और token environment variables से आते हैं: code के बाहर terminal या CI job में set की गई named values, जिन्हें code में
process.env.API_BASE_URLकी तरह पढ़ा जाता है। नीचे environments वाले section की configuration file इन्हें एक बार पढ़ती है, इसलिए वही test local, test या staging पर चलता है और कोई secret commit नहीं होता।
// tests/orders/first.spec.ts
import { test, expect } from '@playwright/test';
test('creates a pending order', async ({ request }) => {
const res = await request.post('/api/v1/orders', {
data: { customerId: 7, items: [{ sku: 'A-100', qty: 2 }] },
});
expect(res.status()).toBe(201);
expect(res.headers()['content-type']).toContain('application/json');
const order = await res.json();
expect(order.status).toBe('pending');
expect(order.items).toHaveLength(1);
});
for (const qty of [0, 100]) {
test(`rejects qty ${qty}`, async ({ request }) => {
const res = await request.post('/api/v1/orders', {
data: { customerId: 7, items: [{ sku: 'A-100', qty: qty }] },
});
expect(res.status()).toBe(422);
});
}Layers में suite की बनावट
पाँच tests एक file में रह सकते हैं, पाँच सौ नहीं: अगर हर test अपने URLs, tokens और JSON खुद बनाता है, तो एक path rename करने का मतलब है उसे call करने वाला हर test बदलना। Maintainable suite काम को layers में बाँटता है, हर layer के बदलने की एक ही वजह होती है, और tests API तक सिर्फ़ नीचे की layers के ज़रिए पहुँचते हैं। कुछ teams client layer को service layer कहती हैं।
- Tests (
tests/): हर test में एक behaviour, test का नाम उसी behaviour पर, और tests resource के हिसाब से grouped। Test अपना input तैयार करता है, client को call करता है और result assert करता है। उसमें कोई URL, token या raw request नहीं होती। - API client (
api/): हर endpoint के लिए एक function, जैसे orders के लिएcreate()याget(id)। Paths, methods और default headers सिर्फ़ यहीं रहते हैं, इसलिए बदला हुआ path एक ही बार ठीक होता है। Client response लौटाता है और assert नहीं करता; क्या सही है यह test तय करता है। - Configuration (
config/): जिस environment पर run चल रहा है उसका base URL, credentials, timeouts और logging, जो environment variables से पढ़े जाते हैं, और framework का HTTP client इन्हीं से एक बार set up होता है। - Test data (
data/): builders जो default रूप से valid payload लौटाते हैं, जिसमें test सिर्फ़ ज़रूरी हिस्सा बदलता है, साथ में उन records के लिए setup और cleanup hooks जिनकी test को ज़रूरत है। - Shared checks (
schemas/): JSON Schemas और reusable assertions, ताकि order पाने वाला हर test उसका shape एक ही तरह जाँचे।
Calls ऊपर से नीचे जाती हैं
- 1Testsहर test में एक behaviour: input, call, assertions; क्या सही है यह test तय करता है
Requirement बदलने पर बदलती है
- 2API clientहर endpoint के लिए एक function, जैसे
create()औरget(id): paths, methods और default headers; कोई assertion नहींPath या parameter बदलने पर बदलती है: एक file, हर test नहीं
- 3HTTP client और configurationBase URL, token, timeouts और logging, जो environment variables से पढ़े जाते हैं
हर environment के साथ बदलती है, test code में कभी नहीं
- 4Test होने वाला APIएक environment में: local, test या staging; सिर्फ़ ऊपर की layers के ज़रिए पहुँचा जाता है
Tests से HTTP client और configuration तक:अनुमति नहीं: Test के अंदर URL, token या raw request
Call path के बगल में: data/ के builders और hooks हर test की ज़रूरत की चीज़ें बनाते और हटाते हैं, schemas/ के JSON Schemas सभी tests share करते हैं, और runner CI के लिए results/ में JUnit report लिखता है।
tests/
orders/
create-order.spec.ts one behaviour per test
order-validation.spec.ts data-driven invalid orders
customers/
api/
orders-client.ts create(), get(), remove(): paths live here
data/
order-builder.ts newOrder(): valid defaults, change what matters
schemas/
order.schema.json the shape of an order, shared by tests
config/
env.ts required(): stops when a variable is missing
playwright.config.ts runner, reporters, HTTP defaults
.github/workflows/api-tests.ymlAssertions, test data और environments
तीन फ़ैसले तय करते हैं कि team suite पर भरोसा करेगी या उसे नज़रअंदाज़ करेगी। हर एक वही लागू करता है जो आप manual API testing से जानते हैं, ऐसे नियमों के साथ जो तब मायने रखते हैं जब run को कोई देख नहीं रहा:
- Assertions: सबसे पहले exact status assert करें और failure message में response body जोड़ें, ताकि report खुद सब समझा दे। फिर वह assert करें जिसके लिए test बना है: आपके भेजे data के लिए exact values,
idऔरcreatedAtजैसी generated values के लिए type और format, और business rule। Structure को हर test में field checks दोहराने के बजाय एक shared JSON Schema से जाँचें। - पूरे response की stored copy से तुलना करने से बचें: हर harmless नया field test तोड़ देता है, और copy में record होते समय जो defect था वह उसमें बना रहता है।
- Data: हर test अपनी ज़रूरत का data खुद बनाता है, API या seed script से, unique values के साथ जैसे names और codes में run id, ताकि tests अकेले, किसी भी order में और parallel में चल सकें। Read-only reference data, जैसे पहले से seeded test customer, share हो सकता है; जिस data को कोई test बदलता है वह नहीं।
- Cleanup: test ने जो बनाया उसे ऐसे after-hook में delete करें जो test fail होने पर भी चलता है, या ऐसे environment पर चलाएँ जो हर run के लिए दोबारा बनता है।
- Data-driven tests: negative API testing वाले lesson की invalid classes और boundary values एक table की rows बन जाती हैं, हर row अपने input और expected result के साथ, और एक ही test body उन्हें चलाती है।
- Environments: base URL, credentials और timeouts configuration से आते हैं, test code से कभी नहीं, ताकि वही suite laptop पर, CI में और staging के सामने चल सके। Secrets CI के secret store से या ऐसी local file से आते हैं जो कभी commit नहीं होती। Data बनाने या delete करने वाले tests production के सामने कभी नहीं चलते। नीचे का code configuration layer दिखाता है: एक helper जो कोई variable missing होने पर run रोक देता है, और Playwright configuration जो उसे इस्तेमाल करती है।
// config/env.ts: stop at once when the environment is not configured
export function required(name: string): string {
const value = process.env[name];
if (!value) throw new Error(`Missing environment variable ${name}`);
return value;
}
// playwright.config.ts
import { defineConfig } from '@playwright/test';
import { required } from './config/env';
export default defineConfig({
testDir: './tests',
fullyParallel: true,
reporter: [['list'], ['junit', { outputFile: 'results/api-junit.xml' }]],
use: {
baseURL: required('API_BASE_URL'), // local, test or staging
extraHTTPHeaders: { Authorization: `Bearer ${required('API_TOKEN')}` },
},
});आज़माएँ: API स्कीमा वैलिडेटर आज़माएँ: API टेस्ट डेटा जेनरेटर
Worked example: नए order के checks को automate करना
POST /api/v1/orders का contract वही है जो API response validation वाले lesson में था: 201 Created, order के path वाला Location header, status pending, और total बराबर qty × unitPrice का जोड़। Contract validation rules भी जोड़ता है: order में कम से कम एक item हो, qty 1 से 99 तक का integer हो और हर sku मौजूद हो; invalid order 422 लौटाता है, एक errors array के साथ जिसकी field values हर invalid field का नाम बताती हैं, जैसे items[0].qty। ऊपर की layers के साथ Playwright Test में लिखा गया, इस section के आख़िर का code orders client, data builder और दो test files दिखाता है, जिनमें एक positive test और पाँच data-driven negative tests हैं। नीचे के notes बताते हैं कि यह code क्या करता है:
- हर test एक behaviour जाँचता है और उसका title वही बताता है, इसलिए report में ही टूटे हुए rule का नाम आ जाता है।
expect(res.status(), await res.text())का दूसरा argument failure message है, इसलिए गलत status response body के साथ report होता है।toMatchObjectतब pass होता है जब order में बताए गए fields इन्हीं values के साथ हों, और बाकी fields को वह नज़रअंदाज़ करता है। - Generated values relationship से जाँची जाती हैं:
Locationको नएidसे बने order path के बराबर होना चाहिए, और expected total response के items से calculate होकर number के रूप में दो decimal places तक compare होता है। GETसे order दोबारा पढ़ना साबित करता है कि वह store हुआ, सिर्फ़ echo नहीं हुआ।- Negative rows एक ही test body share करती हैं; नई boundary value जोड़ना बस एक line है, और हर row report में अलग test के रूप में दिखती है।
qty 0औरqty 100वाली rows 1 से 99 के ठीक बाहर हैं।errors.map(…)error objects की list को उनकीfieldvalues की list में बदलता है, औरtoContainजाँचता है कि expected field उनमें है। - Layers:
OrdersApiएक class है, यानी objects बनाने का template।new OrdersApi(request)एक orders client बनाता है जोrequestfixture अपने पास रखता है; उसकेcreate(),get()औरremove()में paths रहते हैं और वे बिना assert किए response लौटाते हैं।newOrder()customer 7 के लिए valid order लौटाता है, जो एक seeded customer है जिसे कोई test नहीं बदलता, और...changestest के दिए fields को इन default values के ऊपर copy करता है। - Cleanup: हर test अपने बनाए orders के ids दर्ज करता है, और
test.afterEachउन्हें delete करता है। यह एक hook है जो file के हर test के बाद चलता है, test fail होने पर भी, इसलिए tests अकेले, किसी भी order में और parallel में चल सकते हैं। बड़ा suite ऐसे hooks कोdata/में builders के पास ले जाता है, ताकि हर test file उन्हें share करे।
// api/orders-client.ts: the only file that knows the order paths
import type { APIRequestContext } from '@playwright/test';
export class OrdersApi {
constructor(private request: APIRequestContext) {}
create(order: object) { return this.request.post('/api/v1/orders', { data: order }); }
get(id: number) { return this.request.get(`/api/v1/orders/${id}`); }
remove(id: number) { return this.request.delete(`/api/v1/orders/${id}`); }
}
// data/order-builder.ts: a valid order; a test changes only what it is about
export function newOrder(changes: object = {}) {
return { customerId: 7, items: [{ sku: 'A-100', qty: 1 }], ...changes };
}
// tests/orders/create-order.spec.ts
import { test, expect } from '@playwright/test';
import { OrdersApi } from '../../api/orders-client';
import { newOrder } from '../../data/order-builder';
let created: number[] = []; // ids of the orders this test created
test.afterEach(async ({ request }) => { // runs after every test, even a failed one
for (const id of created) await new OrdersApi(request).remove(id);
created = [];
});
test('creates a pending order with the correct total', async ({ request }) => {
const orders = new OrdersApi(request);
const input = newOrder({
items: [{ sku: 'A-100', qty: 2 }, { sku: 'B-200', qty: 1 }],
});
const res = await orders.create(input);
expect(res.status(), await res.text()).toBe(201); // body shown on failure
const order = await res.json();
created.push(order.id); // for the after-hook
expect(res.headers()['location']).toBe(`/api/v1/orders/${order.id}`);
expect(order).toMatchObject({ customerId: input.customerId, status: 'pending' });
let sum = 0;
for (const item of order.items) sum = sum + item.qty * item.unitPrice;
expect(order.total).toBeCloseTo(sum, 2);
const stored = await orders.get(order.id); // stored, not only echoed
expect(stored.status()).toBe(200);
expect(await stored.json()).toMatchObject({ id: order.id, total: order.total });
});
// tests/orders/order-validation.spec.ts (same imports)
const invalid = [
{ name: 'qty 0', items: [{ sku: 'A-100', qty: 0 }], field: 'items[0].qty' },
{ name: 'qty 100', items: [{ sku: 'A-100', qty: 100 }], field: 'items[0].qty' },
{ name: 'qty as text', items: [{ sku: 'A-100', qty: '2' }], field: 'items[0].qty' },
{ name: 'unknown sku', items: [{ sku: 'Z-999', qty: 1 }], field: 'items[0].sku' },
{ name: 'no items', items: [], field: 'items' },
];
for (const row of invalid) {
test(`rejects an order with ${row.name}`, async ({ request }) => {
const res = await new OrdersApi(request).create(newOrder({ items: row.items }));
expect(res.status(), await res.text()).toBe(422);
const fields = (await res.json()).errors.map((e: { field: string }) => e.field);
expect(fields).toContain(row.field);
});
}CI में suite चलाना
Automated suite का फ़ायदा तब मिलता है जब वह किसी के शुरू किए बिना चलता है। CI pipeline में API tests एक job होते हैं: code checkout करना, dependencies install करना, variables और secrets के ज़रिए suite को किसी environment की ओर point करना, उसे चलाना, results publish करना और कोई test fail हो तो pipeline को fail करना। नीचे का workflow यह job GitHub Actions syntax में दिखाता है; GitLab CI और Jenkins वही steps अपनी files में लिखते हैं। ज़्यादातर teams API tests कई जगह चलाती हैं:
- हर pull request पर: suite का तेज़ core हिस्सा, किसी test environment या job के अंदर शुरू की गई service के सामने, ताकि author merge से पहले failure देख ले।
- Main branch में merge के बाद: shared test environment के सामने पूरा regression suite।
- हर deployment के बाद: deployed environment के सामने छोटा read-only smoke set, जैसे health, authentication और हर मुख्य resource का एक read।
- Schedule पर, जैसे हर रात: धीमे tests और third-party sandboxes के सामने चलने वाले tests, जिन्हें हर merge को नहीं रोकना चाहिए।
- हर run JUnit report upload करता है, tests fail होने पर भी (
if: always()), और logs job के साथ रहते हैं, ताकि failure को दोबारा चलाए बिना diagnose किया जा सके। - Red run merge या deployment रोक देता है; GitHub पर इसके लिए job को किसी branch protection rule या ruleset में required status check बनाना होता है। Flaky test को एक owner मिलता है और वह ठीक या quarantine किया जाता है, green होने तक retry नहीं।
# .github/workflows/api-tests.yml
name: api-tests
on:
pull_request:
push:
branches: [main]
jobs:
api-tests:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: 24
- run: npm ci
- run: npx playwright test
env:
API_BASE_URL: ${{ vars.API_BASE_URL }} # configuration variable
API_TOKEN: ${{ secrets.API_TOKEN }} # from the secret store
- uses: actions/upload-artifact@v7
if: always() # failures need evidence
with:
name: api-test-results
path: results/आम गलतियाँ
- Test cases की जगह requests automate करना: जो script request भेजकर सिर्फ़
200जाँचे वह run time बढ़ाती है, coverage नहीं। - एक-दूसरे पर निर्भर tests, जैसे delete test जो सिर्फ़ create test चलने के बाद काम करे। ये अकेले, दूसरे order में या parallel में चलने पर fail होते हैं।
- Tests में base URLs, tokens और ids hard-code करना, जिससे suite सिर्फ़ एक machine पर चले और secrets repository में पहुँच जाएँ।
- ऐसा data share करना जिसे tests बदलते हैं: दो tests एक ही customer edit करते हैं, और result इस पर निर्भर करता है कि पहले कौन चला।
- Generated fields की exact values assert करना या पूरे responses compare करना, जिससे हर harmless change के बाद tests fail हों और team red runs को नज़रअंदाज़ करना सीख जाए।
- Failed tests को pass होने तक दोबारा चलाना। Retry flaky tests और असली intermittent defects दोनों को छिपा देता है; जाँच करें, फिर owner के साथ ठीक या quarantine करें।
QA का नज़रिया
कई teams में API suite क्या जाँचता है इसके owner testers होते हैं, भले ही code का कुछ हिस्सा developers लिखें। Refinement में तय करें कि story के कौन-से checks API level पर automate होंगे और कौन-से manual या exploratory रहेंगे। API client और data builders developers के साथ मिलकर बनाएँ, क्योंकि वे endpoints और seed data जानते हैं, और pull requests में test code का review production code की तरह करें: क्या हर test का एक साफ़ behaviour, असली oracle और अपना data है?
हर red run को जानकारी मानें और उसका triage करें: product defect, test defect, या environment और data की समस्या। Product defect के लिए bug report लिखें जिसमें CI report से लिया गया request, response और failed assertion हो; test defect suite में ठीक होता है; environment की समस्या उसके owner के पास जाती है। देखते रहें कि suite कितना समय लेता है और product defect के बिना कितनी बार fail होता है। इनमें से कोई भी बढ़े तो भरोसा घटता है, और उसे ठीक करना काम का हिस्सा है।
अभ्यास
Coupons API के लिए automated suite की योजना बनाएँ
एक team shop में discount coupons जोड़ रही है। Rules: percent 1 से 50 तक का whole number है; coupon उसकी validUntil तारीख़ ख़त्म होने तक लगाया जा सकता है; हर customer एक code सिर्फ़ एक बार इस्तेमाल कर सकता है। दो environments हैं: test, जो हर रात reset होता है, और staging, जो manual testers के साथ shared है। नीचे दिए contract excerpt के आधार पर इस API के automated tests की योजना बनाएँ।
Pen और paper काफ़ी है। आप REST API Tester में requests आज़मा सकते हैं और API Test Data Generator से invalid payloads का draft बना सकते हैं।
POST /api/v1/coupons create a coupon (admin token)
{"code": "EID15", "percent": 15, "validUntil": "2027-03-31"}
201 coupon | 401 | 403 | 409 code exists | 422 errors[].field
GET /api/v1/coupons/{code} 200 coupon | 404
DELETE /api/v1/coupons/{code} 204 (admin token)
POST /api/v1/orders/{id}/coupon apply a coupon (customer token)
{"code": "EID15"}
200 order with discount | 404 unknown code | 422 expired or already used- Tests की list बनाएँ, हर एक में एक behaviour: positive, data-driven negative और business rules।
- तय करें कि हर test कौन-सा data बनाता है, codes unique कैसे रहते हैं और data का cleanup कैसे होता है।
- Folders और client functions का sketch बनाएँ।
- Suite को जिन configuration values और secrets की ज़रूरत है उन्हें लिखें, और यह भी कि हर हिस्सा CI में कब चलता है।
मॉडल उत्तर दिखाएँ
Positive: coupon बनाना → 201, फिर GET वही code, percent और validUntil लौटाता है। Boundaries: percent 1 और 50 → 201। नए order पर 15% वाला valid coupon लगाना → 200, और discount order total का 15% है, जिसे test response से calculate करता है।
Data-driven 422 rows, हर row errors में बताया गया field assert करती है: percent 0, 51, 15.5 और "15"; code missing; validUntil 2027-02-30। दूसरे negative tests: वही code दो बार → 409; unknown code → GET और apply दोनों पर 404; बिना token create → 401, customer token से → 403।
Business rules: एक ही customer के लिए वही code दूसरी बार लगाना → 422। Expiry एक date boundary है: जिस coupon की validUntil आज है वह अब भी लगता है → 200, और जिसकी validUntil कल थी → 422। अगर create endpoint पिछली तारीख़ें reject करता है, तो developers से seed script या control की जा सकने वाली test clock माँगें। यह खुला सवाल जल्दी उठाएँ, और साथ में यह भी पूछें कि "तारीख़ का अंत" किस time zone में गिना जाता है।
Data: हर test unique code वाला अपना coupon बनाता है, जैसे AT- के बाद run id और एक counter, और after-hook में DELETE से उसे हटाता है; apply tests के बनाए orders भी इसी तरह हटते हैं, और test का nightly reset किसी crashed run का छोड़ा data साफ़ कर देता है। "एक customer, एक बार" वाला rule हर customer और code के जोड़े पर गिना जाता है, इसलिए unique codes के साथ सभी tests CUSTOMER_TOKEN वाले एक ही seeded test customer को share कर सकते हैं और फिर भी parallel में बिना टकराए चलते हैं।
Structure: tests/coupons/ (create, validation, apply), api/coupons-client.ts में create, get, apply और remove, data/coupon-builder.ts जो unique code वाला valid coupon लौटाता है, और schemas/coupon.schema.json। Configuration API_BASE_URL, ADMIN_TOKEN और CUSTOMER_TOKEN environment variables से पढ़ता है; CI में tokens secret store से आते हैं।
CI: हर pull request पर coupon tests (create, validation और apply) test के सामने, क्योंकि वे कम और तेज़ हैं; merge के बाद पूरा API regression suite, coupons समेत, test के सामने; staging पर deployment के बाद सिर्फ़ read-only smoke check, जैसे seeded coupon पर GET, क्योंकि staging manual testers के साथ shared है। हर run JUnit report upload करता है।
अक्सर पूछे जाने वाले सवाल
API automation के लिए पहले कौन-सा framework सीखूँ?
वही जो आपकी team इस्तेमाल करती है, क्योंकि आपको उसके tests पढ़ने और बढ़ाने होंगे। अगर चुनने की आज़ादी हो, तो product के developers की language के साथ चलें: JavaScript या TypeScript के लिए Playwright Test, Java के लिए REST Assured, Python के लिए requests के साथ pytest। Runners, assertions, fixtures और reports सब में एक जैसे काम करते हैं, इसलिए दूसरा framework सीखना कहीं ज़्यादा तेज़ होता है।
Automated API tests staging के सामने चलें या fresh environment के सामने?
दोनों, अलग-अलग मकसद से। हर run या हर pull request के लिए fresh environment isolated और दोहराए जा सकने वाले results देता है, क्योंकि उसका data कोई और नहीं बदलता। Staging जैसा shared environment realism जोड़ता है, जैसे असली configuration और असली dependencies, और deployment के बाद के smoke checks वहीं चलते हैं। Data बनाने और delete करने वाले tests को उन environments से दूर रखें जहाँ लोग हाथ से काम करते हैं, जब तक उनका data unique न हो और बाद में साफ़ न किया जाए।
क्या automated API tests manual API testing की जगह ले लेते हैं?
नहीं। वे जाने-पहचाने checks तेज़ी और भरोसे से दोहराते हैं। नए endpoint को explore करना, अस्पष्ट contract पर सवाल उठाना, यह परखना कि error message client developer की मदद करता है या नहीं, और ऐसे ideas test करना जिन्हें अभी किसी ने लिखा नहीं, इन सबके लिए इंसान चाहिए। Automation उसे इसके लिए ज़्यादा समय देता है।
हर pull request पर कितने API tests चलने चाहिए?
इतने कि बदले हुए endpoints का मुख्य behaviour और मुख्य error cases cover हों, और job इतना तेज़ रहे कि developers आराम से उसके result का इंतज़ार करें। धीमे tests और third-party sandboxes वाले tests को post-merge या scheduled runs में ले जाएँ।
संदर्भ
- Certified Tester Advanced Level Test Automation Engineering (CTAL-TAE) v2.0टेस्ट ऑटोमेशन समाधान और उनकी आर्किटेक्चर के डिज़ाइन, कार्यान्वयन और रखरखाव पर ISTQB का advanced पाठ्यक्रम।
- The Practical Test Pyramid (Ham Vocke)ऑटोमेटेड टेस्ट पोर्टफ़ोलियो में unit, integration, contract और UI टेस्ट का संतुलन कैसे रखें — व्यापक रूप से उद्धृत व्याख्या।
- Playwright DocumentationPlaywright का आधिकारिक दस्तावेज़ — Chromium, WebKit और Firefox चलाने वाला end-to-end टेस्ट फ़्रेमवर्क।
- GitHub Actions documentationGitHub Actions में workflows, jobs, runners और artifacts कैसे परिभाषित होते हैं — जहाँ हर push या pull request पर ऑटोमेटेड टेस्ट चलते हैं।
Mutqan tools से अभ्यास करें
method, हेडर और JSON बॉडी पर पूरे नियंत्रण के साथ REST endpoint टेस्ट करें, फिर स्टेटस, हेडर और JSON path पर assertion लगाएँ।
cURL, JavaScript fetch, axios, Python requests, Java, C#, Go और PHP में चलाने लायक़ REST API रिक्वेस्ट कोड बनाएँ।
OpenAPI / Swagger स्पेसिफ़िकेशन से संरचित टेस्ट-केस मैट्रिक्स (positive, negative, auth, boundary) बनाएँ।
स्कीमा या फ़ील्ड सूची से API टेस्टिंग के लिए रिक्वेस्ट बॉडी और डेटासेट बनाएँ, अमान्य वेरिएंट सहित।
API रिस्पॉन्स को JSON Schema या OpenAPI रिस्पॉन्स स्कीमा के आधार पर जाँचें।
YAML को एकसमान इंडेंटेशन और quoting के साथ फ़ॉर्मेट और वैलिडेट करें।
और गहराई से
इस topic में आपकी प्रगति
प्रगति ट्रैक करने के लिए साइन इन करें
Topics पूर्ण चिह्नित करें, जहाँ छोड़ा था वहीं से जारी रखें और specialisation परीक्षाएँ खोलें।