Redux-React 테스팅하기

no-pla·2024년 7월 10일
0

TIL

목록 보기
13/14
post-thumbnail

리액트-리덕스와 연결되어 있는 리액트 컴포넌트를 테스트해 보자.

먼저, 테스트 코드 작성에 필요한 라이브러리들을 설치해 준다.

npm install --save-dev @testing-library/react @testing-library/react-hooks redux-mock-store

함수 단위(Unit) 테스팅

기본적으로 통합 테스트를 작성하는 것이 좋지만, 단위 테스트를 작성해야 할 때도 있다.
단위 테스트는 이전 상태에 액션을 적용한 후, 새로운 상태를 반환하는 순수 함수다. 대부분의 경우, 명시적인 테스트가 필요 없기 때문에 간단하게 테스트 코드를 작성할 수 있다. 특정 입력 state와 action으로 reducer를 호출하고, 결과가 일치하는지 확인하면 된다.

예시로, 다음과 같은 코드를 테스트하면,

    drop: (state, actions) => {
      if (state.stop) return;
      if (state.winner !== null) {
        console.warn(`이미 종료된 게임입니다. 승자는 ${state.winner}입니다.`);
        return;
      }

      if (state.board[actions.payload.lineNumber][0] !== null) {
        console.warn(
          `${actions.payload.lineNumber + 1} 열은 이미 전부 채워진 열입니다.`
        );
        return;
      }
      let location: null | number = null;

      /**
       * 만약 선택한 행의 첫 번째 셀이 null이 아니면 리턴.
       */
      for (let i = 6; i >= 0; i--) {
        if (state.board[actions.payload.lineNumber][i] === null) {
          state.board[actions.payload.lineNumber][i] =
            state.currentPlayer === "RED" ? "RED" : "YELLOW";
          location = i;
          break;
        }
      }

      if (location === 0) {
        state.notMaxLine = state.notMaxLine.filter(
          (lineNumber) => lineNumber !== actions.payload.lineNumber
        );
      }

      state.markerCount += 1;

      if (state.markerCount >= 7) {
        // 모든 방향을 체크하기 위해 방향 벡터를 사용한다.
        const movement = [
          { dx: 1, dy: 0 }, // 가로
          { dx: 0, dy: 1 }, // 세로
          { dx: -1, dy: 1 }, // 양수 대각선
          { dx: -1, dy: -1 }, // 음수 대각선
        ];

        // 연결 테스트
        const checkDirection = (dx: number, dy: number) => {
          let count = 1; // 기존 마커도 추가.

          let pnx = actions.payload.lineNumber + dx; // X축 각 방향별로 1칸씩 이동
          let pny = location! + dy; // Y축 각 방향별로 1칸씩 이동

          while (
            // 각 좌표가 보드 내에 있고, 해당 위치에 있는 마커가 현재 유저의 마커와 같은 색상의 마커인지 확인한다.
            // 만약 마커가 보드를 넘어갈 경우, 종료.
            pnx >= 0 &&
            pny >= 0 &&
            pnx <= 6 &&
            pny <= 5 &&
            state.board[pnx][pny] === actions.payload.player
          ) {
            count++;
            pnx += dx;
            pny += dy;
          }

          let mnx = actions.payload.lineNumber - dx; // X축 각 방향별로 1칸씩 이동
          let mny = location! - dy; // Y축 각 방향별로 1칸씩 이동

          while (
            // 각 좌표가 보드 내에 있고, 해당 위치에 있는 마커가 현재 유저의 마커와 같은 색상의 마커인지 확인한다.
            // 만약 마커가 보드를 넘어갈 경우, 종료.
            mnx >= 0 &&
            mny >= 0 &&
            mnx <= 6 &&
            mny <= 5 &&
            state.board[mnx][mny] === actions.payload.player
          ) {
            count++;
            mnx -= dx;
            mny -= dy;
          }

          return count;
        };

        for (const { dx, dy } of movement) {
          const count = checkDirection(dx, dy);

          if (count >= 4) {
            state.winner = actions.payload.player;

            if (state.winner === "RED") {
              state.redWin += 1;
            } else {
              state.yellowWin += 1;
            }

            console.log(`${state.winner}가 승리했습니다.`);
            return;
          }
        }
      }

      state.currentPlayer = actions.payload.player === "RED" ? "YELLOW" : "RED";
      state.timer = 30;
    },

다음과 같이 테스트 코드를 작성할 수 있다.

// src/tests/game.test.tsx
    const previousState = {
      board: [
        [null, null, null, null, null, null],
        [null, null, null, null, null, null],
        [null, null, null, null, null, null],
        [null, null, null, null, null, null],
        [null, null, null, null, null, null],
        [null, null, null, null, null, null],
        [null, null, null, null, null, null],
      ],
      currentPlayer: "RED" as "RED" | "YELLOW",
      markerCount: 0,
      winner: null,
      redWin: 0,
      yellowWin: 0,
      timer: 30,
      stop: false,
      notMaxLine: [0, 1, 2, 3, 4, 5, 6],
    };

gameSlice(previousState, drop({ lineNumber: 0 }))

// slice함수(기본값, reducer(payload))

reducer가 있는 gameSlice에 기본값과, 실행할 함수와 payload를 전달해 준다.

0개의 댓글