DEV Community

Martin Chudomel
Martin Chudomel

Posted on

1

Efficient Cypress API Testing with Custom Commands

When working on projects that involve extensive API testing, managing API requests in your Cypress tests can quickly become overwhelming. Writing repetitive cy.request() calls directly in test files not only makes them cluttered but also difficult to maintain. To address this, I’ve adopted a structured approach to organize and simplify API requests using custom commands in Cypress. Here, I’ll walk you through my solution step by step, which you can implement to streamline your API tests.

Step 1: A General Function for All Requests

The first step is to create a generic function to handle any type of API request. This function accepts parameters such as the HTTP method, URL, query parameters, request body, bearer token, and device headers. Here’s the implementation:

function callRequest(method, url, params, body, bearer, device) {
    var headers = {
        uuid: Cypress.uuid // Unique identifier for tracking requests
    }
    if (bearer) {
        headers.authentication = 'Bearer ' + bearer;
    }
    if (device) {
        headers['Origin-Type'] = device;
    }
    return cy.request({
        method: method,
        url: url,
        qs: params,
        body: body,
        headers: headers,
        failOnStatusCode: false,
    }).then((response) => {
        if (response.status === 500) {
            cy.task('log', `[\${new Date().toISOString()}] Request URL: \${url}, Parameters: \${JSON.stringify(params)}, Response: \${JSON.stringify(response.body)}`);
        }
        return cy.wrap(response);
    });
}
Enter fullscreen mode Exit fullscreen mode

This function is a flexible foundation for all types of requests and ensures consistent handling of headers, query parameters, and error logging.

Step 2: Specific Functions for HTTP Methods

Since different HTTP methods often require varying parameters (e.g., GET typically doesn’t need a request body), I created separate functions for each method. For example:

function getRequest(url, params = { countryId: Cypress.env().countryId }, bearer) {
    return callRequest('GET', url, params, undefined, bearer);
}

function postRequest(url, params = { countryId: Cypress.env().countryId }, body, bearer) {
    return callRequest('POST', url, params, body, bearer);
}
Enter fullscreen mode Exit fullscreen mode

This abstraction makes the code cleaner and avoids repeatedly specifying HTTP method details in every API call.

Step 3: Custom Cypress Commands

To further simplify API usage in tests, I created custom Cypress commands. Each command defines the specific endpoint URL and invokes the appropriate HTTP method function. Here are a couple of examples:

Cypress.Commands.add('getTranslation', (params) => {
    const reqUrl = '/_api/v1/translations/translate/';
    return getRequest(reqUrl, params);
});

Cypress.Commands.add('setUserData', (params, body, bearer) => {
    const reqUrl = '/api/v1/user/signup-info-data/';
    return postRequest(reqUrl, undefined, body, bearer);
});
Enter fullscreen mode Exit fullscreen mode

These commands make it incredibly easy to add new API endpoints as needed. You’ll only need to define the endpoint URL and choose the appropriate HTTP method function.
I have created a separate file only for API custom commands. To be able to use it, I had to add import './commands-api' to the e2e.js file in the support folder.

Step 4: Using Custom Commands in Tests

With everything set up, using API requests in your tests becomes effortless. For example:

cy.getTranslation().then(response => {
    expect(response.status).to.eq(200);
    expect(response.body).to.have.property('translations');
});
Enter fullscreen mode Exit fullscreen mode

Benefits of This Approach

  1. Maintainability: Changes to API request logic (e.g., headers, logging) can be made in a single location.
  2. Usability: Reuse the same custom commands across multiple tests.
  3. Readability: Test files remain concise and focused on assertions.
  4. Scalability: Easily add new endpoints or HTTP methods without duplicating code.

Conclusion

By organizing API requests into a general function, HTTP method-specific functions, and custom commands, you can significantly improve the maintainability and scalability of your Cypress API tests. This approach reduces code duplication and makes it easy to handle even complex testing scenarios.

Feel free to implement and adapt this structure in your projects. Let me know your thoughts or share your own approaches to organizing API requests in Cypress!

Image of AssemblyAI tool

Transforming Interviews into Publishable Stories with AssemblyAI

Insightview is a modern web application that streamlines the interview workflow for journalists. By leveraging AssemblyAI's LeMUR and Universal-2 technology, it transforms raw interview recordings into structured, actionable content, dramatically reducing the time from recording to publication.

Key Features:
🎥 Audio/video file upload with real-time preview
🗣️ Advanced transcription with speaker identification
⭐ Automatic highlight extraction of key moments
✍️ AI-powered article draft generation
📤 Export interview's subtitles in VTT format

Read full post

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

👋 Kindness is contagious

Discover a treasure trove of wisdom within this insightful piece, highly respected in the nurturing DEV Community enviroment. Developers, whether novice or expert, are encouraged to participate and add to our shared knowledge basin.

A simple "thank you" can illuminate someone's day. Express your appreciation in the comments section!

On DEV, sharing ideas smoothens our journey and strengthens our community ties. Learn something useful? Offering a quick thanks to the author is deeply appreciated.

Okay