Real World Testing with Cypress (2024)

Installing Cypress

Now that our store is up and running, we are going to install Cypress so that we can write end-to-end tests to test our Next.js front-end.

You can find our more about Cypress and how to install it on our docs site.

cd into the site/ directory and then install Cypress.

cd siteyarn add cypress --dev

Open up the package.json file and add the following scripts.

"scripts": { // ... "cypress:open": "cypress open", "cypress:run": "cypress run"}

The entire "scripts" object should look like this:

"scripts": { "dev": "next dev", "build": "next build", "start": "next start", "analyze": "BUNDLE_ANALYZE=both next build", "lint": "next lint", "prettier-fix": "prettier --write .", "find:unused": "npx next-unused", "cypress:open": "cypress open", "cypress:run": "cypress run" },

Make sure you have the Next.js development server running in another terminal window or tab.

yarn dev

Next, run the cypress:open command to launch Cypress in another terminal window or tab.

yarn cypress:open

Cypress should launch and look like this:

Real World Testing with Cypress (1)

Cypress by default creates several example spec files for demonstration purposes. In the blue alert at the top click on the "No thanks, delete example files" link. This will delete all of the default spec files that Cypress installs upon first launching it.

Writing Our First Test

Create a new spec file within cypress/integration and called it home.spec.js

Real World Testing with Cypress (2)

Next we will create a describe() method like so:

describe("Home Page", () => {})

Now that our spec file is setup, we can begin to write our first test.

Before we do that however, let's take a minute to think through what we need to test. We should be asking ourselves some questions like, "What are the most important features of this application?" In the case of our dev swag store, one of the most important features is the ability to purchase swag. Before we can make a purchase, however, our products need to be displayed in our store from Shopify.

Let's write a test that confirms that all of our products are being displayed in our store.

Update home.spec.js with the following:

describe("Home Page", () => { it("displays all 3 products on the home page", () => {})})

The first thing we need to do in our test is tell Cypress to navigate to the homepage of our application.

describe("Home Page", () => { it("displays all 3 products on the home page", () => { cy.visit("http://localhost:3000") })})

Next, we will need to use cy.get() to get our products. Before we do that however, let's learn a Cypress best practice.

Testing Specific Attributes

Most people will typically use a CSS class or ID to get their elements like so:

cy.get(".btn-large")cy.get("#hero")

While this is perfectly valid and will work, we do not actually recommend this. Why not? You see by using CSS classes or ID's you are tying your tests to things that are most likely going to change overtime. Classes and ID's are for design and layout, which are prone to change. If someone changes a class name or id your tests are going to break. To help make your tests less brittle and more future-proof we recommend you create special data attributes on your elements specifically for testing purposes.

We will be using the data-test attribute on our elements, like so:

Now that we have that covered, let's update one of the components displayed on our homepage with a data-test attribute. We want to confirm that the title and price being displayed on the homepage is correct. This data is being displayed by the ProductTag.tsx component.

Open that component up in your editor and paste the following.

import cn from 'clsx'import { inherits } from 'util'import s from './ProductTag.module.css'interface ProductTagProps { className?: string name: string price: string fontSize?: number}const ProductTag: React.FC<ProductTagProps> = ({ name, price, className = '', fontSize = 32,}) => { return ( <div className={cn(s.root, className)} data-test="product-tag"> <h3 className={s.name}> <span className={cn({ [s.fontsizing]: fontSize < 32 })} style={{ fontSize: `${fontSize}px`, lineHeight: `${fontSize}px`, }} data-test="product-name" > {name} </span> </h3> <div className={s.price} data-test="product-price"> {price} </div> </div> )}export default ProductTag

Notice how we have added three of these data-test attributes:

<div className={cn(s.root, className)} data-test="product-tag">
<span className={cn({ [s.fontsizing]: fontSize < 32 })} style={{ fontSize: `${fontSize}px`, lineHeight: `${fontSize}px`, }} data-test="product-name"> {name}</span>
<div className={s.price} data-test="product-price"> {price}</div>

Now that we have these in place, let's continue writing our test.

Testing Our Products

Update the home.spec.js file with the following:

describe("Home Page", () => { it("displays all 3 products on the home page", () => { cy.visit("http://localhost:3000") cy.get('[data-test="product-tag"]') })})

Now let's run Cypress to make sure everything is working so far.

Real World Testing with Cypress (3)

Click on the home.spec.js file to launch Cypress and run our test.

Real World Testing with Cypress (4)

So far so good.

We currently have three products and so when we use cy.get('[data-test="product-tag"]') Cypress will return all of our products. We only want the first one, so we can use the cy.eq() to grab it.

cy.get('[data-test="product-tag"]').eq(0)

Then, we will want to get the product name and product price to confirm they are displaying correctly within this element, like so:

describe("Home Page", () => { it("displays all 3 products on the home page", () => { cy.visit("http://localhost:3000") cy.get('[data-test="product-tag"]') .eq(0) .within(() => { cy.get('[data-test="product-name"]').should("contain", "Star Wars") cy.get('[data-test="product-price"]').should("contain", "$25.00 USD") }) })})

By using .within() we are limiting the scope of the subsequent cy.get() commands. Otherwise, cy.get() will search through the entire document. Since we know that the product name and price are children of the "product tag" we can limit the scope in which Cypress looks to find these elements.

Real World Testing with Cypress (5)

Now that we know our first product is displayed properly, let's confirm the other two. We can simply copy and past the first part of our test for the first product and modify the product name and title for the other products.

describe("Home Page", () => { it("displays all 3 products on the home page", () => { cy.visit("http://localhost:3000") cy.get('[data-test="product-tag"]') .eq(0) .within(() => { cy.get('[data-test="product-name"]').should("contain", "Star Wars") cy.get('[data-test="product-price"]').should("contain", "$25.00 USD") }) cy.get('[data-test="product-tag"]') .eq(1) .within(() => { cy.get('[data-test="product-name"]').should("contain", "SQL") cy.get('[data-test="product-price"]').should("contain", "$25.00 USD") }) cy.get('[data-test="product-tag"]') .eq(2) .within(() => { cy.get('[data-test="product-name"]').should("contain", "Code") cy.get('[data-test="product-price"]').should("contain", "$25.00 USD") }) })})

Real World Testing with Cypress (6)

Great all of our products are displaying the correct name and price!

Testing the Header

Let's now write some tests for the header of our application. We will write some tests to make sure that the links go to the correct pages and the search bar returns the correct results.

First, create a new spec file in cypress/integration called header.spec.js and add the following.

describe("Header", () => {})

Real World Testing with Cypress (7)

First, let's write a test to make sure that the links in the header go to the correct pages.

describe("Header", () => { it("links to the correct pages", () => {})})

We can add our data-test attributes to these links within the components/common/Navbar/Navbar.tsx component.

import { FC } from 'react'import Link from 'next/link'import s from './Navbar.module.css'import NavbarRoot from './NavbarRoot'import { Logo, Container } from '@components/ui'import { Searchbar, UserNav } from '@components/common'interface Link { href: string label: string}interface NavbarProps { links?: Link[]}const Navbar: FC<NavbarProps> = ({ links }) => ( <NavbarRoot> <Container clean className="mx-auto max-w-8xl px-6"> <div className={s.nav}> <div className="flex items-center flex-1"> <Link href="/"> <a className={s.logo} aria-label="Logo" data-test="logo"> <Logo /> </a> </Link> <nav className={s.navMenu}> <Link href="/search"> <a className={s.link} data-test="nav-link-search"> All </a> </Link> {links?.map((l) => ( <Link href={l.href} key={l.href}> <a className={s.link} data-test="nav-link-home-page"> {l.label} </a> </Link> ))} </nav> </div> {process.env.COMMERCE_SEARCH_ENABLED && ( <div className="justify-center flex-1 hidden lg:flex"> <Searchbar /> </div> )} <div className="flex items-center justify-end flex-1 space-x-8"> <UserNav /> </div> </div> {process.env.COMMERCE_SEARCH_ENABLED && ( <div className="flex pb-4 lg:px-6 lg:hidden"> <Searchbar id="mobile-search" /> </div> )} </Container> </NavbarRoot>)export default Navbar

First, let's confirm that clicking on the store logo will go to the home page.

describe("Header", () => { it("links to the correct pages", () => { cy.visit("http://localhost:3000") cy.get('[data-test="logo"]').click() cy.location("pathname").should("eq", "/") })})

Real World Testing with Cypress (8)

baseUrl and Cypress Custom Commands

Instead of having to write cy.visit('http://localhost:3000') to tell Cypress to go to the home page, we can tell Cypress our applications baseUrl and then simply use cy.visit('/') instead.

Within the cypress.json file in the root of the repo, add the following:

{ "baseUrl": "http://localhost:3000"}

Real World Testing with Cypress (9)

Now you can update our test like so:

describe("Header", () => { it("links to the correct pages", () => { cy.visit("/") cy.get('[data-test="logo"]').click() cy.location("pathname").should("eq", "/") })})

Our test is still passing

Real World Testing with Cypress (10)

Next, instead of having to write cy.get('[data-test="logo"]') we can simplify this as well with a custom Cypress command.

Add the following to the cypress/support/commands.js file.

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

Real World Testing with Cypress (11)

Now we can update our test like so:

describe("Header", () => { it("links to the correct pages", () => { cy.visit("/") cy.getBySel("logo").click() cy.location("pathname").should("eq", "/") })})

And our test is still passing.

Real World Testing with Cypress (12)

Now let's do the same for the other links in the header.

describe("Header", () => { it("links to the correct pages", () => { cy.visit("/") cy.getBySel("logo").click() cy.location("pathname").should("eq", "/") cy.getBySel("nav-link-search").click() cy.location("pathname").should("eq", "/search") cy.getBySel("nav-link-home-page").click() cy.location("pathname").should("eq", "/search/frontpage") })})

When we re-run our test we see that it is failing, why?

Real World Testing with Cypress (13)

If we look closely at our application, we can see that those links do not appear in the header. This is because Cypress by default opens the viewport to "1000x660" which is too narrow. One of the breakpoints in our Next.js application is hiding these links when it is 1000px wide. We can modify the default viewport size by adding the following to the cypress.json file.

{ "baseUrl": "http://localhost:3000", "viewportHeight": 1000, "viewportWidth": 1280}

Now our test should be passing

Real World Testing with Cypress (14)

Testing the Search Bar

Let's write a test to make sure that the search bar in the header returns the correct results

Add the following new test inside of header.spec.js

describe("Header", () => { it("links to the correct pages", () => { cy.visit("/") cy.getBySel("logo").click() cy.location("pathname").should("eq", "/") cy.getBySel("nav-link-search").click() cy.location("pathname").should("eq", "/search") cy.getBySel("nav-link-home-page").click() cy.location("pathname").should("eq", "/search/frontpage") }) it("the search bar returns the correct search results", () => { cy.visit("/") })})

beforeEach() hooks

You will notice that within each test, we have to specifically tell Cypress where to navigate in our app before our test can be executed. We can remove this duplication by using a beforeEach() hook. This hook will run any code we put inside of it before each test is run, hence the name beforeEach()

describe("Header", () => { beforeEach(() => { cy.visit("/") }) it("links to the correct pages", () => { cy.getBySel("logo").click() cy.location("pathname").should("eq", "/") cy.getBySel("nav-link-search").click() cy.location("pathname").should("eq", "/search") cy.getBySel("nav-link-home-page").click() cy.location("pathname").should("eq", "/search/frontpage") }) it("the search bar returns the correct search results", () => {})})

Now Cypress will visit the home page before each and every test in this file.

Within components/common/Searchbar/Searchbar.tsx update the <input> element with the following data-test attribute data-test="search-input":

import { FC, memo, useEffect } from 'react'import cn from 'clsx'import s from './Searchbar.module.css'import { useRouter } from 'next/router'interface Props { className?: string id?: string}const Searchbar: FC<Props> = ({ className, id = 'search' }) => { const router = useRouter() useEffect(() => { router.prefetch('/search') }, [router]) const handleKeyUp = (e: React.KeyboardEvent<HTMLInputElement>) => { e.preventDefault() if (e.key === 'Enter') { const q = e.currentTarget.value router.push( { pathname: `/search`, query: q ? { q } : {}, }, undefined, { shallow: true } ) } } return ( <div className={cn(s.root, className)}> <label className="hidden" htmlFor={id}> Search </label> <input id={id} className={s.input} placeholder="Search for products..." defaultValue={router.query.q} onKeyUp={handleKeyUp} data-test="search-input" /> <div className={s.iconContainer}> <svg className={s.icon} fill="currentColor" viewBox="0 0 20 20"> <path fillRule="evenodd" clipRule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z" /> </svg> </div> </div> )}export default memo(Searchbar)

Now we can .get() the search bar like so:

// header.spec.jsit('links to the correct pages', () => {// ...it('the search bar returns the correct search results', () => { cy.getBySel('search-input').eq(0) })

tip

This search component is actually in the DOM twice, one for desktop and one for mobile. By using .eq(0) we are grabbing the one for desktop.

only()

Since, we now have two tests in this file, Cypress will always run both tests each time we save. We can use .only() to have it only run a single one like so:

it.only("the search bar returns the correct search results", () => { cy.getBySel("search-input").eq(0)})

Real World Testing with Cypress (15)

Let's try to search for the "Star Wars" by typing in the word "star"

it.only("the search bar returns the correct search results", () => { cy.getBySel("search-input").eq(0).type("star")})

We can also simulate pressing the enter key like so:

it.only("the search bar returns the correct search results", () => { cy.getBySel("search-input").eq(0).type("star{enter}")})

Real World Testing with Cypress (16)

Now let's confirm that the correct product is displayed in the search results. We can borrow some of the same code we used in our home.spec.js test.

it.only("the search bar returns the correct search results", () => { cy.getBySel("search-input").eq(0).type("star{enter}") cy.get('[data-test="product-tag"]').within(() => { cy.get('[data-test="product-name"]').should("contain", "Star Wars") cy.get('[data-test="product-price"]').should("contain", "$25.00 USD") })})

Real World Testing with Cypress (17)

Let's write one more test that confirms that our shopping cart is working.

Create a new file within cypress/integration called shopping-cart.spec.js and add the following:

describe("Shopping Cart", () => { it("users can add products to the cart", () => { cy.visit("/") })})

Real World Testing with Cypress (18)

First, we will need to click on a product to add it to the cart.

describe("Shopping Cart", () => { it("users can add products to the cart", () => { cy.visit("/") cy.getBySel("product-tag").eq(0).click() })})

Real World Testing with Cypress (19)

Next, we will need to click on the "Add to Cart" button.

describe("Shopping Cart", () => { it("users can add products to the cart", () => { cy.visit("/") cy.getBySel("product-tag").eq(0).click() cy.get('[aria-label="Add to Cart"]').click() })})

Notice how we are getting the element by using the aria-label attribute.

Real World Testing with Cypress (20)

Now we just need to confirm that our shopping cart has our item in it.

describe("Shopping Cart", () => { it("users can add products to the cart", () => { cy.visit("/") cy.getBySel("product-tag").eq(0).click() cy.get('[aria-label="Add to Cart"]').click() cy.get('[aria-label="Cart items: 1"]').contains("1") })})

Real World Testing with Cypress (21)

Conclusion

In this lesson, we learned how to install Cypress and how to write several different tests to ensure that our store is working as expected. We also learned how to customize the Cypress test runner within the cypress.json file as well has how to use custom Cypress commands.

Practice

Now would be a great time for you to practice writing more Cypress tests. Here are some ideas for tests you could write.

  • Write a test that confirms the shopping cart can contain multiple items.
  • Write a test that confirms your images have the correct alt tags.
  • Write a test that confirms the “Accept Cookies” popup displays on the bottom of the page.
  • Write a test that confirms the language selector in the footer, navigates to the correct page.
Real World Testing with Cypress (2024)

FAQs

What can I test with Cypress? ›

Cypress was originally designed to run end-to-end (E2E) tests on anything that runs in a browser. A typical E2E test visits the application in a browser and performs actions via the UI just like a real user would.

What is the real world app for testing? ›

The Real World App is a payment application that demonstrates real-world usage of Cypress testing methods, patterns, and workflows. Each example breaks down, line by line, a single test within the Real World App.

Can Cypress replace selenium? ›

Will Cypress replace Selenium? Cypress is unlikely to replace Selenium entirely. Both frameworks will likely continue to be used for different purposes. Selenium is more established and has a wider range of capabilities, making it a good choice for large-scale testing projects.

Can you test backend with Cypress? ›

Cypress stands out as a compelling choice for backend testing due to several key advantages: Seamless Integration: Cypress integrates seamlessly with backend services, allowing you to directly interact with your APIs and verify their responses without the need for additional tools or frameworks.

Is Cypress faster than Selenium? ›

Cypress can run individual tests faster than Selenium, but it cannot be used on two browsers simultaneously and it doesn't have multi-tab support.

When not to use Cypress? ›

You cannot use Cypress to drive two browsers at the same time. Each test is bound to a single superdomain. Cross-origin navigation inside tests can be enabled by using the cy.

What is a real life example of unit testing? ›

An example of a real-world scenario that could be covered by a unit test is a checking that your car door can be unlocked, where you test that the door is unlocked using your car key, but it is not unlocked using your house key, garage door remote, or your neighbour's (who happen to have the same car as you) key.

What is the real world test plan? ›

Real World Testing plans are intended to describe measurement approaches for the year immediately following the plan's submission. The plan should address any Health IT Modules certified by or before August 31 of the year in which the plan is submitted.

What is Andrew Tate's app? ›

Tate created the app, Real World Portal, after the closure of his “Hustler's University”, which was an online academy for his fans, promising to assist them in making thousands of pounds while helping Tate's videos on social media, which have been described as misogynistic, to go viral.

Why choose Cypress over Selenium? ›

Selenium is a library but requires a unit testing framework or a runner plus an assertions library to build out its capabilities. Cypress provides a robust, complete framework for running automated tests but takes some of the freedom out of Selenium by confining the user to specific frameworks and languages.

Is Cypress worth learning? ›

Advantages of Cypress

Real-Time Reloading: Cypress enables real-time reloading of tests and application code, simplifying development and debugging. Interactive Debugging: It offers an interactive debugger for real-time test debugging and issue identification.

Does Cypress require coding? ›

Cypress tests are only written in JavaScript.

While you can compile down to JavaScript from any other language, ultimately the test code is executed inside the browser itself. There are no language or driver bindings - there is and will only ever be just JavaScript.

Should I use Cypress for API testing? ›

Cypress is a highly preferred E2E testing framework and it provides us with a way to do API testing. For performing API testing, we don't need any third-party dependency to perform this testing. We can just set up Cypress and perform API testing with it.

What browser is best for Cypress testing? ›

Cross Browser Support

Cypress currently supports Firefox and Chrome-family browsers (including Edge and Electron). To run tests optimally across these browsers in CI, check out the strategies demonstrated in the cross browser Testing guide.

Is Cypress good for end-to-end testing? ›

End-to-End (E2E) testing is designed to ensure that all components of a software application are working together correctly and that the system as a whole meets the desired functionality, performance, and reliability requirements. Cypress is a popular open-source end-to-end testing framework for web applications.

Can Cypress do visual testing? ›

Luckily, Cypress gives a stable platform for writing plugins that can perform visual testing. Typically such plugins take an image snapshot of the entire application under test or a specific element, and then compare the image to a previously approved baseline image.

Is Cypress good for unit testing? ›

Usually, to achieve all these benefits, the tester will have to use 2-3 different tech stacks, so Cypress enables the tester to get all these options while using it. Hence, using Cypress in unit testing is much more convenient.

Is Cypress good for UI testing? ›

Fast and reliable: Cypress is built on top of the browser and runs the tests in the same environment as the application, which makes tests faster and more reliable.

Top Articles
Latest Posts
Article information

Author: Dong Thiel

Last Updated:

Views: 6043

Rating: 4.9 / 5 (79 voted)

Reviews: 86% of readers found this page helpful

Author information

Name: Dong Thiel

Birthday: 2001-07-14

Address: 2865 Kasha Unions, West Corrinne, AK 05708-1071

Phone: +3512198379449

Job: Design Planner

Hobby: Graffiti, Foreign language learning, Gambling, Metalworking, Rowing, Sculling, Sewing

Introduction: My name is Dong Thiel, I am a brainy, happy, tasty, lively, splendid, talented, cooperative person who loves writing and wants to share my knowledge and understanding with you.