DEV Community

Martin Chudomel
Martin Chudomel

Posted on

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!

Top comments (0)