Today, let’s look at how to automate React application testing with the Cypress tool.
To start working with Cypress in a React project, the first thing you need to do is install the package itself. This can be done using npm or Yarn:
npm install cypress --save-dev
# or
yarn add cypress --dev
After installation, you will need to initialize Cypress, which will create a basic structure of folders and configuration files:
npx cypress open
The command will create all the necessary files and open the Cypress user interface where you can see and manage all the tests.
The structure will look as follows:
Example of a basic cypress.json configuration:
{
"baseUrl": "http://example.com",
"defaultCommandTimeout": 10000,
"pageLoadTimeout": 30000,
"viewportWidth": 1280,
"viewportHeight": 720,
"testFiles": "**/*.spec.js"
}
Here:
baseUrl
specifies the base URL to which relative URLs will be appended in your tests.defaultCommandTimeout
specifies the default timeout for Cypress commands in milliseconds (10 seconds in this case).pageLoadTimeout
sets the maximum time to wait for a page to load (30 seconds).viewportWidth
and viewportHeight
specify the width and height of the browser window for running tests.estFiles
specifies a template for test files (in this case, all files with the .spec.js extension in any subfolders).Cypress uses a chain of commands. Tests are usually organized using the describe
and it
functions, which are part of the global Mocha API.
Example of a basic test:
describe('Login Test', function() {
it('Visits the login page', function() {
cy.visit('https://example.com/login') // login page
cy.get('input[name=username]').type('user1') // username
cy.get('input[name=password]').type('password123') // password
cy.get('form').submit() // form submission
cy.url().should('include', '/dashboard') // URL check after login
})
})
Items search is done with cy.get()
, which accepts a selector string. Cypress supports most CSS selectors.
Interaction with elements is possible through commands like .click()
, .type()
, .check()
for different types of elements.
Assertions check the expected state or behavior of elements. Cypress integrates with Chai by providing multiple assertions via .should()
or .expect()
.
Examples:
cy.get('.list-item').should('have.length', 5) // element count check
cy.get('.alert').should('not.exist') // the element must not exist
Cypress automatically manages asynchrony, so you will very rarely have to explicitly use async/await
. For example, if you make a request to an API, Cypress will wait for it to complete before moving on:
cy.request('POST', '/api/users', {name: 'Alex'}).then((response) => {
expect(response.body).to.have.property('name', 'Alex') // response check
})
Cypress allows HTTP requests to be intercepted, making it possible to test without actual server responses using mocks:
cy.intercept('GET', '/api/users', {fixture: 'users.json'}).as('getUsers')
cy.wait('@getUsers').its('response.statusCode').should('eq', 200)
You can use aliases to save data and use it later in tests:
cy.get('input[name=firstname]').type('Jane').as('firstname')
cy.get('@firstname').should('have.value', 'Jane')
Cypress can take snapshots of the application’s state:
cy.visit('/settings')
cy.get('.theme').click()
cy.screenshot() // screenshot
For a counter component that increments or decrements a value when buttons are pressed, tests in Cypress can check the initial state and response to user actions:
describe('Counter Component', () => {
beforeEach(() => {
cy.visit('/');
});
it('should render the counter with initial value', () => {
cy.get('h1').contains('Counter: 0');
});
it('should increment the counter when increment button is clicked', () => {
cy.get('button').contains('Increment').click();
cy.get('h1').contains('Counter: 1');
});
it('should decrement the counter when decrement button is clicked', () => {
cy.get('button').contains('Increment').click();
cy.get('button').contains('Decrement').click();
cy.get('h1').contains('Counter: 0');
});
});
The test verifies that the initial display of the component is correct and changes as the user interacts with the controls.
Input form testing includes testing user input and the form’s response to submission:
describe('Login Form', () => {
beforeEach(() => {
cy.visit('/login');
});
it('should allow a user to type in the username and password', () => {
cy.get('input[name="username"]').type('testuser');
cy.get('input[name="password"]').type('qwerty');
});
it('should show an error message for invalid credentials', () => {
cy.get('input[name="username"]').type('invaliduser');
cy.get('input[name="password"]').type('wrongpassword');
cy.get('button[type="submit"]').click();
cy.get('.error-message').should('be.visible').and('contain', 'Invalid credentials');
});
it('should redirect to dashboard on successful login', () => {
cy.get('input[name="username"]').type('testuser');
cy.get('input[name="password"]').type('qwerty');
cy.get('button[type="submit"]').click();
cy.url().should('include', '/dashboard');
});
});
The test suite checks both negative and positive login scenarios.
Test the addition, deletion and validation of list items:
describe('Todo List Functionality', () => {
beforeEach(() => {
cy.visit('/todos');
});
it('displays the list of todos correctly', () => {
cy.get('li').should('have.length', 2); // предположим, что уже есть два элемента в списке
});
it('adds a new todo', () => {
const newItem = 'Complete the test';
cy.get('.new-todo-input').type(`${newItem}{enter}`);
cy.get('li').should('have.length', 3);
});
it('deletes a todo', () => {
cy.get('li').first().find('.delete-button').click();
cy.get('li').should('have.length', 1);
});
});
Cypress not only allows you to test your application but also extends its capabilities through plugins, let’s take a look at my favorites.
cypress-axe
The cypress-axe plugin integrates axe-core
accessibility tools with Cypress, allowing you to test web pages against accessibility standards:
it('Tests accessibility on the page', () => {
cy.visit('/your-page');
cy.injectAxe();
cy.checkA11y();
});
cypress-plugin-tab
The plugin adds support for keyboard events, such as pressing the Tab key, which is not supported in the base version of Cypress:
it('should be able to navigate fields with tab', () => {
cy.get('#first-input').type('info').tab().type('more info');
});
cypress-graphql-mock
The cypress-graphql-mock
plugin allows you to mock GraphQL responses, which is good for developing and testing client applications that use GraphQL to interact with the server:
it('mocks GraphQL data', () => {
cy.mockGraphQL({ Query: { user: () => ({ id: 1, name: 'John' }) } });
cy.visit('/user-profile');
cy.contains('John');
});
The test verifies that the UI correctly displays user data by mocking GraphQL queries.
cypress-downloadfile
The cypress-downloadfile
plugin adds the ability to download files while running tests:
it('downloads a file', () => {
cy.downloadFile('http://example.com/file.pdf', 'mydownloads', 'example.pdf');
cy.readFile('mydownloads/example.pdf').should('exist');
});
This downloads the PDF file from the provided URL, saves it to a directory and further checks if the file is available on the system.
cypress-localstorage-commands
The cypress-localstorage-commands
plugin adds commands to manage data in localStorage during testing:
beforeEach(() => {
cy.restoreLocalStorage();
});
afterEach(() => {
cy.saveLocalStorage();
});
it('tests local storage', () => {
cy.setLocalStorage('testKey', 'testValue');
cy.getLocalStorage('testKey').should('eq', 'testValue');
});
Here, the state of localStorage is restored before each test and saved after the test.
I am available for work in 2024. Have a project for me, any questions, or wish to make an order? You can email or visit my Fiverr page.