Nest.js jest 단위테스트 Unit Test

jsonLee·2024년 1월 30일

Nest.js

목록 보기
1/1
post-thumbnail

서론

최근에 나는 Nest.js를 사용하여 간단한 서비스를 개발하는 중이다 이 프로젝트에서 중요한 것 중 하나는 테스트 주도 개발(TDD)을 통해 안정적이고 신뢰성 있는 코드를 작성하는 것인데. 이 블로그 포스트에서는 Nest.js의 BlogService 클래스에 대한 TDD 개발 과정을 공유하고자 한다.

spec

  • nest.js
  • typescript
  • mongoDB

1. BlogService 클래스 작성

import { Injectable, Param } from '@nestjs/common';
import { Blog } from './blog.schema';

interface BlogInterface {
  getAllPosts(): Promise<Blog[]>;
  createPost(post: any): Blog;
  updatePost(id: string, post: any): Blog;
  deletePost(id: string): void;
  getPost(id: string): Promise<Blog>;
}

@Injectable()
export class BlogService implements BlogInterface {
  getAllPosts(): Promise<Blog[]> {
    throw new Error('Method not implemented.');
  }
  createPost(post: any): Blog {
    throw new Error('Method not implemented.');
  }
  updatePost(id: string, post: any): Blog {
    throw new Error('Method not implemented.');
  }
  deletePost(id: string): void {
    throw new Error('Method not implemented.');
  }
  getPost(id: string): Promise<Blog> {
    throw new Error('Method not implemented.');
  }
}

2. 테스트 작성

import { Test, TestingModule } from '@nestjs/testing';
import { BlogService } from './blog.service';
import { Blog } from './blog.schema';

describe('BlogService', () => {
  let service: BlogService;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [BlogService],
    }).compile();

    service = module.get<BlogService>(BlogService);
  });
  const postData = {
    id: '1',
    title: '블로그 제목',
    content: '블로그 내용.',
    name: 'json lee',
  };

  it('should be defined', () => {
    expect(service).toBeDefined();
  });
  describe('getAllPosts', () => {
    it('모든 posts 정보를 가져온다', async () => {
      // Arrange
      const mockPosts = [{ ...postData }];

      jest.spyOn(service, 'getAllPosts').mockResolvedValue(mockPosts);

      // Act
      const result = await service.getAllPosts();

      // Assert
      expect(result).toEqual(mockPosts);
    });
  });

  describe('createPost', () => {
    it('포스트 하나를 등록한다', () => {
      jest.spyOn(service, 'createPost').mockReturnValue(postData);
      const result = service.createPost(postData);
      expect(result).toEqual(postData);
    });
  });

  describe('updatePost', () => {
    it('포스트를 업데이트한다 ', () => {
      const id = '1';
      const expectedResult = postData;
      jest.spyOn(service, 'updatePost').mockReturnValue(postData);
      const result = service.updatePost(id, postData);
      expect(result).toEqual(expectedResult);
    });
  });

  describe('deletePost', () => {
    it('포스트 하나를 삭제한다 ', () => {
      const id = '1'; // Mock the id
      jest.spyOn(service, 'deletePost').mockImplementation(() => {});
      expect(() => service.deletePost(id)).not.toThrow();
    });
  });

  describe('getPost', () => {
    it('포스트 id를 전달받아 post 정보를 가져온다 ', async () => {
      const id = '1'; // Mock the id
      const expectedResult = postData; // Mock the expected result
      jest.spyOn(service, 'getPost').mockResolvedValue(postData);
      const result = await service.getPost(id);
      expect(result).toEqual(expectedResult);
    });
  });
});

3. 구현

1. repository 작성

import { Model } from 'mongoose';
import { PostDto } from './blog.model';
import { Blog, BlogDocument } from './blog.schema';
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';

export interface BlogRepository {
  getAllPost(): Promise<PostDto[]>;
  createPost(postDto: PostDto);
  getPost(id: String): Promise<PostDto>;
  deletePost(id: String);
  updatePost(id: String, postDto: PostDto);
}

@Injectable()
export class BlogMongoRepository implements BlogRepository {
  constructor(@InjectModel(Blog.name) private blogModel: Model<BlogDocument>) {}

  async getAllPost(): Promise<Blog[]> {
    return await this.blogModel.find().exec();
  }

  async createPost(postDto: PostDto) {
    const createPost = {
      ...postDto,
      createdDt: new Date(),
      updatedDt: new Date(),
    };
    this.blogModel.create(createPost);
  }

  async getPost(id: string): Promise<PostDto> {
    return await this.blogModel.findById(id);
  }

  async deletePost(id: string) {
    await this.blogModel.findByIdAndDelete(id);
  }

  async updatePost(id: string, postDto: PostDto) {
    const updatePost = { id, ...postDto, updatedDt: new Date() };
    await this.blogModel.findByIdAndUpdate(id, updatePost);
  }
}

2. model를 service클래스에 의존성 주입 및 비지니스 로직 구현

import { Injectable, Param } from '@nestjs/common';
import { Blog } from './blog.schema';
import { BlogRepository } from './blog.repository';
import { PostDto } from './blog.model';

interface BlogInterface {
  getAllPosts(): Promise<Blog[]>;
  createPost(post: PostDto): Blog;
  updatePost(id: string, post: PostDto): Blog;
  deletePost(id: string): void;
  getPost(id: string): Promise<Blog>;
}

@Injectable()
export class BlogService implements BlogInterface {
  constructor(private readonly blogRepository: BlogRepository) {}
  deletePost(id: string): void {
    throw new Error('Method not implemented.');
  }

  async getAllPosts() {
    return await this.blogRepository.getAllPosts();
  }

  createPost(postDto: PostDto): Blog {
    return this.blogRepository.createPost(postDto);
  }

  async getPost(id): Promise<PostDto> {
    return await this.blogRepository.getPost(id);
  }

  delete(id): void {
    this.blogRepository.deletePost(id);
  }

  updatePost(id, postDto: PostDto) {
    return this.blogRepository.updatePost(id, postDto);
  }
}

4. controller 작성 및 e2e test

profile
풀스택 개발자

0개의 댓글