Best Practices - spryker-projects/cypress-boilerplate GitHub Wiki
Cypress Best Practices
To ensure that your Cypress tests are robust, maintainable, and efficient, follow these best practices. Refer to the Cypress Best Practices guide for detailed information.
General Recommendations
- Avoid chaining multiple commands in one line: It makes debugging difficult.
- Use Cypress commands like
cy.get()
,cy.contains()
effectively: They provide built-in retries. - Use custom scenarios: For repeated user flows (registration, checkout), create custom scenarios.
- Use custom commands: For repeated sequences of commands and abstract actions (
closeAllFlashMessages
,runConcoleCommand
), create custom commands. - Leverage fixtures and data files: Use
cypress/fixtures
for storing test data. - Avoid using
const
,let
, orvar
with Cypress commands: Cypress commands are asynchronous. Use aliases or closures instead. - Test only what you control: Avoid testing third-party services or websites.
- Environment Configuration: Use environment-specific configuration files to manage different environments efficiently.
Organizing Tests
- Structure your tests: Organize tests by features or user stories.
- Create reusable utility functions: For setup, teardown, and common actions.
- Use hooks (
before
,beforeEach
,afterEach
,after
) wisely: To set up and clean up state before and after tests.
Avoiding Anti-Patterns
- Avoid testing implementation details: Focus on testing the behavior of your application.
- Avoid using
cy.pause()
: It's better to use proper debugging tools. - Avoid
cy.wait()
for arbitrary periods: Usecy.intercept()
orcy.get()
with assertions for proper synchronization.
Visiting External Sites
- Avoid visiting external sites directly: Instead, mock the network requests to avoid dependencies on external services and to speed up tests.
after
Or afterEach
Hooks
Using - Use sparingly: Avoid using
after
orafterEach
hooks to clean up state as they run even if tests fail, potentially masking issues.
Setting a Global baseUrl
- Set
baseUrl
in Cypress configuration: This simplifies your test code by avoiding hard-coded URLs.- Example:
// cypress.config.ts
import { defineConfig } from 'cypress'
export default defineConfig({
e2e: {
baseUrl: 'http://localhost:8484',
},
})
Specs Best Practices
- Independence: Each spec should be independent of each other.
- Autonomy: Each spec should be able to run on its own.
- Order: Each spec should be able to run in any order.
- Environment: Each spec should be able to run in any environment.
- Tests: Each spec can contain multiple tests.
- Assertions: Each test can have many assertions.
Classes Best Practices
- Single Responsibility Principle: Each class should have a single responsibility.
- Encapsulation: Hide internal state and require all interaction to be performed through an object's methods.
- Reusability: Design classes to be reusable in different contexts.
TypeScript - Why We Prefer It
TypeScript is a superset of JavaScript that adds static typing. It helps in catching errors early during development and provides better tooling with IntelliSense. TypeScript enhances code quality and maintainability by enabling:
- Static Typing: Reduces runtime errors by catching type errors at compile time.
- Enhanced IDE Support: Better autocompletion, navigation, and refactoring tools.
- Improved Code Quality: Enforces type checks and makes the code more predictable.
Page Object Best Practices
Page Objects
Page Objects encapsulate the page structure and UI interactions, promoting reusability and maintainability.
- Abstract Page Usage: Create an abstract page class that contains common functionalities for all pages.
- Getter Functions: Use getter functions to locate elements.
- Example:
const loginForm = 'form[name="loginForm"]'
getEmailField = (): Cypress.Chainable => {
return cy.get(loginForm).find('#loginForm_email')
}
- Action Functions: Create atomic functions for actions.
- Example:
selectFirstBusinessAddressAvailableForShipping = (): void => {
this.getShippingAddressDropdown().click()
cy.get('[id*="company_business_unit_address"]').first().click()
}
Best Practices
- Atomic Functions: Ensure each function has a single purpose.
- Reuse Functions: Reuse common functions across different page objects.
- Encapsulation: Keep the page structure details hidden from the test scripts.
Reusing API for Setup
To minimize testing steps and enhance test speed, reuse APIs for setup tasks. For example, if you need to test Order Management in the Back Office, create an order via API to save time, and have Order placement test via UI only once. This approach helps in:
- Speeding Up Tests: Avoid redundant UI operations by setting up state via APIs.
- Ensuring State Consistency: APIs can help ensure the application state is consistent before running UI tests.