Building Cypress Commands

Cypress commands allow you to create custom functionality and even overwrite existing commands. Having this flexibility is incredibly convenient and powerful, but developers often struggle with when to write custom commands. A helpful thing to think about when you should write your custom command is when your test code forces you to do so. What exactly does this mean?

When you begin to notice yourself writing the same functionality over and over again, aka repeating yourself, across multiple tests, that is usually a good sign to make a custom command. Don't begin writing your tests by thinking in abstractions, i.e., custom commands—rather abstract custom commands from your tests.

You already do this all the time as a JavaScript developer each time you create a custom function in your code. Typically you will create a custom JS function to abstract some functionality to re-use within your application, i.e., utility functions. Cypress commands are the same thing. They allow you to re-use functionality across multiple tests.

Creating a Custom Command

Let's take a look an example.

cy.get('[data-test="signup-first-name"]').type("Bob")
cy.get('[data-test="signup-last-name"]').type("Ross")
cy.get('[data-test="signup-username"]').type("PainterJoy90")
cy.get('[data-test="signup-password"]').type("s3cret")
cy.get('[data-test="signup-confirmPassword"]').type("s3cret")

In this example, we are selecting several elements that all have a data-test attribute. While there is nothing inherently wrong with this syntax, it is quite a bit verbose. We could clean this up quite a bit and simplify things by creating our own custom command.

Instead of writing all of the above, it would be nice to write.

cy.getBySel("signup-first-name").type("Bob")
cy.getBySel("signup-last-name").type("Ross")
cy.getBySel("signup-username").type("PainterJoy90")
cy.getBySel("signup-password").type("s3cret")
cy.getBySel("signup-confirmPassword").type("s3cret")

Let's see how to create the custom command cy.getBySel(). You can create your own custom commands by placing them inside of cypress/support/commands.js.

Here is what the cy.getBySel() looks like:

Cypress.Commands.add("getBySel", (selector, ...args) => {
  return cy.get(`[data-test=${selector}]`, ...args)
})

As you can see, our custom command is a simple wrapper around cy.get(). This way, we can pass in the string contained within the data-test attribute, and our custom command will return the element.

Another Custom Command Example

Let's take a look at another example. Let's say you need to log in as a user before each test. You can create a custom command that handles all of the logging in and authentication logic, so you can simply call it within your test.

Cypress.Commands.add("loginByApi", (username, password) => {
  return cy.request("POST", `http://localhost:3000/login`, {
    username,
    password,
  })
})

In this example, we use a custom command called cy.loginByApi(), which accepts a username and password and then sends a post request to our API login route. Now within our tests, we can call this command, passing in a username and password.

describe("POST /login", () => {
  it("login as user", () => {
    cy.loginByApi("jdoe", "password123").then((response) => {
      expect(response.status).to.eq(200)
    })
  })
})

Practice

If you would like to practice how to build custom Cypress Commands, we have created a special repo which can be found here. The installation instructions are located in the README.md file.

The practice file you are looking for can be found in cypress/e2e/Practice/cypress-commands.spec.js.

Should you get stuck or need some help, all of the answers are provided within cypress/e2e/Answers/cypress-commands.spec.js


Unlock the next lesson

You should be thinking in abstractions before you begin writing your tests