1. 개발 전 준비

실습 레포

codeceptjs e2e 테스트 세팅

codeceptjs 세팅

index.tsx

리셋, global stylerhk 초기 라우팅까지 잡아줬다.

function index() {
  const element = document.getElementById("root");
  if (!element) {
    return;
  }

  const root = ReactDOM.createRoot(element);

  root.render(
    <StrictMode>
      <ThemeProvider theme={defaultTheme}>
        <Reset />
        <GlobalStyle />

        <RouterProvider router={router} />
      </ThemeProvider>
    </StrictMode>
  );
}

index();

Test 에서는 memoryRouter를 사용해줘야한다

// routes.test.tsx

.
.
.

const context = describe;

describe("routes", () => {
  function renderRouter(path: string) {
    const router = createMemoryRouter(routes, { initialEntries: [path] });
    render(
      <ThemeProvider theme={defaultTheme}>
        <RouterProvider router={router} />
      </ThemeProvider>
    );
  }

  context("when the current path is “/”", () => {
    it("renders the intro page", async () => {
      renderRouter("/");

      await waitFor(() => {
        screen.getByText("top");
      });
    });
  });
});
.
.
.

Test helper

styled-components의 Theme이나 React Router의 Link를 사용하는 프로젝트에서 test를 하려면

테스트 환경에서의 provider와 router를 따로 사용해줘야 한다.

이때 Test Helper로 테스트들에 Provider로 내려준다.

// src/test-helpers.tsx

import { render as originalRender } from "@testing-library/react";

import React from "react";

import { MemoryRouter } from "react-router-dom";

import { ThemeProvider } from "styled-components";

import defaultTheme from "./styles/defaultTheme";

export function render(element: React.ReactElement) {
  return originalRender(
    <MemoryRouter initialEntries={["/"]}>
      <ThemeProvider theme={defaultTheme}>{element}</ThemeProvider>
    </MemoryRouter>
  );
}

msw를 위한 handlers

import { rest } from "msw";

import { ProductSummary } from "../types";

import fixtures from "../../fixtures";

const BASE_URL = "xxxxxxxxxxxxsecretxxxxxxxxxxxx";

const productSummaries: ProductSummary[] = fixtures.products.map(
  (product: {
    id: any;
    category: any;
    images: any[];
    name: any;
    price: any;
  }) => ({
    id: product.id,
    category: product.category,
    thumbnail: product.images[0],
    name: product.name,
    price: product.price,
  })
);

const handlers = [
  rest.get(`${BASE_URL}/categories`, (req, res, ctx) =>
    res(ctx.json({ categories: fixtures.categories }))
  ),
  rest.get(`${BASE_URL}/products`, (req, res, ctx) =>
    res(ctx.json({ products: productSummaries }))
  ),
  rest.get(`${BASE_URL}/products/:id`, (req, res, ctx) => {
    const product = fixtures.products.find(
      (i: { id: string | readonly string[] }) => i.id === req.params.id
    );
    if (!product) {
      return res(ctx.status(404));
    }
    return res(ctx.json(product));
  }),
  rest.get(`${BASE_URL}/cart`, (req, res, ctx) => res(ctx.json(fixtures.cart))),
  rest.post(`${BASE_URL}/cart/line-items`, (req, res, ctx) =>
    res(ctx.status(201))
  ),
];

export default handlers;

fixtures를 name space처럼 쓰고싶기 때문에 파일을 바로 Import 하지않고 index를 거치게 했다.

// fixtures/index.ts

import categories from "./categories";
import products from "./products";
import cart from "./cart";

export default {
  categories,
  products,
  cart,
};
// fixtures/categories.ts

import { Category } from "../src/types";

const categories: Category[] = [
  { id: "category-01", name: "Category #1" },
  { id: "category-02", name: "Category #2" },
];

export default categories;

Last updated