Understanding Cypress Basics: Core Features and Syntax Explained
Cypress is one of the most popular end-to-end (E2E) testing frameworks, known for its simplicity, power, and developer-friendly features. In this article, we’ll explore Cypress's core features and syntax, helping you build a strong foundation for writing reliable E2E tests. This guide is written for beginners, with simple explanations and practical examples to make Cypress accessible to everyone.
Introduction
This article is part of our Cypress Tutorial Series designed to help beginners master Cypress, starting from the basics. If you're new to Cypress, we recommend starting with the previous article, A Beginner's Guide to Cypress: End-to-End Testing Made Easy. It introduces Cypress, explains its benefits, and walks you through the setup process.
By the end of this series, you’ll have the skills to confidently write, execute, and debug Cypress tests for modern web applications.
Why Cypress?
Modern web applications involve a lot of dynamic and user-driven interactions. Testing these interactions manually is tedious and error-prone. Cypress allows you to:
- Automate user interactions, such as form submissions, dropdown selections, and navigation.
- Validate UI elements and ensure they function as expected.
- Test edge cases and user flows to ensure the reliability of your application.
Now let’s set up the project and get started.
Setting Up the Demo Project
Step 1: Create a Vite React Project
To follow along with this guide, you need a sample application to test. Create a new React project using Vite:
npm create vite@latest cypress-demo --template react
cd cypress-demo
npm install
Step 2: Install Cypress
Install Cypress as a development dependency:
npm install cypress --save-dev
and install cypress types:
npm install --save-dev @cypress/types
Add Cypress to the tsconfig.json
:
{
"compilerOptions": {
"types": ["cypress"]
}
}
Step 3: Open Cypress
Initialize Cypress and open its interactive runner:
npx cypress open
This command will create a cypress
folder in your project with default configurations.
Step 4: Create Sample Components
For this tutorial, we’ll test a simple login form. Create a LoginForm
component:
src/components/LoginForm.tsx:
import React, { useState } from 'react';
const LoginForm: React.FC = () => {
const [username, setUsername] = useState<string>('');
const [password, setPassword] = useState<string>('');
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
alert(`Welcome, ${username}!`);
};
return (
<form onSubmit={handleSubmit}>
<input
id="username"
type="text"
placeholder="Username"
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
<input
id="password"
type="password"
placeholder="Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<button type="submit">Login</button>
</form>
);
};
export default LoginForm;
Now that the project is ready, let’s dive into writing test cases.
Writing Cypress Test Cases
Cypress uses a BDD-style syntax with describe
and it
blocks:
describe
: Groups related test cases.it
: Defines individual test cases.
Let’s write a basic test suite for our LoginForm
.
Creating Test Suites and Tests
A test suite is a collection of related tests, and each test validates specific functionality. Here’s an example:
describe('LoginForm Tests', () => {
it('should render the login form', () => {
cy.visit('http://localhost:5173'); // Visit the app
cy.get('form').should('be.visible'); // Assert the form is visible
});
it('should allow users to enter username and password', () => {
cy.get('#username').type('testuser'); // Type into username
cy.get('#password').type('password123'); // Type into password
cy.get('#username').should('have.value', 'testuser'); // Assert value
cy.get('#password').should('have.value', 'password123');
});
});
Breaking down the above code into pieces to understand each command and its usuage.
Visiting Pages
The cy.visit()
command is used to load a specific URL:
cy.visit('http://localhost:5173');
cy.url().should('eq', 'http://localhost:5173/');
This command ensures the target page loads successfully and is ready for testing.
Selecting Elements
Cypress provides powerful selectors to locate elements on the page.
Using .get()
The .get()
command locates elements by id
, class
, or other attributes:
cy.get('#submit-button').click();
Using .find()
The .find()
command narrows the search scope to a specific parent element:
cy.get('.form').find('input').type('Hello!');
Selecting by Text
Use cy.contains()
to locate elements by their text content:
cy.contains('Submit').click();
This command is especially useful for buttons, links, and other text-based elements.
Checking for Partial Text
You can assert partial text matches for dynamic content:
cy.contains('Welcome').should('exist');
This ensures that elements with partial text, such as greetings or messages, are present.
Assertions
Assertions validate that elements behave as expected.
Implicit Assertions
Built into Cypress commands like .should()
:
cy.get('#login').should('be.visible');
Explicit Assertions
Use libraries like chai
for more complex checks:
cy.get('#title').then((element) => {
expect(element.text()).to.equal('Welcome Back!');
});
Dropdown Selections
Test dropdowns by selecting a value and asserting the result:
cy.get('select').select('Option 1');
cy.get('select').should('have.value', 'Option 1');
Cypress Test Isolation
Each Cypress test runs in isolation. Cypress resets the browser state (cookies, local storage, etc.) between tests.
describe('Test Isolation Example', () => {
it('should set a cookie', () => {
cy.setCookie('session', '12345');
cy.getCookie('session').should('exist');
});
it('should not retain the cookie', () => {
cy.getCookie('session').should('not.exist');
});
});
Practical Example: Testing the Login Form
Combining everything, here’s a complete example:
describe('LoginForm Tests', () => {
beforeEach(() => {
cy.visit('http://localhost:5173'); // Replace with your app's URL
});
it('should render the login form', () => {
cy.get('form').should('be.visible'); // Assert the form is visible
});
it('should allow users to type in the fields', () => {
cy.get('#username').type('testuser'); // Type into username
cy.get('#password').type('password123'); // Type into password
cy.get('#username').should('have.value', 'testuser'); // Assert value
cy.get('#password').should('have.value', 'password123');
});
it('should display an alert on submit', () => {
cy.get('#username').type('testuser');
cy.get('#password').type('password123');
cy.get('button[type="submit"]').click();
// Listen for the alert
cy.on('window:alert', (text) => {
expect(text).to.contains('Welcome, testuser!');
});
});
});
Conclusion
In this article, we explored Cypress’s core features and syntax, including how to create test suites, select elements, and make assertions. By following along with the React-based demo project, you’ve gained hands-on experience with Cypress.
In the next article, we’ll cover advanced features like stubs, spies, and handling network requests. Stay tuned, and happy testing!
Written by
Bhavik Bamania