DEV Community

Alex Spinov
Alex Spinov

Posted on

Cypress Has a Free API — The Complete E2E Testing Toolkit

Cypress is the most popular end-to-end testing framework with 47K+ GitHub stars — and its free API lets you test anything from React components to full user flows without Selenium.

Why Cypress Dominates E2E Testing

  • No Selenium — runs directly in the browser, not through WebDriver
  • Time travel — snapshots at each step, hover to see what happened
  • Automatic waiting — no sleep(), no flaky selectors
  • Network stubbing — intercept and mock any API call
  • Component testing — test React/Vue/Angular components in isolation
  • Dashboard — free tier with test recordings and analytics

Quick Start

# Install
npm install -D cypress

# Open interactive runner
npx cypress open

# Run headless
npx cypress run
Enter fullscreen mode Exit fullscreen mode

Your First E2E Test

// cypress/e2e/login.cy.js
describe("Login Flow", () => {
  beforeEach(() => {
    cy.visit("/login");
  });

  it("logs in with valid credentials", () => {
    cy.get("[data-testid=email]").type("user@example.com");
    cy.get("[data-testid=password]").type("password123");
    cy.get("[data-testid=submit]").click();

    // Automatic waiting — no sleep needed
    cy.url().should("include", "/dashboard");
    cy.get("[data-testid=welcome]").should("contain", "Welcome back");
  });

  it("shows error for invalid credentials", () => {
    cy.get("[data-testid=email]").type("wrong@email.com");
    cy.get("[data-testid=password]").type("wrongpassword");
    cy.get("[data-testid=submit]").click();

    cy.get("[data-testid=error]")
      .should("be.visible")
      .and("contain", "Invalid credentials");
  });
});
Enter fullscreen mode Exit fullscreen mode

Network Interception (The Killer Feature)

describe("API Mocking", () => {
  it("displays products from API", () => {
    // Intercept the API call and return mock data
    cy.intercept("GET", "/api/products", {
      statusCode: 200,
      body: [
        { id: 1, name: "Widget", price: 9.99 },
        { id: 2, name: "Gadget", price: 19.99 },
      ],
    }).as("getProducts");

    cy.visit("/products");
    cy.wait("@getProducts");

    cy.get("[data-testid=product]").should("have.length", 2);
    cy.get("[data-testid=product]").first().should("contain", "Widget");
  });

  it("handles API errors gracefully", () => {
    cy.intercept("GET", "/api/products", {
      statusCode: 500,
      body: { error: "Internal Server Error" },
    }).as("getProducts");

    cy.visit("/products");
    cy.wait("@getProducts");

    cy.get("[data-testid=error-message]")
      .should("be.visible")
      .and("contain", "Something went wrong");
  });

  it("tests loading states", () => {
    cy.intercept("GET", "/api/products", (req) => {
      req.reply({
        delay: 2000, // Simulate slow network
        body: [{ id: 1, name: "Widget", price: 9.99 }],
      });
    }).as("getProducts");

    cy.visit("/products");
    cy.get("[data-testid=loading-spinner]").should("be.visible");
    cy.wait("@getProducts");
    cy.get("[data-testid=loading-spinner]").should("not.exist");
  });
});
Enter fullscreen mode Exit fullscreen mode

Component Testing (React Example)

// cypress/component/Button.cy.jsx
import Button from "../../src/components/Button";

describe("Button Component", () => {
  it("renders with text", () => {
    cy.mount(<Button>Click me</Button>);
    cy.get("button").should("contain", "Click me");
  });

  it("calls onClick handler", () => {
    const onClick = cy.spy().as("clickHandler");
    cy.mount(<Button onClick={onClick}>Click me</Button>);

    cy.get("button").click();
    cy.get("@clickHandler").should("have.been.calledOnce");
  });

  it("renders variants", () => {
    cy.mount(<Button variant="primary">Primary</Button>);
    cy.get("button").should("have.class", "btn-primary");

    cy.mount(<Button variant="danger">Danger</Button>);
    cy.get("button").should("have.class", "btn-danger");
  });

  it("disables correctly", () => {
    cy.mount(<Button disabled>Disabled</Button>);
    cy.get("button").should("be.disabled");
  });
});
Enter fullscreen mode Exit fullscreen mode

Custom Commands (DRY Your Tests)

// cypress/support/commands.js
Cypress.Commands.add("login", (email, password) => {
  cy.visit("/login");
  cy.get("[data-testid=email]").type(email);
  cy.get("[data-testid=password]").type(password);
  cy.get("[data-testid=submit]").click();
  cy.url().should("include", "/dashboard");
});

Cypress.Commands.add("apiLogin", (email, password) => {
  cy.request("POST", "/api/auth/login", { email, password })
    .its("body.token")
    .then((token) => {
      window.localStorage.setItem("authToken", token);
    });
});

// Usage in tests:
it("shows dashboard after login", () => {
  cy.apiLogin("user@example.com", "password123"); // Fast, no UI
  cy.visit("/dashboard");
  cy.get("[data-testid=welcome]").should("be.visible");
});
Enter fullscreen mode Exit fullscreen mode

Cypress vs Playwright vs Selenium

Feature Cypress Playwright Selenium
Setup npm install npm install Driver + bindings
Speed Fast Fastest Slowest
Auto-wait Yes Yes No
Network mock Built-in Built-in External tool
Component test Yes Yes No
Browsers Chrome, Firefox, Edge Chrome, Firefox, Safari, Edge All
Parallel Dashboard (paid) Built-in Grid (complex)
Debugging Time travel UI Trace viewer Screenshots

Need to scrape data from any website and get it in structured JSON? Check out my web scraping tools on Apify — no coding required, results in minutes.

Have a custom data extraction project? Email me at spinov001@gmail.com — I build tailored scraping solutions for businesses.

Top comments (0)