FunChat 테스팅 코드 TIL

YEONGHUN KO·2024년 3월 21일
0

JAVASCRIPT TESTING

목록 보기
11/11
post-thumbnail

testing playground를 잘 활용하기

testing playground에 들어가서 테스팅 하고 싶은 DOM을 복사 붙여넣기 하면 알아서 특정 DOM을 select할 수 있도록 가장 최적의 jest api를 추천하고 있다.

일일이 공식문서를 보면서 골머리를 앓을 필요가 없다는 것!

regEx로 요소를 찾을경우

''를 제거할것. 아래 코드를 참고


// wrong
const messageInput = screen.getByPlaceholderText(/"type a message"/i);

// right
const messageInput = screen.getByPlaceholderText(/type a message/i);

라이브러리 사용시 다운그레이드도 고려해볼것

msw 2.x 버전대를 다운받아 사용하던 도중 not a function 에러가 도저히 잡히지 않았다.

결국 1.x으로 다운그레이드 하였고 매우 깔끔하게 작동하였다.

kent.c.dodds의 글을 참고하여 깔끔하게 테스트 코드 리팩토링함

반복되어 setup되는 코드를 하나로 묶어 관리하고 싶다? beforeEach를 사용할 수 도 있겠지만 전역변수가 변경될 우려가 있다. kent형님은 이 글에서 함수를 사용하여 관리하라고 하신다.

그래서 적용해보았다!

const setUp = () => {
  const utils = customRender(<ChatBox />);

  const messageInput = screen.getByPlaceholderText(/type a message/i);
  const messageSubmitButton = screen.getByTestId("send-message");

  const changeMessageInput = async (message: string) =>
    await userEvent.type(messageInput, message);
  const submitMessage = async () => await userEvent.click(messageSubmitButton);

  return {
    ...utils,
    changeMessageInput,
    submitMessage,
  };
};

describe("test sending text messages and wait for it to appear", () => {
  it("successufully should send text messages and optimistically update message container)", async () => {
   
    // arrange
    const utils = setUp();

    // act
    await utils.changeMessageInput("new message");
    await utils.submitMessage();

    // assert
    const newMessage = await screen.findByText(/new message/i);
    expect(newMessage).toBeInTheDocument;
  });
});

계산과 액션을 나누자

서버에 작성된 socket io - addUser 함수를 테스팅 하려고 했다.

addUser은 아래에 나열된 작업을 수행한다.
1. 방금 로그인한 user를 onlineUsers DB에 등록한다.
2. DB를 모든 유저에게 소켓으로 전파한다.
3. chat list를 업데이트 하기 위한 정보를 소켓으로 전송한다.

이때 단순 계산과 api통신을 하게하여 클라이언트 상태를 바꾸는 액션이 섞여있어서 테스트가 힘들었다.

단순 계산은 1번이고 액션은 2,3번이었다. 그래서 1번과 2/3번을 함수로 나누어 관심사를 분리하였다.

const addUser = (
  { me }: { me: number },
  socket: Socket<DefaultEventsMap, DefaultEventsMap, DefaultEventsMap, any>
) => {
  if (!me) {
    return;
  }

  // 계산
  const { loggedInUsers, userChattingWithMe } = setNewUser(me, socket.id);

  // 액션
  io.emit("get-onlineUsers", {
    onlineUsers: JSON.stringify(loggedInUsers),
  });

  if (userChattingWithMe) {
    const [userId, { socketId }] = userChattingWithMe;

    updateChatList(socket, {
      from: me,
      to: userId,
      otherSocketId: socketId,
    });
  }
};

그러니 테스트 코드가 훨씬 분명해지고 쉬워졌다.

test("register new user and emit updated usersList", (done) => {
      // arrange
      const { loggedInUsers } = setNewUser(MOCKED_USER_ID, serverSocket.id);

      // act
      io.emit("get-onlineUsers", {
        onlineUsers: JSON.stringify(loggedInUsers),
      });

      // assert
      clientSocket.on(
        "get-onlineUsers",
        ({ onlineUsers }: { onlineUsers: any }) => {
          const parsedOnlineUsers = JSON.parse(onlineUsers) as Array<
            [number, { chatRoomId: number; socketId: string }]
          >;
          const [userId, { socketId }] = parsedOnlineUsers[0];
          expect(socketId).toBe(serverSocket.id);
          expect(userId).toBe(MOCKED_USER_ID);
          done();
        }
      );
    });

라이브러리 보단 직접 구현해보기

백엔드에서 테스트 코드를 작성할때 prisma를 모킹해야하는 상황이 발생하였다.

그리고 공홈에 이미 모킹하는 방법이 있었다.

prisma 모킹방법

그러나 undefned 에러를 해결할 수 없어서 그냥 직접 jest.mock을 사용하여 모킹하기로 했다.

// /__test__/mock/prismaInstance.ts
import {
  mockedPrismaUserDB,
  mockedPrismaMessagesDB,
  type IMessages,
} from "../fixtures/mockedPrismaDB";

jest.mock("../../utils/PrismaClient", () => ({
  __esModule: true,
  default: jest.fn(() => ({
    user: {
      create: async (userData: any) => {
       ...

      },
      findUnique: async (target: { where: { email: string } }) => {
        ...
      },
      
    messages: {
      findMany: async (findArgs: {
        where: {
          OR: {
            senderId: number;
            recieverId: number;
          }[];
        };
        orderBy: keyof IMessages;
      }) => {
        ...
      },
    },
  })),
}));

그리고 config에 setup시켜주면 된다.

import type { Config } from "jest";
const config: Config = {
  setupFilesAfterEnv: ["<rootDir>/__test__/mock/prismaInstance.ts"],

};

export default config;

prisma에서 사용하는 api를 일일이 모킹해줘야하는 번거로움이 있지만 라이브러리에 의존하지 않아서 디버깅이 쉽고 통제권한을 가질 수 있다는 장점이 있다.

profile
'과연 이게 최선일까?' 끊임없이 생각하기

0개의 댓글