Why Test Organization Matters
A well-organized test suite:
Makes tests easy to find and understand
Reduces duplication and maintenance overhead
Enables efficient test execution
Improves collaboration among team members
Scales as your application grows
Directory Structure
The framework follows a clear separation between test types:
cypress/
├── e2e/
│ ├── api/ # API tests
│ │ └── GETSpec.cy.ts
│ └── ui/ # UI tests
│ ├── LoginPageSpec.cy.ts
│ ├── HomePageSpec.cy.ts
│ └── CheckoutPageSpec.cy.ts
├── fixtures/
│ ├── users.json
│ ├── loginPageData.json
│ └── bookings.json
├── support/
│ ├── commands.ts
│ ├── e2e.ts
│ ├── pages/
│ │ ├── BasePage.ts
│ │ ├── LoginPage.ts
│ │ └── HomePage.ts
│ └── components/
│ └── Navbar.ts
Organizing UI Tests
By Feature or Page
Group tests by the feature or page they test:
cypress/e2e/ui/
├── authentication/
│ ├── LoginPageSpec.cy.ts
│ ├── SignupPageSpec.cy.ts
│ └── PasswordResetSpec.cy.ts
├── products/
│ ├── ProductListSpec.cy.ts
│ ├── ProductDetailSpec.cy.ts
│ └── ProductSearchSpec.cy.ts
└── checkout/
├── CartSpec.cy.ts
├── CheckoutSpec.cy.ts
└── PaymentSpec.cy.ts
By User Journey
Organize tests following user workflows:
cypress/e2e/ui/
├── guest-user/
│ ├── BrowseProductsSpec.cy.ts
│ └── CreateAccountSpec.cy.ts
├── registered-user/
│ ├── LoginSpec.cy.ts
│ ├── PurchaseSpec.cy.ts
│ └── ProfileManagementSpec.cy.ts
└── admin-user/
├── UserManagementSpec.cy.ts
└── InventoryManagementSpec.cy.ts
Choose the organization strategy that best fits your application’s structure and team’s mental model.
Organizing API Tests
By HTTP Method
Group API tests by HTTP method:
cypress/e2e/api/
├── GETSpec.cy.ts
├── POSTSpec.cy.ts
├── PUTSpec.cy.ts
└── DELETESpec.cy.ts
By Resource
Group tests by API resource:
cypress/e2e/api/
├── bookings/
│ ├── GetBookingsSpec.cy.ts
│ ├── CreateBookingSpec.cy.ts
│ ├── UpdateBookingSpec.cy.ts
│ └── DeleteBookingSpec.cy.ts
├── auth/
│ ├── LoginSpec.cy.ts
│ └── TokenSpec.cy.ts
└── users/
├── GetUsersSpec.cy.ts
└── CreateUserSpec.cy.ts
By Test Type
Separate functional, integration, and contract tests:
cypress/e2e/api/
├── functional/
│ └── BookingCRUDSpec.cy.ts
├── integration/
│ └── BookingWorkflowSpec.cy.ts
└── contract/
└── BookingSchemaSpec.cy.ts
Test File Naming Conventions
Descriptive Names Use clear, descriptive names that indicate what’s being tested
Spec Suffix End files with Spec.cy.ts or just .cy.ts
PascalCase Use PascalCase for test file names
Avoid Abbreviations Write full words for clarity
Good Examples
✅ LoginPageSpec.cy.ts
✅ UserAuthenticationSpec.cy.ts
✅ ProductSearchSpec.cy.ts
✅ CheckoutFlowSpec.cy.ts
Bad Examples
❌ test1.cy.ts
❌ login.cy.ts (too generic )
❌ usrAuth.cy.ts (uses abbreviations )
❌ spec.cy.ts (not descriptive )
Structuring Test Files
Single describe Block
Group related tests under one describe:
describe ( 'User Authentication' , () => {
beforeEach (() => {
// Common setup
})
it ( 'Should log in with valid credentials' , () => {})
it ( 'Should not log in with invalid credentials' , () => {})
it ( 'Should show error for locked account' , () => {})
it ( 'Should redirect after successful login' , () => {})
})
Nested describe Blocks
Use nested describes for complex scenarios:
describe ( 'Product Management' , () => {
beforeEach (() => {
// Common setup for all product tests
})
describe ( 'Product List' , () => {
it ( 'Should display all products' , () => {})
it ( 'Should filter products by category' , () => {})
it ( 'Should sort products by price' , () => {})
})
describe ( 'Product Details' , () => {
it ( 'Should show product information' , () => {})
it ( 'Should add product to cart' , () => {})
it ( 'Should show related products' , () => {})
})
describe ( 'Product Search' , () => {
it ( 'Should search by product name' , () => {})
it ( 'Should show no results message' , () => {})
})
})
Context Blocks
Use context as an alias for describe to add clarity:
describe ( 'Login' , () => {
context ( 'With valid credentials' , () => {
it ( 'Should log in successfully' , () => {})
it ( 'Should redirect to home page' , () => {})
})
context ( 'With invalid credentials' , () => {
it ( 'Should show error message' , () => {})
it ( 'Should remain on login page' , () => {})
})
context ( 'With locked account' , () => {
it ( 'Should show locked account message' , () => {})
it ( 'Should not allow login' , () => {})
})
})
Hooks Organization
before vs beforeEach
describe ( 'User Tests' , () => {
// Runs once before all tests
before (() => {
// Expensive operations (database seeding, etc.)
})
// Runs before each test
beforeEach (() => {
// Setup for each test (login, navigation, etc.)
cy . fixture ( 'users' ). as ( 'userData' )
loginPage = new LoginPage ()
cy . visit ( '/' )
})
// Runs after each test
afterEach (() => {
// Cleanup (logout, clear cookies, etc.)
})
// Runs once after all tests
after (() => {
// Final cleanup
})
})
Shared Setup in Support Files
For setup used across multiple test files:
// Global beforeEach
beforeEach (() => {
// Runs before every test in every file
cy . clearCookies ()
cy . clearLocalStorage ()
})
// Global afterEach
afterEach (() => {
// Runs after every test
cy . screenshot ({ capture: 'runner' })
})
Use global hooks sparingly. Prefer test-specific hooks for better test isolation.
Custom Commands for Reusable Logic
Define Custom Commands
cypress/support/commands.ts
Cypress . Commands . add ( 'login' , ( username : string , password : string ) => {
cy . visit ( '/login' )
cy . get ( '#username' ). type ( username )
cy . get ( '#password' ). type ( password )
cy . get ( '#login-button' ). click ()
})
Cypress . Commands . add ( 'logout' , () => {
cy . get ( '#menu' ). click ()
cy . get ( '#logout' ). click ()
})
Use in Tests
it ( 'Should access protected page' , () => {
cy . login ( 'standard_user' , 'secret_sauce' )
cy . visit ( '/protected' )
cy . contains ( 'Protected Content' ). should ( 'be.visible' )
})
TypeScript Declarations
cypress/support/commands.ts
declare global {
namespace Cypress {
interface Chainable {
login ( username : string , password : string ) : Chainable < void >
logout () : Chainable < void >
}
}
}
Test Data Organization
Fixture Organization
Match fixture structure to test structure:
cypress/fixtures/
├── ui/
│ ├── auth/
│ │ ├── users.json
│ │ └── credentials.json
│ └── products/
│ └── inventory.json
└── api/
├── bookings.json
└── users.json
Shared vs Specific Data
cypress/fixtures/
├── shared/
│ └── common-users.json # Used across multiple tests
├── login/
│ └── test-users.json # Specific to login tests
└── checkout/
└── payment-data.json # Specific to checkout tests
Page Object Organization
One File Per Page
cypress/support/pages/
├── BasePage.ts
├── LoginPage.ts
├── HomePage.ts
├── ProductListPage.ts
├── ProductDetailPage.ts
├── CartPage.ts
└── CheckoutPage.ts
Group by Feature
cypress/support/pages/
├── BasePage.ts
├── auth/
│ ├── LoginPage.ts
│ ├── SignupPage.ts
│ └── PasswordResetPage.ts
├── products/
│ ├── ProductListPage.ts
│ └── ProductDetailPage.ts
└── checkout/
├── CartPage.ts
└── CheckoutPage.ts
Component Organization
cypress/support/components/
├── Navbar.ts
├── Footer.ts
├── SearchBar.ts
├── ProductCard.ts
└── Modal.ts
Test Tagging and Grouping
Use Descriptive Test Names
// ✅ Good - describes behavior
it ( 'Should display error message when login fails' , () => {})
// ❌ Bad - vague
it ( 'Test login' , () => {})
it ( 'Should process payment' , { tags: [ '@critical' , '@payment' ] }, () => {
// Test implementation
})
it ( 'Should display terms' , { tags: [ '@ui' , '@low-priority' ] }, () => {
// Test implementation
})
Group by Priority
describe ( 'Critical User Flows' , () => {
it ( 'Should complete purchase' , () => {})
it ( 'Should process refund' , () => {})
})
describe ( 'Nice-to-Have Features' , () => {
it ( 'Should show product recommendations' , () => {})
it ( 'Should display wishlist' , () => {})
})
Best Practices Summary
Keep test files focused on a single feature or page: // ✅ Good - LoginPageSpec.cy.ts
describe ( 'Login Page' , () => {
// All login-related tests
})
// ❌ Bad - AllTests.cy.ts
describe ( 'All Application Tests' , () => {
// Tests for login, products, checkout, etc.
})
Use consistent naming across your test suite: ✅ LoginPageSpec.cy.ts
✅ HomePageSpec.cy.ts
✅ CheckoutPageSpec.cy.ts
❌ login-test.cy.ts
❌ home_page_spec.cy.ts
❌ checkout.cy.ts
Use describe blocks to create clear hierarchy: describe ( 'E-commerce Application' , () => {
describe ( 'Authentication' , () => {
describe ( 'Login' , () => {
it ( 'Should login with valid credentials' , () => {})
})
})
})
Ensure tests can run independently: // ✅ Good - each test sets up its own state
it ( 'Test 1' , () => {
cy . visit ( '/' )
// Test logic
})
it ( 'Test 2' , () => {
cy . visit ( '/' )
// Test logic
})
Next Steps
Maintainability Keep your tests maintainable long-term
TypeScript Usage Leverage TypeScript for better organization
Running Tests Execute your organized test suite
Project Structure Understand the overall project layout