Cypress: Test Structure & Locators
- Radek Stolarczyk
- Jun 28
- 2 min read
Updated: Jul 9
Test Structure
Cypress organizes test files using a describe block to group related test cases, while each individual test is defined with an it block. This structure is similar to other JavaScript testing frameworks like Mocha or Jasmine. You can also nest describe blocks to create sub-groups of tests. Additionally, you can use hooks like before, beforeEach, after, and afterEach to set up preconditions or cleanups.
At the top of every Cypress test file, you often see the line:
This tells VS Code (or other editors) to recognize Cypress's types for better autocomplete and type checking.
Example Test Structure
In the example above, the nested describe block helps you logically group tests for a specific part of the user registration flow, making the suite easier to maintain.
Locator Strategies in Cypress
Locators are selectors Cypress uses to identify elements on a web page. Cypress relies on CSS selectors by default (no built-in XPath, though plugins exist). The best practice is to use dedicated attributes such as data-cy or data-test, because these are stable and less likely to change when the page design evolves.
Locator Best Practices
Use data-cy attributes wherever possible.They are designed for testing and will not change due to CSS or layout updates.
Avoid using styling classes as locators.CSS classes change frequently as styles evolve.
Prefer unique IDs if available.They guarantee stable targeting.
Use cy.contains() for text-based elementsGreat for buttons or links where text is consistent.
Avoid brittle selectors with long nested CSS chainsThey are fragile and break easily with UI refactors.
Use partial attribute matchesFor dynamic IDs or classes that change partly on every build.
Advanced Locator Techniques
When working with modern frameworks (like Wix, React, Angular), you’ll often see dynamic classes and IDs that change on every build. Instead of trying to match the full ID or class name, you can use partial attribute selectors:
Starts with (^=)
Targets any ID that starts with input_comp-, which is great for generated patterns.
Contains (*=)
Matches any class that contains a stable part of the class name, ignoring the random suffix.
Ends with ($=)
Targets elements whose IDs end with -field, helpful if you know a consistent ending pattern.
Additional Relationships and Hierarchical Locators
You can also combine partial selectors with hierarchical locators for more resilient tests:
Parent-child relationships
Look for an input within a known container.
Closest ancestor
Climb up to the nearest container, helpful if you want to verify validation errors.
Sibling relationships
Useful for targeting elements on the same horizontal level.
nth-child
If lists do not have unique identifiers, you can still grab a specific child.