✅ 개발 환경
Yarn Berry(yarn2)
React
TypeScript
Jest
Cypress
폴더 구조
📦project ... ┣ 📂.yarn ┣ 📂cypress ┃ ┣ 📂e2e ┃ ┃ ┗ 📂api ┃ ┃ ┃ ┗ 📜test.cy.js ┃ ┣ 📂fixtures ┃ ┃ ┗ 📜example.json ┃ ┗ 📂support ┃ ┃ ┣ 📜commands.js ┃ ┃ ┗ 📜e2e.js ┣ 📂public ... ┣ 📂src ┃ ┣ 📂api ┃ ┃ ┗ 📜sample.ts ┃ ┣ 📂utils ┃ ┃ ┗ 📜sample.ts ... ┣ 📂test ┃ ┗ 📜sample.test.ts ┣ 📜.eslintrc.json ┣ 📜.gitignore ┣ 📜.pnp.cjs ┣ 📜.pnp.loader.mjs ┣ 📜.prettierrc.json ┣ 📜.yarnrc.yml ┣ 📜cypress.config.ts ┣ 📜jest.config.ts ┣ 📜package.json ┣ 📜README.md ┣ 📜tsconfig.json ┗ 📜yarn.lock
"baseUrl": "src"
추가ts.config.json
에서 "baseUrl": "src"
추가한다.
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"types": ["cypress"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"baseUrl": "src"
},
"include": ["src"]
}
baseUrl
은 상대 경로를 계산할 기본 디렉토리를 지정한다. 여기서는 "src"를 기본 디렉토리로 설정한다.
jest.config.js
에 아래 내용 추가// Jest 특정경로 테스트 진행 X
testPathIgnorePatterns: ['<rootDir>/cypress/'],
// Jest 절대경로 사용
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/$1',
'^utils/(.*)': '<rootDir>/src/utils/$1',
},
실행 에러 jest-config tried to access ts-node
Error: Jest: Failed to parse the TypeScript config file C:\Users\smt\Desktop\Remon\dpms_fe\jest.config.ts
Error: jest-config tried to access ts-node (a peer dependency) but it isn't provided by its ancestors; this makes the require call ambiguous and unsound.
Required package: ts-node (via "ts-node\package.json")
Required by: jest-config@virtual:6a53c2378e7cba6f9934905320933b15fb461ebc13bfaf566ba916aee80e6cee2098933b51ddd0bc9e6d3f79ebcbb07767b5e1fa4abadd019ff19d8b2cf1bfe9#npm:29.5.0 (via C:\Users\smt\Desktop\Remon\dpms_fe\.yarn\**virtual**\jest-config-virtual-9dc828f591\0\cache\jest-config-npm-29.5.0-15ac67fe8b-c37c4dab96.zip\node_modules\jest-config\build\readConfigFileAndSetRootDir.js)
이 오류는 jest-config
모듈이 ts-node
모듈에 대한 peer dependency를 찾을 수 없기 때문에 발생하는 것이다. 이 문제를 해결하기 위해서는 ts-node
모듈을 설치한다.
yarn add -D ts-node
실행 에러 Unable to compile TypeScript
Error: Jest: Failed to parse the TypeScript config file C:\Users\smt\Desktop\Remon\dpms_fe\jest.config.ts
TSError: ⨯ Unable to compile TypeScript:
jest.config.ts:5:1 - error TS2591: Cannot find name 'module'. Do you need to install type definitions for node? Try npm i --save-dev @types/node and then add 'node' to the types field in your tsconfig.
5 module.exports = {
이 오류는 jest.config.ts
에서 TypeScript에서 import/export 구문을 인식하지 못하기 때문에 발생할 수 있다. 그래서 module.exports
대신에 export default
를 사용하여 Jest 구성을 내보내야한다.
yarn add -D @jest/types
export default
를 사용하기 위해 @jest/types 설치한다.
ts.config.js
내용 변경import type { Config } from '@jest/types';
const config: Config.InitialOptions = {
preset: 'ts-jest',
testEnvironment: 'node',
verbose: true,
collectCoverage: true,
// Jest 특정경로 테스트 진행 X
testPathIgnorePatterns: ['<rootDir>/cypress/'],
// Jest 절대경로 사용
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/$1',
'^utils/(.*)': '<rootDir>/src/utils/$1',
},
};
export default config;
간단한 테스트를 만들어서 절대경로가 제대로 적용되었는지 확인해보자
src/utils
폴더에서 test를 위한 간단한 sample 함수가 있는 sample.ts
를 만든다.export function sam(props: { a: number; b: number }) {
const { a, b } = props;
return a + b;
}
import { sam } from 'utils/sample';
it('test', () => {
expect(sam({ a: 1, b: 2 }));
});
it('ENV Test', async () => {
const testApi = process.env.REACT_APP_TEST_API;
if (testApi) {
expect(testApi).toBe('https://jsonplaceholder.typicode.com/posts/1');
}
});
만약 위와같이 “모듈 또는 해당 형식 선언을 찾을 수 없습니다. ts(2307)” 오류가 나온다면 tsconfig.json
파일에 include
에 어떤 파일들을 baseUrl 속성을 포함시킬지 작성해줘야한다.
{
"compilerOptions": {
"target": "ES2022",
"lib": ["dom", "dom.iterable", "esnext"],
"types": ["cypress"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"baseUrl": "src"
},
"include": ["src", "test/*.test.ts"]
}
jest test를 작성하는 폴더의 경로를 include에 작성한다.
yarn jest
로 적용을 확인한다.yarn add -D @cypress/webpack-preprocessor
웹팩 설정을 위한 @cypress/webpack-preprocessor 설치한다.
cypress.config.ts
에 절대경로 내용 추가const webpack = require('@cypress/webpack-preprocessor');
const path = require('path');
module.exports = {
e2e: {
baseUrl: 'https://localhost:3000',
setupNodeEvents(on, config) {
const options = webpack({
webpackOptions: {
resolve: {
extensions: ['.ts', '.tsx', '.mjs', '.cjs'],
alias: {
cypress: path.resolve(
__dirname,
'cypress',
'e2e',
'api',
),
api: path.resolve(__dirname, 'src', 'api'),
},
},
},
});
on('file:preprocessor', options);
return config;
}
}
};
const webpack = require('@cypress/webpack-preprocessor');
@cypress/webpack-preprocessor
패키지를 가져와서 webpack을 사용할 수 있도록 한다.const path = require('path');
path
모듈을 가져온다.module.exports
를 통해 Cypress의 구성을 내보낸다.e2e
객체로 정의되어 있다.baseUrl
: 테스트 시나리오에서 사용할 기본 URL을 설정한다.setupNodeEvents
: Cypress 플러그인 API의 setupNodeEvents
메서드를 사용하여 Node 이벤트를 설정한다. 이 메서드는 on
과 config
매개변수를 사용한다. setupNodeEvents
메서드 내부에서는 @cypress/webpack-preprocessor
를 사용하여 Webpack 설정을 정의한다.webpackOptions
: Webpack 설정을 정의하는 객체이다.resolve
: 모듈 해석에 대한 설정을 정의하는 객체이다.extensions
: 모듈 해석에 사용될 확장자를 정의하는 배열이다..ts
, .tsx
, .mjs
, .cjs
확장자를 사용할 수 있도록 지정한다.alias
: 모듈 별칭을 정의하는 객체이다.cypress
별칭api
별칭은 다른 경로로 해석되도록 설정되어 있다.on('file:preprocessor', options);
: 파일 프리프로세서 이벤트를 등록한다. 이를 통해 Webpack 프리프로세서가 파일 처리를 수행할 수 있도록 한다. 이 구성을 반환하여 전체 Cypress 구성이 완료된다.간단한 테스트를 만들어서 절대경로가 제대로 적용되었는지 확인해보자
src/api
폴더에서 test를 위해 오픈 api url이 들어있는 sample.ts
를 만든다.export const testUrl = 'https://jsonplaceholder.typicode.com/posts/1';
import { testUrl } from 'api/sample';
it('API Test', () => {
cy.request(testUrl).then(response => {
expect(response.status).to.eq(200);
expect(response.body.title).to.eq(
'sunt aut facere repellat provident occaecati excepturi optio reprehenderit',
);
});
});
it('ENV Test', () => {
cy.request(Cypress.env('TEST_API')).then(response => {
expect(response.status).to.eq(200);
expect(response.body.title).to.eq(
'sunt aut facere repellat provident occaecati excepturi optio reprehenderit',
);
});
});
yarn cypress
로 실행해본다.실행 에러 Can't resolve './commands'
Error: Webpack Compilation Error
Module not found: Error: Can't resolve './commands' in
위와 같이 실행에러가 뜬다면 cypress.config.ts
에 supportFile
속성 추가한다.
const webpack = require('@cypress/webpack-preprocessor');
const path = require('path');
module.exports = {
e2e: {
baseUrl: 'https://localhost:3000',
setupNodeEvents(on, config) {
const options = webpack({
webpackOptions: {
resolve: {
extensions: ['.ts', '.tsx', '.mjs', '.cjs'],
alias: {
cypress: path.resolve(
__dirname,
'cypress',
'e2e',
'Api',
),
api: path.resolve(__dirname, 'src', 'api'),
},
},
},
});
on('file:preprocessor', options);
return config;
},
supportFile: 'cypress/support/commands.js',
}
};
이렇게 하면 commands.js
파일이 Cypress에서 로드된다.
supportFile
에 지정한 파일은before
또는beforeEach
훅에서 사용할 수 있는 유틸리티 함수를 정의하거나, 브라우저 환경을 설정하는 코드를 작성하는 등의 용도로 사용된다.
이 속성을 사용하면 Cypress 실행 중에 로드할 수 있는 추가적인 스크립트 파일의 경로를 지정할 수 있다. 이 파일들은 모든 테스트 파일보다 먼저 로드된다.
yarn cypress
로 적용을 확인한다.yarn start
시 .eslintrc.json
files 오류[eslint] ESLint configuration in .eslintrc.json is invalid:
- Unexpected top-level property "files".
yarn start
를 했는데 위와 같은 오류가 나온다면 tsconfig.json
파일에서 "include": ["src", "cypress/**/*.ts"]
를 추가해주면 오류가 해결된다.
{
"compilerOptions": {
"target": "ES2022",
"lib": ["dom", "dom.iterable", "esnext"],
"types": ["cypress"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"baseUrl": "src"
},
**"include": ["src", "cypress/**/*.ts", "test/*.test.ts"]**
}
위와 같은 오류가 나오는 이유는 .eslintrc.json
파일에서 "files": ["cypress/**/*.ts"]
속성을 사용했기 때문이다.
"files" 속성은 ESLint가 린트링을 수행할 파일을 지정하는 속성인데, 현재 프로젝트에서는 cypress
폴더 아래에 있는 모든 하위 폴더에서 .ts
확장자를 가진 파일을 대상으로 ESLint 린트링을 수행하도록 설정하고 있다.
그러나 해당 오류는 .eslintrc.json
파일에서 예상치 못한 files
속성이 발견되었다는 것을 알려준다.
tsconfig.json
baseUrl
을 설정하면 TypeScript 컴파일러는 baseUrl을 기준으로 모듈을 찾습니다. 하지만 `"files": ["cypress//*.ts"]`** 설정을 추가하면 ESLint는 해당 경로 아래의 파일만 검사하고 그 외의 경로에 있는 파일은 검사하지 않게 됩니다. 이때 검사 대상에서 제외된 파일 중 baseUrl 설정에 따라 모듈을 찾을 수 없는 파일이 있어서 해당 오류가 발생한 것으로 보인다.
따라서 해당 오류를 해결하기 위해서는, tsconfig.json 파일에서 "include"
에 `"cypress//*.ts"`를** 추가해주면 된다.