[Jest] 특정 테스트 파일의 config 만 업데이트 하기

HYUNGU, KANG·2023년 7월 19일
3

일반적으로 Jest 를 실행할때는 환경별로 jest config 설정을 파일별로 다르게 지정해놓고 많이 사용들을 한다.
jest 의 config 에는 setupFiles 라는 옵션이 있는데, 테스트가 실행되기 전에 해당 파일을 import 해서
전역적으로 필요한 글로벌 값이라던가 mocking 등을 미리 세팅해두고 테스트마다 자동으로 적용할 수 있게 도와주는 설정이다.

// jest.config.js
module.exports = {
  ...
  setupFiles: [
    './react-native.setup.js',
    './dom.setup.js',
    './mock.setup.js',
    './global.setup.js'
  ],
}

물론 jest config 파일을 분리하는것이 편하기는 하지만, 일반적으로 모든 환경은 똑같은 상황에서 특정 파일에 대해 특정 setup file 만 제거하고 싶은 상황이 생겼다.

내 경우 node.js 환경에서 fetch polyfill 을 추가해주는 파일을 설정해놨고, import 시점에 코드가 실행되면서 fetch 가 없으면 폴리필을 설정을 해준다.

// jest.fetch-setup.js
import 'whatwg-fetch'

그런데 제품에 작성된 코드중에는 이런식으로 polyfill 을 강제로 설정할 수 있는 옵션이 있었고

if (shouldImportFetchCompat) {
  global.fetch = null;
}

if (!global.fetch) {
  await import('whatwg-fetch');
}

이를 테스트하기 위해서는 위의 jest.fetch-setup.js 파일을 import 하지 말아야 await import('whatwg-fetch'); 코드를 통해서 fetch polyfill 이 정상적으로 채워질 수 있는 상황이었다.

  1. 셋업 파일: import whatwg-fetch >> 최초 import 라, 폴리필 설정해주는 코드가 실행됨.
  2. 코드: global.fetch = null;
  3. 코드: import whatwg-fetch >> 이미 import 했던 모듈이라, 폴리필 설정해주는 코드가 재실행되지 않음.

위의 1번 스텝이 사라져야 정상적으로 polyfill 이 추가되는 상황..!

이 테스트만 따로 config 파일을 분리해서 돌리기는 뭐해서 방법이 뭐가 있을까 찾아보다가
jest 의 docblock 이란것을 발견했다. (https://jestjs.io/docs/jest-platform#jest-docblock)

대충 간단하게 설명하면, jsdoc 을 jest 가 파싱해주는 기능이다.

이걸 커스터마이징해서 핸들링 하려면, custom testEnvironment 를 만들어야 한다.
https://jestjs.io/docs/configuration/#testenvironment-string

일단 custom-env-jsdom.js 파일을 생성하고, jest config 의 testEnvironment 에 지정하자.

// jest.config.js
module.exports = {
  ...
  testEnvironment: './custom-env-jsdom.js',
}

나는 jsdom 환경을 사용하고 있었으므로, jest-environment-jsdom 환경을 상속해준다.

const JSDOMEnvironment = require('jest-environment-jsdom');

class CustomEnvironment extends JSDOMEnvironment {
  constructor(config, context) {
    super(config, context);
    this.applyDocblock(context.docblockPragmas, config);
  }
  applyDocblock(docblockPragmas, config) {
    // Do something
  }
}

JSDOMEnvironment 또한 역시 JestEnvironment 를 상속하고 있는데
정의돼있는 메소드들을 override 해서, VM 에 각 환경별로 필요한 설정들을 미리 지정할 수 있다.

나는 거창한거 필요없고, setupFiles 만 필터링 하면 되니까 context.docblockPragmas 에 읽어온 주석으로 특정 파일을 필터링 하면 된다.

기본적으로 key-value 형식의 오브젝트로 값이 들어오고
주석을 어떻게 다냐에 따라서 다른데...

하나를 달면 하나가 들어오고

// web.test.js

/**
 * @ignore-setup-files react-native.setup.js
 * */
describe('web-test', ...)

         
         
// context.docblockPragmas
docblockPragmas = {
  'ignore-setup-files': 'react-native.setup.js'
}

여러개를 달면 배열로 들어온다.

// react-native.test.js

/**
 * @ignore-setup-files dom.setup.js
 * @ignore-setup-files mock.setup.js
 * */
describe('react-native-test', ...)

         
         
// context.docblockPragmas
docblockPragmas = {
  'ignore-setup-files': ['dom.setup.js', 'mock.setup.js']
}

이 값을 이렇게 저렇게 다뤄서, 내가 무시하겠다고 선언해놓은 파일들을 config 의 setupFiles 에서 제거해주면 된다.

const JSDOMEnvironment = require('jest-environment-jsdom');

class CustomEnvironment extends JSDOMEnvironment {
  constructor(config, context) {
    super(config, context);
    this.applyDocblock(context.docblockPragmas, config.projectConfig);
  }
  
  applyDocblock(docblockPragmas, config) {
    const value = docblockPragmas['ignore-setup-files'] ?? [];
    const values = [value].flat();
    config.setupFiles = config.setupFiles.filter(file => values.every(filter => !file.includes(filter)));
  }
  
  async teardown() {
    // recover from here
    super.teardown();
  }
}

이렇게 하면, 특정 파일에만 특정 config 를 자유롭게 갈아치울 수 있다.
*주의점: 이렇게 변경한 설정들은 teardown 메소드를 오버라이드 해서 복구해주는 작업이 필요하다. 안그러면 다른 테스트 실행시에도 영향을 미친다.

profile
JavaScript, TypeScript and React-Native

1개의 댓글

comment-user-thumbnail
2023년 7월 19일

많은 도움이 되었습니다, 감사합니다.

답글 달기