MSW

MSW(Mock Server Worker)는 간단하게 말하면 테스트용 서버를 만드는 것이다.

테스트 서버를 따로 만들어 정해진 응답을 리턴하도록 만들어 Backend와 독립된 Frontend 테스트를 가능하게 해준다.

1. UserList 컴포넌트 만들기

  • UserListContainer.js

    import React, { useState } from "react";
    import UserListPresenter from "./UserListPresenter";
    
    const UserListContainer = () => {
      const [users, setUsers] = useState([]);
      const requestUsers = async () => {
        const response = await fetch("http://111.111.111.111:3000/users", {
          method: "GET",
        });
        const newUsers = await response.json();
        if (newUsers.length > 0) {
          setUsers(() => newUsers);
        }
      };
      return <UserListPresenter users={users} requestUsers={requestUsers} />;
    };
    export default UserListContainer;
    
  • UserListPresenter.js

    import React from "react";
    import styled from "styled-components";
    
    const UserList = styled.div`
      display: flex;
      flex-direction: column;
      margin: 20px;
      font-size: 20px;
      font-weight: bold;
    `;
    
    const UserListPresenter = ({ users, requestUsers }) => {
      return (
        <>
          <UserList>
            {users.map((user) => {
              return (
                <div key={user.id}>
                  {user.id} : {user.name}
                </div>
              );
            })}
          </UserList>
          <button type="button" onClick={requestUsers}>
            유저 새로고침
          </button>
        </>
      );
    };
    export default UserListPresenter;
    

UserList는 사용자 리스트를 보여주고 버튼을 누르면 서버로부터 UserList를 받아 사용자 리스트를 새로고침 하는 컴포넌트이다.

그럼 서버를 만들고 서버에 직접 요청해서 테스트를 하면 될까???

2. API 테스트의 문제점

  1. 테스트는 의존성을 낮춰야 한다.

    어떤 기능을 테스트 하는지에 따라 다르지만 테스트 단위는 하나의 기능 단위라고 생각한다.

    UserList같은 경우유저 정보를 받아서 잘 보여주는지만 테스트를 하면 된다.

    서버가 UserList를 잘 넘겨주도록 구현되있는지는 신경쓰지 않는다! (물론 경우에 따라 한꺼번에 테스트 할 수도 있지만……)

  2. 데이터 CRUD는 Backend 영역이다.

    물론 Frontend에서 DB접근을 할 수 있는 좋은 방법이나 기술이 많지만 지금 테스트 하고 있는것은 서버에서 정보를 받을 때 이다.

    데이터를 쓰고, 읽고, 수정하고, 삭제하는 것은 백엔드가 할 일이고 프론트엔드 관심이 없다.

    Backend가 원하는 데이터만 잘 넘겨주면 되고 그 데이터를 잘 넘겨주고 그 데이터를 가지고 잘 동작하는지만 테스트하면 된다.

    한마디로 데이터가 정상적으로 온다는 조건 하에 잘 동작하면된다.

API 테스트 방법

  1. fetch 함수를 mock 함수로 대체

    UserList의 경우 UserListPresenter를 테스트하고 requestUsers를 mock함수로 대체하는 것이다.

    하지만 상태관리 등 UserListContainer의 역할을 테스트 코드 내에 구현해주어야 한다.

  2. 테스트를 위한 API 서버 구현

    테스트만을 위한 간단한 API 서버를 만드는 것이다.

    API Request가 발생하도록 놔두고 요청을 캡쳐 하여 테스트 환경에서 예상 하능한 값을 반환하도록 만든다.

3. Jest와 MSW로 API 테스트하기

1) 테스트코드 작성

2) MSW 설치하기

npm i -D msw

2) MSW 서버 만들기

src/__mock__폴더를 생성한다.

  • Handler 생성

    Handler는 Controller의 역할을 한다. (express의 router)

    __mock__/handlers.js에 핸들러를 생성한다.

    import { rest } from "msw";
    export const handlers = [
      rest.get("http://111.111.111.111:3000/users", (req, res, ctx) => {
        return res(
          ctx.status(200),
          ctx.json([
            {
              id: "EarlyHail",
              name: "Hojin Lee",
            },
            {
              id: "user1",
              name: "Other User",
            },
          ])
        );
      }),
    ];
    
  • Server 생성

    Server는 Controller를 모아주는 역할을 한다. (express의 app)

    import { setupServer } from "msw/node";
    import { handlers } from "./handlers";
    export const server = setupServer(...handlers);
    
  • test 환경설정

    이제 테스트가 실행될 때 서버가 실행되도록 만들어보자.

    JestbeforeAll, afterEach, afterAll을 이용해서 서버를 시작하고, 초기화하고, 종료해준다.

    import { server } from "./__mocks__/server.js";
    beforeAll(() => server.listen());
    afterEach(() => server.resetHandlers());
    afterAll(() => server.close());
    

3) 테스트코드 작성

__test__/UserList.test.js에 테스트 코드를 작성해보자.

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

describe("UserList Test", () => {
  beforeEach(() => {
    render(<UserListContainer />);
  });
  test("유저 새로고침 버튼 클릭 시 유저를 보여줍니다.", async () => {
    fireEvent.click(screen.getByText("유저 새로고침"));
    await waitFor(() => screen.getByText(/EarlyHail/g));
  });
});

결과

TestResult

버튼 클릭 이벤트만 활성화 시켜줘도 테스트가 잘 된다!

거의 API 서버를 만들어 주는 방식이긴 하지만 테스트 코드 자체가 깔끔해지기 때문에 좋은 방식인것 같다.