노드심화 5-7

·2022년 12월 29일
0

study

목록 보기
47/81
post-thumbnail

Service Layer 단위 테스트 (UnitTest)

테스트 코드를 구현하려했을 때 가장 많이 작업할 계층은 이 Service Layer!
실제로 프로젝트를 진행할 때 Service Layer는 비즈니스 로직, 에러 핸들링과 같은 핵심적인 작업을 하게 됩니다. 그로인해 다양한 계층 중에서 가장 많은 코드 분량을 자랑하고, 복잡한 코드 구성을 가지게 될 것!

Service Layer의 경우 Repository Layer를 하위 계층으로 가지고 있습니다.
즉, Service Layer의 단위 테스트 (UnitTest)를 구현하기 위해서는 Repository 계층을 Mocking하여 원하는 비즈니스 로직이 정상적으로 동작하는지 확인

  1. __tests__ 폴더 하위에 unit폴더를 생성하고, posts.service.unit.spec.js 파일을 만들어 진행
  2. Service Layer의 단위 테스트는 findAllPost, deletePost 2개의 Method를 테스트
  3. repository를 Mocking
  4. Service에서는 에러를 고의로 발생시켜 정상적으로 에러가 발생하는지 또한 검증

posts.service.unit.spec.js

const PostService = require("../../services/posts.service.js");

let mockPostsRepository = {
  findAllPost: jest.fn(),
  findPostById: jest.fn(),
  createPost: jest.fn(),
  updatePost: jest.fn(),
  deletePost: jest.fn(),
};

let postService = new PostService();
// postService의 Repository를 Mock Repository로 변경합니다.
postService.postRepository = mockPostsRepository;

describe("Layered Architecture Pattern Posts Service Unit Test", () => {
  // 각 test가 실행되기 전에 실행됩니다.
  beforeEach(() => {
    jest.resetAllMocks(); // 모든 Mock을 초기화합니다.
  });

  test("Posts Service findAllPost Method", async () => {
    const findAllPostReturnValue = [
      {
        postId: 1,
        nickname: "nickname_1",
        title: "title_1",
        createdAt: new Date("11 October 2022 00:00"),
        updatedAt: new Date("11 October 2022 00:00"),
      },
      {
        postId: 2,
        nickname: "nickname_2",
        title: "title_2",
        createdAt: new Date("12 October 2022 00:00"),
        updatedAt: new Date("12 October 2022 00:00"),
      },
    ];

    mockPostsRepository.findAllPost = jest.fn(() => {
      return findAllPostReturnValue;
    });

    const allPost = await postService.findAllPost();

    // 1. 결과가 sort된 결과를 제대로 가져오는지 검증
    expect(allPost).toEqual(
      findAllPostReturnValue.sort((a, b) => {
        return b.createdAt - a.createdAt;
      })
    );

    // 2. 한 번 호출되는지 검증
    expect(mockPostsRepository.findAllPost).toHaveBeenCalledTimes(1);
  });

  test("Posts Service deletePost Method By Success", async () => {
    const findPostByIdReturnValue = {
      postId: 1,
      nickname: "nickname_1",
      title: "title_1",
      content: "content_1",
      createdAt: new Date("11 October 2022 00:00"),
      updatedAt: new Date("11 October 2022 00:00"),
    };

    mockPostsRepository.findPostById = jest.fn(() => {
      return findPostByIdReturnValue;
    });

    const deletePost = await postService.deletePost(1, "0000");

    // 1. 1번 호출, 입력받는 인자는 postId
    expect(mockPostsRepository.findPostById).toHaveBeenCalledTimes(1);
    expect(mockPostsRepository.findPostById).toHaveBeenCalledWith(1);
    // 2. postId, password, deletePost Method가 호출
    expect(mockPostsRepository.deletePost).toHaveBeenCalledTimes(1);
    expect(mockPostsRepository.deletePost).toHaveBeenCalledWith(1, "0000");
    // 3. return값이 findPostById의 반환된 결과와 일치
    expect(deletePost).toMatchObject({
      postId: findPostByIdReturnValue.postId,
      nickname: findPostByIdReturnValue.nickname,
      title: findPostByIdReturnValue.title,
      content: findPostByIdReturnValue.content,
      createdAt: findPostByIdReturnValue.createdAt,
      updatedAt: findPostByIdReturnValue.updatedAt,
    });
  });

  test("Posts Service deletePost Method By Not Found Post Error", async () => {
    const findPostByIdReturnValue = null;
    mockPostsRepository.findPostById = jest.fn(() => {
      return findPostByIdReturnValue;
    });

    try {
      const deletePost = await postService.deletePost(90, "0000");
    } catch (error) {
      // 1. postId 입력한 findPostById Method 1번 호출
      expect(mockPostsRepository.findPostById).toHaveBeenCalledTimes(1);
      expect(mockPostsRepository.findPostById).toHaveBeenCalledWith(90);
      // 2. return 된 findPostById의 결과가 존재하지 않을 때 에러발생
      expect(error.message).toEqual("Post doesn't exist");
    }
  });
});

expect를 이용해 Mock의 호출 횟수, 호출 인자만을 검증했던 성공 케이스와 다르게,
에러 케이스의 경우 try catch를 이용해 에러 핸들링을 하고, 에러가 발생한 경우 테스트 코드가 동작하도록 구현하였습니다.

expect(error.message).toEqual("Post doesn't exist"); 와 같이 에러메시지를 검증하여 원하는 에러가 정상적으로 발생하였는지까지 검증 완료

profile
개발자 꿈나무

0개의 댓글