본문 바로가기
Back-End/🐱NestJS (TypeScript) log

[NestJS 일기] 영화 api 만들기(3) - 유닛 테스팅 (unit testing) - jest

by 코딩하는 동현😎 2022. 6. 7.

유닛(unit) 테스팅

유닛 테스트란 전체를 테스팅하지 않고 소스 코드의 특정 모듈이나 함수등이 의도된 대로 정확히 작동하는지 부분부분 검증하는 테스트 입니다.

테스트 spec 파일

컨트롤러나 서비스 생성할때 딸려서 나오는 spec 파일이 테스트 코드를 작성하는 파일입니다.

movies.service.spec 테스트 파일 (기초 테스트)

import { Test, TestingModule } from '@nestjs/testing';
import { MoviesService } from './movies.service';

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

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

    service = module.get<MoviesService>(MoviesService);
  });

  it('should be defined', () => {
    expect(service).toBeDefined();
  });

  /////////여기부터 코드 예제//////////
  
  // 2+2 는 4가 되는지 테스트(기본예제) - should be 4 Test -> true
  it("should be 4" , ()=>{
    expect( 2 + 2 ).toEqual(4);
  })

  // should be 8 test - false 가 나올 예정
  it("should be 8" , ()=>{
    expect( 2 + 2 ).toEqual(8);
  })
});

it('테스트 이름' , function)

테스트 이름을 설정해주고, toEqual 함수를 이용해서  2+2 가 4와 일치하는 검사하는 테스트 입니다.

 

터미널에 이렇게 선언해주면 실시간으로 테스트가 진행될 것입니다.

$ npm run test:watch

예상대로 4가 되는 테스트는 합격하고 8이 되는 거짓 코드는 오류를 잡아주죠?

이렇게 터미널에서 정확히 어느 부분이 문제가 있는지 보여줍니다.

 

 

이제 본격적으로 service 파일을 테스트해 볼까요??


테스트 할 대상 (service 파일)

import { Injectable, NotFoundException } from '@nestjs/common';
import { Movie } from './entities/movie.entities';

@Injectable()
export class MoviesService {
    private movies : Movie[] = [];

    getAll() : Movie[]{
            return this.movies;
    }

    create(movieData){
        this.movies.push({
            id: this.movies.length + 1,
            ...movieData
        })
    }
    getOne(id:number) :Movie{
        const movie = this.movies.find(movie => movie.id === id);
        if (!movie) {
            throw new NotFoundException(`movie with id ${id} not found`);
        }
        return movie;
    }

    deleteOne(id:number){
        this.movies = this.movies.filter(movie => movie.id !== id)
        return;
    }

    update(id:number, updateData ){
        const movie = this.getOne(id)
        this.deleteOne(id)
        this.movies.push({...movie , ...updateData})

    }
}

-spec 파일-

!!설명은 코드 주석에다가 다 달아놨습니다!!

getAll 함수에서 반환값이 배열인지 테스트

import { Test, TestingModule } from '@nestjs/testing';
import { MoviesService } from './movies.service';

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

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

    service = module.get<MoviesService>(MoviesService);
  });

  it('should be defined', () => {
    expect(service).toBeDefined();
  });

  /*여기부터 코드 예제*/

  // getAll 함수에 대한 여러 테스트이므로 describe(이름)을 씁니다
  describe('getAll function' , ()=>{

    // getAll 함수의 반환값이 배열인지 테스트(1)
    it('should be an Array',()=>{

      //movie.service 파일의 getAll 함수의 반환값을 저장
      const result = service.getAll();

      //테스트
      expect(result).toBeInstanceOf(Array);
    })
  })
});

getAll function에 속해있는 should be an Array 테스트가 성공한것을 볼 수 있습니다.


getOne - 내부에서 함수실행 , 예외처리에 대한 테스트

describe('getOne' ,()=>{
    // create 함수로 무비하나를 추가하고 반환했을때,
    // 제대로 된 무비가 나오는지 테스트
    it('should return a movie' , ()=>{
      service.create({
        title : 'Test Movie',
        genres : ['test'],
        year : 2002
      });
      const movie = service.getOne(1);
      // 존재 여부를 테스트
      expect(movie).toBeDefined();
      // id , title 등의  정보가 제대로 들어갔는지 테스트
      expect(movie.id).toEqual(1);
      expect(movie.title).toEqual("Test Movie");
      expect(movie.year).toEqual(2002);
    });

    it('should return a not found error' , ()=>{
      try {
        service.getOne(999); //존재하지 않는 무비
      } catch (error) {
        expect(error).toBeInstanceOf(NotFoundException);
      }
    })
  });


deleteOne - 삭제 전후 총 영화 갯수 감소 , 404 에러 뜨는지 테스트

404 에러는 존재하지 않는 무비를 삭제할때 꼭 나와야하는 에러이고, 정상적으로 에러가 뜨는지 확인해보겠습니다.

describe('deleteOne', ()=>{
    it('deletes a movie' , ()=>{
      service.create({
        title : "test Movie",
        genres : ["test"],
        year : 2022
      })
      // 삭제 전후 무비 전제를 조회
      const beforeDelete = service.getAll();
      service.deleteOne(1);
      const afterDelete = service.getAll();

      // 삭제 됐다면 삭제 전후에 총 무비 갯수의 차이는 1이어야한다.
      expect(afterDelete.length).toBeLessThan(beforeDelete.length);
    })

    it('should return a 404 error' , ()=>{
      try {
        service.deleteOne(999); //존재하지 않는 무비
      } catch (error) {
        expect(error).toBeInstanceOf(NotFoundException);
      }
    })
  })

create - 무비를 생성하면 총 무비의 갯수가 증가하는지 테스트

describe('create' , ()=>{
    it('should create a movie' , ()=>{
      const beforeCreate = service.getAll().length;
      service.create({
        title : "test Movie",
        genres : ["test"],
        year : 2022
      })
      const afterCreate = service.getAll().length;
      // 무비 생성하면 영화 갯수가 늘어나야한다.
      expect(afterCreate).toBeGreaterThan(beforeCreate);
    })
  });

update - 무비의 제목을 수정하고 , 제목이 제대로 수정됐는지 테스트

describe('update' , ()=>{
    it('should update a movie' , ()=>{
      service.create({
        title : "test Movie",
        genres : ["test"],
        year : 2022
      });
      service.update(1 , {title : "updated title"});
      expect(service.getOne(1).title).toEqual("updated title");
    })

    it('should return a 404 error' , ()=>{
      try {
        service.update(999 , {title : "updated title"} ); //존재하지 않는 무비
      } catch (error) {
        expect(error).toBeInstanceOf(NotFoundException);
      }
    })
  })

테스트 실행 , 조회 결과

각 함수들(describe문)의 세부 테스트가 실행된 모습입니다.

반응형

댓글