साइप्रेस
Cypress web applications की end-to-end testing के लिए एक open-source tool है। आप tests JavaScript या TypeScript में लिखते हैं, और Cypress उन्हें असली browser के अंदर, application under test के बगल में चलाता है। उदाहरण के लिए, cy.get("[data-cy=cart-count]").should("have.text", "3") cart badge ढूँढता है और तब तक जाँचता रहता है जब तक वह 3 न दिखाए या समय-सीमा ख़त्म न हो जाए।
यह क्यों ज़रूरी है
Tool का design तय करता है कि कौन-से tests आसान हैं और कौन-से तकलीफ़देह। Cypress queries और assertions को अपने-आप retry करता है और हर command को page के snapshot के साथ दिखाता है, इसलिए कई failures जल्दी समझ आते हैं। यही design सीमाएँ भी लाता है: हर test में एक tab, सिर्फ़ JavaScript, और ऐसे commands जो सामान्य JavaScript जैसे व्यवहार नहीं करते। इन्हें अनदेखा करेंगे तो fixed sleeps और गलत वजह से fail होने वाले tests लिखेंगे।
Product के लिए, हर pull request पर चलने वाला Cypress suite अहम journeys के regressions users से पहले पकड़ता है। आपके लिए, किसी मौजूदा Cypress suite को बढ़ाना JavaScript front end वाली किसी भी team में काम का skill है।
Cypress कैसे काम करता है: browser के अंदर, command queue के ज़रिए
Selenium (Java, Python, C#, JavaScript, Ruby) HTTP पर W3C WebDriver commands chromedriver या safaridriver जैसे driver को भेजता है; explicit waits आप लिखते हैं और windows के बीच handle से switch करते हैं। Playwright (TypeScript/JavaScript, Python, Java, .NET) Chromium, Firefox और WebKit को अपने ख़ुद के connection से चलाता है, auto-waiting actions और retry होने वाले assertions के साथ, और एक test में कई pages इस्तेमाल कर सकता है। दोनों tests को अलग process में चलाते हैं। Cypress आपकी spec को browser के अंदर, application के ही run loop में चलाता है; उसके पीछे एक Node.js process browser शुरू करता है और config file में defined वे Node tasks चलाता है जिन्हें test cy.task() से call करता है। Cypress network को भी control करता है, और इसी से cy.intercept() संभव होता है।
Selenium WebDriver
Test code browser के बाहर
- 1आपका test processHTTP पर W3C WebDriver commands
- 2Browser driver
chromedriver, safaridriver - 3Browser
- Test languages:
- Java, Python, C#, JavaScript, Ruby
- Waiting:
- Explicit waits जो आप लिखते हैं
- एक test में tabs:
- कई windows
- इस lesson में नामित browsers:
- Chrome, Safari और दूसरे, हर एक अपने driver से
Playwright
Test code browser के बाहर
- 1आपका test processPlaywright का अपना connection
- 2BrowserChromium, Firefox, WebKit
- Test languages:
- TypeScript/JavaScript, Python, Java, .NET
- Waiting:
- Auto-waiting actions, retry होने वाले assertions
- एक test में tabs:
- कई pages और browser contexts
- इस lesson में नामित browsers:
- Chromium, Firefox, WebKit
Cypressयह lesson
Test code browser के अंदर
- 1Cypress का Node.js processBrowser शुरू करता है,
cy.task()से call किए गए tasks चलाता है - 2Browser: app के बगल में आपकी specएक ही run loop; network Cypress के control में
- Test languages:
- JavaScript, TypeScript
- Waiting:
- Queries और assertions
defaultCommandTimeoutतक retry होते हैं - एक test में tabs:
- एक tab; दूसरे के लिए plugin
- इस lesson में नामित browsers:
- Chrome, Edge, Firefox
Commands queue में जाते हैं, अपनी line execute होते ही नहीं चलते: cy.visit(), cy.get() और .click() एक queue में जुड़ते हैं जिसे Cypress test function ख़त्म होने के बाद चलाता है। ये promises नहीं हैं, इसलिए आप इन्हें await नहीं कर सकते, न इनका result रख सकते हैं; इसकी जगह .then() या .should() chain करें।
मुख्य विचार retry-ability है: cy.get() और cy.contains() जैसी queries अपने chained assertions के साथ तब तक दोबारा चलती हैं जब तक assertions pass न हों या defaultCommandTimeout (default 4 seconds) ख़त्म न हो। .click() जैसे actions एक बार चलते हैं, element के actionable होने का इंतज़ार करने के बाद: visible, enabled और किसी चीज़ से ढका न हो। cy.wait(5000) जोड़ने की जगह अपेक्षित state assert करें।
cy.visit('/cart')
cy.get('[data-cy=add-item]').click() // action: waits until actionable, runs once
cy.get('[data-cy=cart-count]') // query: retried...
.should('have.text', '1') // ...until this passes or the timeout expires
// Wrong: commands are queued, they return no value you can store
// const count = cy.get('[data-cy=cart-count]').text()
// Right: work with the yielded value inside .then()
cy.get('[data-cy=cart-count]').invoke('text').then((text) => {
cy.log(`The badge shows ${text}`)
})Install, project layout और config file
Cypress को हर project में npm install --save-dev cypress से install करें; पहली बार npx cypress open चलाने पर Launchpad खुलता है, जहाँ E2E Testing चुनने से config file और cypress/ folder बन जाते हैं। नीचे की config file Node के require() और module.exports (CommonJS) इस्तेमाल करती है, जो import और export का पुराना समकक्ष है, क्योंकि उसे Node load करता है; spec, support और page object files फिर भी import और export इस्तेमाल कर सकती हैं, क्योंकि Cypress उन्हें browser में चलाने से पहले bundle करता है।
Config file में baseUrl cy.visit("/orders") को किसी environment की ओर point करता है; timeouts queries, page loads और responses का समय सीमित करते हैं; retries cypress run और cypress open के लिए अलग-अलग set होता है; reporter output चुनता है, जैसे CI के लिए JUnit XML। Browser run के समय --browser chrome, edge या firefox से चुना जाता है; flag न हो तो defaultBrowser वाला browser चलता है (वरना cypress run bundled Electron browser इस्तेमाल करता है, जो Cypress 16 से deprecated है), और browsers list को setupNodeEvents में filter किया जा सकता है।
cypress/e2e/:orders.cy.jsजैसी spec files।cypress/fixtures/:orders.jsonजैसा stub data, और upload करने वाली files।cypress/support/:e2e.jsहर spec से पहले चलती है औरcommands.jsimport करती है, जिसमेंcy.login()जैसे custom commands होते हैं; page objects अक्सर इसके बगल में रहते हैं।cypress/screenshots/औरcypress/downloads/: run का output, version control से बाहर।
// cypress.config.js
const { defineConfig } = require('cypress')
module.exports = defineConfig({
e2e: {
baseUrl: 'http://localhost:3000', // cy.visit('/orders') opens this host
specPattern: 'cypress/e2e/**/*.cy.js',
},
defaultBrowser: 'chrome', // used when --browser is not given
defaultCommandTimeout: 6000, // queries and assertions (default 4000)
pageLoadTimeout: 60000, // page loads after cy.visit()
responseTimeout: 30000, // cy.request() and cy.wait() responses
retries: { runMode: 1, openMode: 0 }, // one rerun in cypress run only
reporter: 'junit',
reporterOptions: { mochaFile: 'results/junit-[hash].xml' },
screenshotOnRunFailure: true,
video: false,
})एक test चलाएँ, debug करें, और suite को CI में चलाएँ
लिखते समय npx cypress open इस्तेमाल करें: browser और spec चुनें, और हर save पर spec फिर से चलती है। Command Log में किसी command पर click करने से page वैसा दिखता है जैसा वह उस पल था (time travel)। Command line और CI में:
- एक spec:
npx cypress run --spec cypress/e2e/orders.cy.js --browser chrome। - एक test: काम करते समय
it.only(; commit से पहले हटा दें। - Headed mode:
cypress runbrowser छिपाता है; run देखने के लिए--headedजोड़ें। - Debugging:
cy.pause()open runner में queue रोकता है,.debug()DevTools खुले हों तो breakpoint पर रुकता है, औरDEBUG=cypress:*(Linux और macOS syntax) Cypress के अपने logs छापता है जब वह start न हो या browser launch न कर पाए। - CI: official image
cypress/browsersमें Node.js और browsers हैं (cypress/includedउसमें एक fixed Cypress version जोड़ती है)। नीचे का job सारी specs Chrome में चलाता है, password repository secrets से लेता है और JUnit results व failure screenshots रखता है।
# .github/workflows/e2e.yml
name: e2e
on: [pull_request]
jobs:
cypress:
runs-on: ubuntu-latest
container:
image: cypress/browsers:latest # pin an exact tag in a real pipeline
steps:
- uses: actions/checkout@v7
- run: npm ci
- run: npx cypress run --browser chrome --config baseUrl=https://test.example.com
env:
CYPRESS_BUYER_PASSWORD: ${{ secrets.BUYER_PASSWORD }}
- uses: actions/upload-artifact@v7
if: always()
with:
name: cypress-results
path: |
results
cypress/screenshotsSessions, network stubs, dialogs, tabs और file upload
Test isolation default रूप से on है: हर test खाली page से, बिना cookies या storage के शुरू होता है, इसलिए हर test को login करना पड़ता है। cy.session() login एक बार चलाता है, बनी cookies और storage cache करता है, और उसी id वाले बाद के tests में restore करता है; validate जाँचता है कि restore हुई session अब भी काम करती है। उसके बाद कोई page visit करें, क्योंकि page खाली होता है।
cy.intercept() requests को method और URL से match करता है। { fixture: "orders.json" } या { statusCode: 500 } जैसे static response के साथ वह server को stub करता है, जिससे empty states और errors जब चाहें मिल जाते हैं; उसके बिना वह सिर्फ़ देखता है, और cy.wait("@alias") request का इंतज़ार करता है ताकि आप उस पर assert कर सकें।
- Dialogs: Cypress
alert()औरconfirm()को अपने-आप accept करता है।cy.on("window:alert", …)को एक stub दें, यानी असली function की जगह लेने वाला function जो अपनी calls record करता है, और उसके alias पर assert करें, ताकि alert न आने पर भी test fail हो;falseलौटाने वालाwindow:confirmstub Cancel चुनता है। - नए tabs: Cypress एक tab चलाता है।
target="_blank"वाले link काhrefऔरtargetजाँचें, फिर URL उसी tab में visit करें या click से पहलेtargetहटा दें; दूसरा tab ज़रूरी ही हो तो documentation@cypress/puppeteerplugin बताता है। - Upload: file input पर
.selectFile("cypress/fixtures/receipt.pdf"); drop zone के लिए{ action: "drag-drop" }जोड़ें।
// cypress/support/commands.js: sign in once per user, reuse the session
Cypress.Commands.add('login', (email) => {
cy.session(email, () => {
cy.env(['BUYER_PASSWORD']).then(({ BUYER_PASSWORD }) => {
cy.request({
method: 'POST',
url: '/api/login',
body: { email, password: BUYER_PASSWORD },
log: false, // keep the password out of the log
})
})
}, {
validate() {
cy.request('/api/me').its('status').should('eq', 200)
},
})
})
// In a spec: watch a request, check an alert, upload a file
cy.login('buyer@example.com')
cy.intercept('POST', '/api/receipts').as('upload') // watch only, no stub
cy.on('window:alert', cy.stub().as('alert')) // record every alert
cy.visit('/account/receipts')
cy.get('[data-cy=receipt-file]').selectFile('cypress/fixtures/receipt.pdf')
cy.get('[data-cy=upload]').click()
cy.wait('@upload').its('response.statusCode').should('eq', 201)
cy.get('@alert').should('have.been.calledOnceWith', 'Receipt uploaded')आज़माएँ: Mock API रिस्पॉन्स जनरेटर आज़माएँ: Mock एरर रिस्पॉन्स जनरेटर
Worked example: चार tests और एक page object में order history
पहला Cypress test अक्सर एक ही it में login करता है, list जाँचता है, order cancel करता है और error जाँचता है। कोई शुरुआती step fail हो तो बाद की जाँचें चलती ही नहीं। इसे behaviour के हिसाब से बाँटें।
Requirement: “Order history page buyer के orders दिखाता है। कोई order न हो तो You have no orders yet दिखाता है; loading fail हो तो Could not load your orders। Cancel करने पर confirmation माँगा जाता है; Cancel चुनने पर order बना रहता है।”
Page object में selectors हैं और कोई assertion नहीं। Fixture orders.json में तीन orders हैं; A-1002 का status Processing है। हर test अपना data ख़ुद stub करता है, cached session reuse करता है और एक behaviour जाँचता है, इसलिए fail हुए test का title बताता है कि क्या टूटा।
// cypress/support/pages/ordersPage.js: knows the screen, makes no assertions
export const ordersPage = {
visit: () => cy.visit('/account/orders'),
rows: () => cy.get('[data-cy=order-row]'),
row: (id) => cy.get(`[data-cy=order-row][data-order-id="${id}"]`),
message: () => cy.get('[data-cy=orders-message]'),
cancel: (id) => ordersPage.row(id).find('[data-cy=cancel-order]').click(),
}
// cypress/e2e/orders.cy.js: one behaviour per test
import { ordersPage } from '../support/pages/ordersPage'
describe('Order history', () => {
beforeEach(() => {
cy.login('buyer@example.com')
})
it('lists the orders returned by the API', () => {
cy.intercept('GET', '/api/orders', { fixture: 'orders.json' }).as('orders')
ordersPage.visit()
cy.wait('@orders')
ordersPage.rows().should('have.length', 3)
})
it('shows the empty state when there are no orders', () => {
cy.intercept('GET', '/api/orders', { body: [] })
ordersPage.visit()
ordersPage.message().should('have.text', 'You have no orders yet')
})
it('shows an error when the orders cannot be loaded', () => {
cy.intercept('GET', '/api/orders', { statusCode: 500, body: {} })
ordersPage.visit()
ordersPage.message().should('have.text', 'Could not load your orders')
})
it('keeps the order when the buyer cancels the confirmation', () => {
cy.intercept('GET', '/api/orders', { fixture: 'orders.json' })
cy.on('window:confirm', cy.stub().as('confirm').returns(false)) // choose Cancel
ordersPage.visit()
ordersPage.cancel('A-1002')
cy.get('@confirm').should('have.been.calledOnceWith', 'Cancel order A-1002?')
ordersPage.row('A-1002').should('contain', 'Processing')
})
})आम गलतियाँ
- अपेक्षित state assert करने या alias वाली request का इंतज़ार करने की जगह
cy.wait(5000)जोड़ना। - Commands को सामान्य JavaScript मानना:
cy.get()को variable में रखना,awaitलगाना, या ऐसी state परifसे branch करना जहाँ queue अभी पहुँची ही नहीं। cy.session()की जगह हर test में UI से login करना।- हर request stub करना, जिससे front end और API असहमत हों तब भी suite हरा रहे।
- Flaky tests छिपाने के लिए
retriesइस्तेमाल करना; retry containment है, इलाज नहीं। it.onlycommit करना, जो spec के बाकी tests चुपचाप skip कर देता है, या CI secrets की जगह असली passwords।
QA का नज़रिया
Tester Cypress से तब मिलता है जब वह चुनता है कि किन journeys को end-to-end test चाहिए (कुछ, और क़ीमती; बाकी pyramid में नीचे), जब developers के साथ specs लिखता और review करता है, और जब CI failures पढ़ता है। data-cy attributes पर जल्दी सहमति बनाएँ, जैसा Cypress की best-practices guide सुझाती है।
CI में test fail हो तो error message और failure screenshot से शुरू करें, और rerun से पहले तय करें कि यह product defect है, test defect है या environment की समस्या। Bug report में spec, test, browser, Cypress version और build लिखें, और बताएँ कि data stubbed था या नहीं।
अभ्यास
Coupon field के लिए Cypress spec plan करें
किसी coupon से पहले cart total 200.00 SAR है। Requirement: “Valid code 10% discount लगाता है और Coupon applied दिखाता है। Expired code This coupon has expired दिखाता है। Coupon service fail हो तो page Coupons are unavailable, try again later दिखाता है और total नहीं बदलता।” Page नीचे दी गई request भेजता है।
Cart का page object और हर behaviour के लिए एक test design करें, हर test cy.intercept() से अपना response stub करे। काग़ज़ काफ़ी है।
POST /api/coupons/apply
Content-Type: application/json
{"code": "SAVE10"}
Possible responses
200 OK {"code": "SAVE10", "discountPercent": 10, "total": 180.00}
422 Unprocessable Content {"error": "COUPON_EXPIRED"}
503 Service Unavailable {"error": "SERVICE_UNAVAILABLE"}- Behaviours और हर एक के लिए ज़रूरी response लिखें।
- Page object लिखें: selectors और actions, कोई assertion नहीं।
- हर behaviour के लिए stub और assertions के साथ एक
itलिखें, total समेत। - लिखें कि stubs क्या साबित नहीं करते और आप उसे कैसे cover करेंगे।
मॉडल उत्तर दिखाएँ
Behaviours: valid code (200, total: 180.00), expired code (422) और service failure (503)। खाली code को stub नहीं चाहिए: Apply disabled रहे या validation message दिखे, जैसा team ने तय किया हो।
Page object: visit(), apply(code) ([data-cy=coupon-code] में type करे और [data-cy=apply-coupon] पर click करे), message() और total(); अंदर कोई should() नहीं।
Tests: (1) 200 को @apply नाम से stub करें, SAVE10 apply करें, और जाँचें कि cy.wait("@apply").its("request.body") { code: "SAVE10" } के बराबर है, message Coupon applied है और total 180.00 है। (2) 422 stub करें; This coupon has expired और 200.00 अपेक्षित। (3) 503 stub करें; Coupons are unavailable, try again later और 200.00 अपेक्षित।
Stubs यह साबित नहीं करते कि असली service SAVE10 स्वीकार करती है और 10% सही calculate करती है; एक API test और seeded coupon data के साथ एक end-to-end test जोड़ें। Product owner से पूछें: discount VAT और delivery से पहले लगता है या बाद में, और क्या coupons साथ लग सकते हैं?
अक्सर पूछे जाने वाले सवाल
Cypress सीखूँ या Playwright?
वह सीखें जो आपकी team इस्तेमाल करती है; locators, waits, assertions और page objects दोनों में काम आते हैं। Cypress उन teams के लिए ठीक है जो JavaScript या TypeScript लिखती हैं और उसका interactive runner पसंद करती हैं। Playwright ज़्यादा languages support करता है और एक test में कई pages और browser contexts चला सकता है, जो तब मायने रखता है जब product के flows कई tabs या windows में फैले हों।
Cypress commands के साथ async/await क्यों नहीं चलता?
क्योंकि commands queue में जाते हैं, अपनी line चलते ही execute नहीं होते, और वे promises नहीं हैं। Cypress test function ख़त्म होने के बाद queue चलाता है और साथ-साथ queries retry करता है। मिली value के साथ काम करने के लिए .then(), उसे दोबारा इस्तेमाल करने के लिए aliases, और retries के साथ assert करने के लिए .should() इस्तेमाल करें।
क्या Cypress APIs test कर सकता है?
cy.request() test से HTTP requests भेजता है। यह setup के लिए उपयोगी है, जैसे data बनाना या login करना, और UI tests के बगल में कुछ API checks के लिए भी। फिर भी Cypress को चलने के लिए browser चाहिए, इसलिए बड़ा API suite आम तौर पर किसी dedicated API testing tool या library में आसान होता है।
क्या retries on करूँ?
cypress run में एक या दो retries pipeline को तब तक उपयोगी रख सकते हैं जब तक flaky test ठीक हो रहा है, लेकिन retry के बाद मिला हर pass report में दिखना चाहिए और test या product के defect की तरह treat होना चाहिए। openMode retries 0 रखें ताकि tests लिखते समय flakiness दिखे।
संदर्भ
- Cypress DocumentationCypress से ब्राउज़र में एंड-टू-एंड और कंपोनेंट टेस्टिंग का आधिकारिक दस्तावेज़।
- Best Practices — Cypressसमर्पित एट्रिब्यूट से एलिमेंट चुनने, फ़िक्स्ड wait से बचने और टेस्ट को स्वतंत्र रखने पर Cypress की गाइडेंस।
- GitHub Actions documentationGitHub Actions में workflows, jobs, runners और artifacts कैसे परिभाषित होते हैं — जहाँ हर push या pull request पर ऑटोमेटेड टेस्ट चलते हैं।
Mutqan tools से अभ्यास करें
success, list, detail, error और empty केस के लिए पूरे mock API रिस्पॉन्स (स्टेटस, हेडर, JSON बॉडी) बनाएँ।
लोकप्रिय फ़ॉर्मेट में संरचित एरर रिस्पॉन्स बनाएँ (validation, auth, not found, rate limit, सर्वर एरर)।
JSON सिंटैक्स वैलिडेट करें और गलतियों की सटीक जगह के साथ सुधार के उपयोगी सुझाव पाएँ।
YAML को एकसमान इंडेंटेशन और quoting के साथ फ़ॉर्मेट और वैलिडेट करें।
JavaScript और TypeScript कोड को एकसमान इंडेंटेशन और स्पेसिंग के साथ सुंदर बनाएँ।
और गहराई से
इस topic में आपकी प्रगति
प्रगति ट्रैक करने के लिए साइन इन करें
Topics पूर्ण चिह्नित करें, जहाँ छोड़ा था वहीं से जारी रखें और specialisation परीक्षाएँ खोलें।