Posts tagged "cypress"

Testing Frontend applications

We know that testing is hard. Imagine testing a frontend applications 🤯.

So for that, we are going to cover different approaches and review several libraries to test a frontend application.

Static Testing

Use ESLint to detect bugs, e.g. typos, and codebase improvements on build time. Use Typescript to detect type bugs on build time.

    uploader.tsx
        44:2    error     Expected '===' and instead saw '=='    eqeqeq
    greeter.ts(8,21): error TS2345: Argument of type ‘number[]’ is not assignable to parameter of type ‘string’.

Logic Unit Testing

Use Jest to unit test business logic decoupled from UI.

import React from "react";
import renderer from "react-test-renderer";
import App, { Counter, reducer } from "./App";

const list = ["a", "b", "c"];

describe("App", () => {
  describe("Reducer", () => {
    it("should set a list", () => {
      const state = { list: [], error: null };
      const newState = reducer(state, {
        type: "SET_LIST",
        list,
      });
      expect(newState).toEqual({ list, error: null });
    });
  });
  ...
});

Component Unit Testing

Use Jest to unit tests the component using different possible approaches.

Regression Testing with Snapshots

Use Jest Snapshots for regression testing of the output.

import React from "react";
import { render } from "@testing-library/react";
import LoginForm from "./LoginForm";

test("LoginForm should generate the correct HTML and CSS", () => {
  const onSubmit = jest.fn();

  const { asFragment } = render(<LoginForm onSubmit={onSubmit} />);
  expect(asFragment()).toMatchSnapshot();
});

Use Storybook with Storyshoots or Jest Image Snapshot for visual regression testing.

Behavior Testing with Testing Library

Use Testing Library to test the behavior of the components from the user point of view.

import React from "react";
import { render, fireEvent } from "@testing-library/react";
import LoginForm from "./LoginForm";

test("LoginForm should call the onSubmit callback", () => {
  const onSubmit = jest.fn();

  const { getByText } = render(<LoginForm onSubmit={onSubmit} />);
  fireEvent.click(getByText(/send/));
  expect(onSubmit).toHaveBeenCalled();
});

Integration Test for Complex Component

Use Cypress or TestCafe to test complex workflows as a real user, in a real browser with a fake backend and faking any HTTP call.

import { RequestMock } from "testcafe";
import { within, addTestCafeTestingLibrary } from "@testing-library/testcafe";

const loginAPIMock = RequestMock.onRequestTo(
  "http://localhost:3000/api",
).respond(null, 200);

fixture`Login`.beforeEach(addTestCafeTestingLibrary)
  .page`http://localhost:3000`.requestHooks(loginAPIMock);

test("fill login", async t => {
  const { getByLabelText, getByText } = await within("body");

  await t.typeText(getByLabelText(/email/), "test@test.com");
  await t.typeText(getByLabelText(/password/), "abc123");
  await t.click(getByText(/submit/));
  await t.expect(getByText(/success/).exists).ok();
});

End to End Tests for Complex Workflows

Use Cypress or TestCafe to test complex workflows as a real user, in a real browser with a real working backend.

import { within, addTestCafeTestingLibrary } from "@testing-library/testcafe";

fixture`Login`.beforeEach(addTestCafeTestingLibrary)
  .page`http://localhost:3000`;

test("fill login", async t => {
  const { getByLabelText, getByText } = await within("body");

  await t.typeText(getByLabelText(/email/), "test@test.com");
  await t.typeText(getByLabelText(/password/), "abc123");
  await t.click(getByText(/submit/));
  await t.expect(getByText(/success/).exists).ok();
});

Using tests, we don't have to be scared about deploying on Friday anymore 🥳.