Free tools Windows power users keep installed
One-click scans. No signup required.
Use cy.get() to collect elements identified by a stable test selector, choose a random zero-based index inside the yielded callback, and act on $items.eq(index). Record that index—or a seed used to generate it—so a failed run can be replayed.
The reliable random-selection pattern
Cypress does not provide a dedicated “random element” command. The dependable approach is ordinary JavaScript applied to Cypress’s yielded collection:
cy.get('[data-cy="menu-item"]')
.should('have.length.greaterThan', 0)
.then(($items) => {
const index = Math.floor(Math.random() * $items.length)
cy.log(`Random menu item index: ${index}`)
cy.wrap($items.eq(index)).click()
})
cy.get() queries the DOM and retries until its subject exists; chained assertions are retried as well. The .eq() documentation describes selecting an item by index. The random calculation belongs inside .then(), after Cypress has yielded the actual collection.
Build a stable candidate set first
Randomness cannot compensate for an unstable selector. Give every candidate a test-specific attribute such as data-cy or data-testid:
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →#1 Best Overall
<button data-cy="menu-item">Reports</button>
<button data-cy="menu-item">Billing</button>
<button data-cy="menu-item">Settings</button>
Cypress recommends dedicated data-* attributes because they are decoupled from CSS styling and JavaScript behavior. A selector such as .card:nth-child(3) can change when the layout changes, while [data-cy="menu-item"] expresses the test’s intent.
Keep the selector narrow enough that every match is a valid target. If disabled, hidden, or decorative nodes are possible, filter them explicitly before selecting:
cy.get('[data-cy="menu-item"]:visible:not([disabled])')
.should('have.length.greaterThan', 0)
.then(($items) => {
const index = Math.floor(Math.random() * $items.length)
cy.wrap($items.eq(index)).click()
})
Add an explicit empty-set diagnostic
Without an assertion, an empty collection can make the failure appear to be a random-index problem. Assert the precondition before calculating the index:
cy.get('[data-cy="product-card"]')
.should('have.length.greaterThan', 0)
.then(($cards) => {
const index = Math.floor(Math.random() * $cards.length)
cy.log(`Selected product card ${index + 1} of ${$cards.length}`)
cy.wrap($cards.eq(index)).click()
})
Math.floor(Math.random() * length) produces an integer from 0 through length - 1. Using length itself would be out of range because DOM collections are zero-indexed.
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallOutdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchRank #2
Make random failures reproducible
Unrecorded randomness is difficult to debug. At minimum, log the selected index and collection size with cy.log(). The index identifies the candidate only if the collection order is stable; log a meaningful attribute as well when possible:
cy.get('[data-cy="menu-item"]')
.should('have.length.greaterThan', 0)
.then(($items) => {
const index = Math.floor(Math.random() * $items.length)
const label = $items.eq(index).text().trim()
cy.log(`Selected index=${index}, label=${label}`)
cy.wrap($items.eq(index)).click()
})
For repeatable CI runs, replace Math.random() with a small seeded generator owned by your test utilities. Cypress does not prescribe a seed API; this is general JavaScript test design. One simple generator is:
function seededRandom(seed) {
let value = seed % 2147483647
if (value <= 0) value += 2147483646
return () => {
value = value * 16807 % 2147483647
return (value - 1) / 2147483646
}
}
const seed = Number(Cypress.env('RANDOM_SEED') || 20260929)
const random = seededRandom(seed)
cy.get('[data-cy="menu-item"]')
.should('have.length.greaterThan', 0)
.then(($items) => {
const index = Math.floor(random() * $items.length)
cy.log(`seed=${seed}, index=${index}`)
cy.wrap($items.eq(index)).click()
})
Set RANDOM_SEED in the CI job or on the command line, and print it in the test output. When a run fails, rerun with the same seed and the same application data. If the page’s candidate order changes between runs, a seed alone cannot restore the same element; log a stable identifier and make fixture data deterministic too.
Respect Cypress’s command queue
Cypress commands are queued and run serially. The official introduction describes this design as intended to produce consistent, non-flaky tests. Do not assign the result of cy.get() to a normal variable and expect a synchronous array:
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Clear out junk files and repair common Windows errors3Fix the driver behind crashes, sound loss and screen glitchesRank #3
// Wrong: cy.get() does not return a synchronous array.
const items = cy.get('[data-cy="menu-item"]')
const index = Math.floor(Math.random() * items.length)
Use .then(), a callback assertion, or another command that receives the yielded subject. Wrapping the selected jQuery element with cy.wrap() puts the click back into Cypress’s queue, preserving command logging, retries, and error reporting.
Re-query after a re-render
The jQuery object yielded by cy.get() represents the elements found at that point in time. If clicking one item causes React, Vue, Angular, or another framework to replace the list, an old reference can become stale. Query again for the next action:
cy.get('[data-cy="menu-item"]')
.should('have.length.greaterThan', 0)
.then(($items) => {
const index = Math.floor(Math.random() * $items.length)
cy.wrap($items.eq(index)).click()
})
// The page may have rendered a new list after the click.
cy.get('[data-cy="menu-panel"]')
.should('be.visible')
.within(() => {
cy.get('[data-cy="close-panel"]').click()
})
If you need to select another item after the first action, perform a fresh cy.get() and choose again from the new collection. This costs another query but avoids acting on detached DOM nodes.
Random coverage versus exhaustive coverage
| Goal | Recommended strategy | What one run proves |
|---|---|---|
| Exercise varied user paths | Choose one random candidate per run | Only the selected candidate was exercised |
| Verify every candidate on every run | Use a deterministic loop or separate tests | Each candidate receives an assertion |
| Find intermittent interaction defects | Use a logged seed and repeated runs | Different candidates can be explored while failures remain replayable |
| Validate ordering or accessibility rules | Use deterministic indexes and explicit assertions | The expected element and state are checked consistently |
Random selection is sampling, not coverage. With ten candidates, one execution checks one of them, not all ten. If every candidate matters, write a deterministic test for each or iterate through a known fixture and assert each result.
Rank #4
Use cy.each() for iteration, not random selection
cy.each() is designed to iterate over a collection. It yields the original collection, and assertions chained after .each() are not retried for every item. It is therefore not a replacement for choosing a random index:
cy.get('[data-cy="menu-item"]').each(($item) => {
cy.wrap($item).should('be.visible')
})
Use this form when you intentionally want to inspect every item. If an iteration action changes the page, re-query the collection for the next operation rather than relying on elements captured before the re-render.
Useful variations
Pick a random option in a form
cy.get('[data-cy="plan-option"]')
.filter(':visible')
.should('have.length.greaterThan', 0)
.then(($options) => {
const index = Math.floor(Math.random() * $options.length)
cy.wrap($options.eq(index)).click()
})
Select a random card, then assert its details
cy.get('[data-cy="article-card"]')
.should('have.length.greaterThan', 0)
.then(($cards) => {
const index = Math.floor(Math.random() * $cards.length)
const slug = $cards.eq(index).attr('data-slug')
cy.log(`Opening article slug=${slug}`)
cy.wrap($cards.eq(index)).click()
})
cy.get('[data-cy="article-title"]').should('be.visible')
Choose from a known subset
cy.get('[data-cy="row"]')
.filter('[data-status="ready"]')
.should('have.length.greaterThan', 0)
.then(($rows) => {
const index = Math.floor(Math.random() * $rows.length)
cy.wrap($rows.eq(index)).click()
})
Troubleshooting random-selection failures
| Symptom | Likely cause | Fix |
|---|---|---|
| “Expected to find element” | The selector matched nothing before the timeout. | Verify the route and fixture data, wait for the page’s ready state, and keep the explicit length assertion so the missing precondition is clear. |
| Index is undefined or out of range | The collection is empty, or the index was calculated before Cypress yielded the collection. | Calculate inside .then(($items) and assert a positive length first. |
| Element is detached from the DOM | A framework re-render replaced the node after it was queried. | Query again immediately before the next action; do not retain a jQuery reference across a state-changing command. |
| The same item appears repeatedly | Randomness allows repeats, especially with a small candidate set. | Use a deterministic sequence or shuffle a copied list when each candidate must be visited once. |
| CI failure cannot be reproduced | The run used unrecorded randomness or unstable data ordering. | Log the seed, index, collection size, and a stable identifier; rerun with the same seed and fixtures. |
Assertions after cy.each() behave unexpectedly |
.each() yields the original subject and does not retry per-item assertions. |
Put the assertion inside the callback or use a different deterministic test structure. |
Performance and reliability considerations
- A random pick still queries the entire matching collection, so a broad selector can be slower than a narrow test attribute.
- Filtering in the selector reduces accidental matches and avoids choosing hidden or disabled controls.
- Re-querying after a render adds commands but is safer than using detached elements.
- Run random tests repeatedly only when the extra exploration is intentional; otherwise, a fixed seed gives stable feedback.
- Do not use randomness to hide a timing problem. Wait for a specific selector, state, or assertion that represents readiness.
Cypress’s best-practices guidance favors selectors and tests that remain independent of presentation details. Its command model is built around a serial queue, so keeping selection and actions in that chain is more reliable than mixing Cypress commands with synchronous control flow.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Or skip the browser setup
If your goal is to capture a URL for a test artifact, visual regression input, or debugging note rather than interact with elements, ScreenshotNeo makes one HTTP request and returns a PNG, JPEG, WebP, or PDF. Its capture steps can accept cookie-consent banners and remove more than 60 known consent platforms, newsletter popups, and chat widgets before the shot. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status.
Use the ScreenshotNeo API documentation for authentication and options. A minimal cURL request is:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
The equivalent Python request:
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
And Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot failed: ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
ScreenshotNeo also provides an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots. Create a free ScreenshotNeo account to try it.
Frequently Asked Questions
Can random selection guarantee that every candidate is eventually tested?
No. Random runs can repeat candidates indefinitely. Use deterministic cases or a shuffle when complete coverage is required.
Where should a CI seed be recorded?
Store it as a CI environment variable, print it with the test result, and preserve it with the failure artifact so the run can be replayed.
When should a test avoid randomness entirely?
Avoid it for assertions about a specific element, ordering, accessibility requirement, or business rule that must be checked on every execution.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

