일반적으로 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 이 정상적으로 채워질 수 있는 상황이었다.
- 셋업 파일: import whatwg-fetch >> 최초 import 라, 폴리필 설정해주는 코드가 실행됨.
- 코드: global.fetch = null;
- 코드: 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 메소드를 오버라이드 해서 복구해주는 작업이 필요하다. 안그러면 다른 테스트 실행시에도 영향을 미친다.
많은 도움이 되었습니다, 감사합니다.