jest 기반의 타입스크립트 테스트 코드 작성

scorpi0n·2020년 7월 20일
0

javascript

목록 보기
6/6

본내용은 https://www.notion.so/load28/jest-703fb758557a4e5e9952a8e2d0be61c9을
기반으로 작성되었습니다.

1. 설치

  • jest, typescript, @types/jest 설치 한다
  • @babel/core, @babel/preset-env, @babel/preset-typescript, babel-jest 설치한다

babel: jest를 ts로 작성하고 실행하기 위해서는 babel을 통해서 먼저 자바스크립트로 변환 해야한다.

babel/core는 코어 기능이며, babel/preset-env는 테스트 코드 안의 es6 모듈을 다른 모듈로 변환 시

켜주는 역활을 한다. 또한 타입스크립트를 변환해주는 역활을 하는 babel/preset-typescript를 설치

한다. 마지막으로 babel-jest는 jest가 실행 될때에 .babelrc 파일을 자동으로 추적하여 추가적인

커맨드 없이 babel을 실행하여 자동 변환된 다음, jest가 실행 될 수 있도록 한다.

babel을 설치 할 때, @가 붙은 것을 볼 수 있는데, babel 버전이 올라감에 따라 패키지 명이 변경된 것이므로, 구글링을 통해 생각없이 babel과 @babel을 혼재하거나 babel을 사용하면 에러가 발생하므로 주의해야 한다.

2. 설정

이제 설정 파일만 작성하면 된다.

.babelrc 파일을 생성하여 아래의 내용을 입력한다.

{
  "presets": [["@babel/preset-env", {"modules": false}], "@babel/preset-typescript"],
  "env": {
    "test": {
      "presets": [["@babel/preset-env"], "@babel/preset-typescript"]
    }
  }
}

tsconfig.json (타입스크립트 설정 파일)을 생성하여 아래의 내용을 입력한다.

{
  "compilerOptions": {
    "target": "es5",                          /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
    "module": "commonjs",                     /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
    "strict": true,                           /* Enable all strict type-checking options. */
    "esModuleInterop": true,                  /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
    "skipLibCheck": true,                     /* Skip type checking of declaration files. */
    "forceConsistentCasingInFileNames": true  /* Disallow inconsistently-cased references to the same file. */
  }
}

위의 tsconfig 설정 중 매우 중요한 설정들이 있는데 살펴보자.

module 은 어떠한 방식으로 모듈 형태로 컴파일 할지를 결정하는 속성이다.

여기서 중요한 것은 jest는 commonjs 방식으로 컴파일해야 정상적으로 동작한다. jest 자체가 commonjs로 작성되어서 그런듯하다.

다음으로 esmoduleInterop 설정이 있는데, 이것은 commonjs 모듈에서 es6 모듈을 다른 모듈 형태로 변환 할 수 있도록 하는 설정이다.

위에서 말했듯이, jest는 commonjs로 컴파일해야 되는데 테스트 코드에 es6 모듈 형식으로 export된 함수를 호출 한다면 에러가 발생 할 것이다.

그래서 esModuleInterop 설정을 주어서 컴파일 될때에 es6 모듈 형태로 import 된 것들에 대해서 같이 컴파일 하며, .babelrc에 설정되어 있는 @babel/preset-env 프리셋을 통해서 es6모듈을 commonjs 모듈로 변환해준다.

또한 "@babel/preset-env", {"modules": false} 값을 보면 modules를 false로 설정함으로써 es6 모듈을 사용하지 않도록 한것을 볼 수 있다.

이렇게 해서 es6 모듈과 commonjs 모듈이 같이 동작 할 수 있도록 설정한다.

이제 테스트 코드를 작성해보자

3. code

요즘 타입 안정성을 높이기 위해서 fp-ts 라이브러리를 보고 있는데, 해당 라이브러리를 이용해

함수를 작성하였다.

import { Option, some, none, fromNullable } from 'fp-ts/lib/Option';

export function findIndex<T>(
  as: T[],
  predicate: (a: T) => boolean
): Option<number> {
  const index = as.findIndex(predicate);
  return index === -1 ? none : some(index);
}

export function find<T>(as: T[], predicate: (a: T) => boolean): Option<T> {
  const finedItem: T = as.find(predicate) as T;
  return fromNullable<T>(finedItem);
}

작성한 함수를 이용하여 테스트 케이스를 만들어보자. npx jest 또는 jest 커맨드를 실행하면

테스트 코드가 실행되는 것을 확인 할 수 있다.

import { find, findIndex } from '../src/fp-ts';
import { isSome, none, some } from 'fp-ts/lib/Option';

describe('option', () => {
  test('none/some option', () => {
    const result = findIndex<number>([1, 2, 3, 4], (a: number) => a === 1);

    expect(isSome(result)).toStrictEqual(true);
    expect(result).toStrictEqual(some(0));
  });

  test('fromNullable option', () => {
    const result_1 = find<number>([1, 2, 3, 4], (a: number) => a === 4);
    const result_2 = find<number>([1, 2, 3, 4], (a: number) => a === 5);

    expect(isSome(result_1)).toStrictEqual(true);
    expect(result_2).toStrictEqual(none);
  });
});

해당 코드에서 import { find, findIndex } from '../src/fp-ts'; 와 같이 상대경로를 사용했다. 상대 경로를 사용하는 것은 매우 좋지 않다.
왜 그런지는 실제 프로젝트에서 모든 경로를 상대경로로 설정한 다음
프로젝트의 디렉토리 구조를 변경해보면 답이 나온다.
모든 import 문을 수정해야 할 것이다.

위의 내용을 토대로 절대 경로로 변경해보자

tsconfig 설정에 paths 값을 추가해주면 된다. 보통 절대경로를 구분하기 위해서 @기호를 추가해 준다. * 표시자를 통해서 해당 / 아래의 경로는 배열 안의 상대경로로 인식이 된다.

@fp-ts/test.ts 이런 식으로 사용하면 src/fp-ts/test.ts 로 해석이 될 것이다.

{
  "compilerOptions": {
    "target": "es5",                          /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
    "module": "commonjs",                     /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
    "strict": true,                           /* Enable all strict type-checking options. */
    "esModuleInterop": true,                  /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
    "skipLibCheck": true,                     /* Skip type checking of declaration files. */
    "forceConsistentCasingInFileNames": true,  /* Disallow inconsistently-cased references to the same file. */
    "baseUrl": ".",
    "paths": {
      "@fp-ts/*": ["src/fp-ts/*"],
      "@fp/*": ["src/fp/*"]
    }
  }
}

한가지 주의 할 것이, 이렇게 절대 경로로 바꾸게 되면 jest 테스트 코드에서는 절대 경로를 해석 할 수 가 없다.
그래서 jest.config.js 파일에 설정 값으로 nameMapper를 선언해주어야 한다.

nameMapper를 쉽게 선언하는 방법은 tsconfig-paths-jest 라는 오픈소스를 이용하면 편하다.

직접 설정해줘도 되긴하는데, 어차피 tsconfig의 path값을 동일하게 적용하는 것이라

굳이 중복된 데이터를 선언 할 필요는 없어 보인다.

ryohey/tsconfig-paths-jest

tsconfig-paths-jest를 yarn 또는 npm 설치하고 jest.config.js에 설정을 추가해주면 되는데

방법은 매우 간단하다.

// tsconfig.json 파일을 로딩
const tsconfig = require("./tsconfig.json")
// tsconfig-paths-jest 라이브러리를 require를 통해 호출하고 
// 로딩한 tsConfig 객체를 인자로 전달 한다
const moduleNameMapper = require("tsconfig-paths-jest")(tsconfig)

module.exports = {
	// 그럼 라이브러리가 tsconfig.json 안에 선언된 
  // paths 설정을 파싱하여 자동으로 주입해 준다.
  moduleNameMapper
}

위와 같이 설정하면 jest에서 타입스크립트에서 사용하는 절대경로를 인식 할 수 있게 된다.

전체 예시는 아래의 레파지토리를 참조하면 된다.

load28/fp-javascript

끝.

profile
이끄는 개발

0개의 댓글