Pipeline 테스트에서 경로를 인식하지 못할 때 삽질 기록

배준형·2023년 7월 26일
0

회사 코드에 react-testing-library를 적용했다. 적절히 테스트 코드를 추가하고 테스트를 돌렸을 때 모두 정상 Passed 된 것을 확인, 기쁜 마음으로 저장소에 Push 했으나 미리 설정해놓은 Pipeline에서 러너 내부 테스트는 실패하고 만다.

테스트가 로컬 환경에선 성공하고 Pipeline 러너 내부에선 실패하는 현상에 대해 삽질한 기록을 정리한다.

1. 문제

jest.config.ts


export default {
  clearMocks: true,
  collectCoverage: false,
  coverageDirectory: 'coverage',
  moduleDirectories: ['node_modules'],
  moduleNameMapper: {
    '^@repository/(.*)$': '<rootDir>/src/repository/$1'),
		// ...
  },

index.test.tsx

// ...
jest.mock('@repository/some/directory', () => ({
	// ...
})

jest 설정에서 @repository 경로를 맵핑해주고, 테스트 파일에서 mocking할 때 경로를 @repository를 이용해서 설정해줬다.

로컬환경에서 test 시 정상 동작 확인.

그런데, Bitbucket에 push 후 pipeline에서 실패.


실패의 원인은

● Test suite failed to run
    Configuration error:
    Could not locate module @repository/phsCenter/usePhsCenter mapped as:
    /opt/atlassian/pipelines/agent/build/src/repository/$1.
    Please check your configuration for these entries:
    {
      "moduleNameMapper": {
        "/^@repository\/(.*)$/": "/opt/atlassian/pipelines/agent/build/src/repository/$1"
      },
      "resolver": undefined
    }

라고 한다. 결국 로컬 환경에서의 경로와 파이프라인 러너 내에서의 경로 차이로 인해 발생한 문제인 것으로 판단.


2. 상대 경로 써보기

혹시 절대경로를 인식하지 못하는 것이면 상대경로는 해결할 수 있을 수도 있다고 생각하고 시도.

index.test.tsx

// ...
jest.mock('../../../repository/some/directory', () => ({
	// ...
})

결과

● Test suite failed to run
    Cannot find module '../../../repository/phsCenter/usePhsCenter' from 'current path'
// ...
    > 19 | jest.mock('../../../repository/phsCenter/usePhsCenter', () => ({
         |      ^

여전히 실패. 절대 경로일 때 맵핑 자체에 실패했다면 상대 경로에서는 경로를 찾지 못한다.

마찬가지로 로컬 환경에서는 모두 정상 Passed.


3. import문 추가

테스트 파일 내부에서 사용되지는 않지만 mocking 하려는 경우이기 때문에 코드 상단에 import문이 없다. 혹시 몰라 추가해봤다.

index.test.tsx

import someModule from '../../../repository/some/directory';

// ...
jest.mock('../../../repository/some/directory', () => ({
	// ...
})

결과

● Test suite failed to run
    Cannot find module '../../../repository/phsCenter/usePhsCenter' from 'current path'
// ...
    > 19 | jest.mock('../../../repository/phsCenter/usePhsCenter', () => ({
         |      ^

똑같은 에러 발생. 다른 방법으로 해결해보려 했다.


4. jest.mock 대신 jest.spyOn 사용

spyOn, mockImplementaion 메서드들을 사용하면서 jest.mock과 동일한 동작을 구현할 수 있고, spyOn은 import된 module을 인자로 받기 때문에 경로 문제가 해결될 수 있을 것이라 판단.

index.test.tsx

import * as someModule from '../../../repository/some/directory';

// ...
jest.spyOn(someModule, 'someMethod').mockImplementation(() => jest.fn());

결과

temp.test.tsx(9,37): error TS2307: Cannot find module '../../../repository/some/directory' or its corresponding type declarations.

다른 에러로 바뀌었지만 경로, 타입을 인식하지 못하는 것은 동일. 편집기 내부에서 해당 경로로 타고 들어가면 정상적으로 잘 나오고 타입 추론도 되지만 pipeline 러너 내부에서는 인식이 안되는 듯 하다.


5. resolver 추가

resolver는 Jest에서 모듈 경로를 해석하는 방법을 지정하는 옵션으로, 테스트 중에 모듈을 로드하고 해석해야 할 때, 어떤 방식으로 모듈 경로를 찾을지를 결정하기 위해 사용한다. resolver 값을 지정하지 않았거나 undefined로 지정한다면 Node.js의 모듈 해석 방식을 따르고, 직접 지정해서 어떤 방식을 사용할지 지정할 수 있다.

여태까지 resolver 설정을 해주지 않았으니 직접 지정해보려 했다.

그 중 알게된 jest-node-exports-resolver 모듈 해석 방식으로 적용했는데, 테스트에 별칭(alias)을 사용하거나, 특정 패턴에 따라 모듈 경로를 매핑하는 경우 jest-node-exports-resolver는 이런 모듈 경로를 해석하는 역할을 수행한다.

jest.config.ts

export default {
  // ...
  resolver: 'jest-node-exports-resolver',
};

결과

● Test suite failed to run
    Configuration error:
    Could not locate module @repository/phsCenter/usePhsCenter mapped as:
    /opt/atlassian/pipelines/agent/build/src/repository/$1.
    Please check your configuration for these entries:
    {
      "moduleNameMapper": {
        "/^@repository\/(.*)$/": "/opt/atlassian/pipelines/agent/build/src/repository/$1"
      },
			"resolver": /opt/atlassian/pipelines/agent/build/node_modules/jest-node-exports-resolver/index.js
		}

에러 모습은 똑같지만, resolver: undefined 라고 표시되던 것이 다르다.


6. 그 외

그 외에 자잘한 수정사항으로 pipeline을 계속 돌려봤다.

  • next js + pages 폴더 내부 test 파일 형태라서 pages 폴더 외부로 테스트 파일 분리
  • 스크립트를 통해 러너의 node, yarn, npm 버전을 확인해보고 만약 로컬 환경과 다르면 일치시켜 확인하기
  • jest config에서 roots, modulePaths에 추가하기

위 3가지 모두 동일한 에러를 보이면서 해결되지 않았다.


7. branch 및 library 버전

경로를 다시 눈으로, 손으로 확인해봐도 문제는 없었고, jest 설정도 문제가 될만한 부분은 발견하지 못했다. 결국 6번 까지의 방법에서 해결되지 않다가 해당 테스트 코드가 작업된 브랜치를 어느 브랜치 기준으로 생성했는지, 라이브러리 버전 문제는 없는지 확인했다.

부모 역할이 되는 브랜치를 A, 테스트 코드 작업이 되어 있는 브랜치를 B 라고 했을 때 A와 B의 형상이 달랐고, yarn.lock과 package.json 버전이 달라 충돌이 일어나고 있음을 확인했다.

우선 A를 최신화 시키고, B 브랜치에 merge 하여 합쳤다. merge 후 B 브랜치에 에러가 발생하는 부분이 있었고, 해당 에러를 수정한 후 다시 테스트를 돌려 보았다.


결과

…?? 테스트 코드와 config를 변경하지 않았고, 브랜치 싱크를 맞춰준 후 에러가 발생하는 부분을 수정했더니 파이프라인 통과가 되었다.



결론

테스트 코드나 jest config 또는 ts config 등의 설정은 문제가 없었던 것일 수도 있다. 결과적으로 해결 됐던 순간은 부모 브랜치와 싱크를 맞춘 후에 에러가 발생하는 부분을 수정해준 후에 정상 통과가 되었다.

스크립트가 yarn > yarn type-check > yarn test > yarn build 순서로 이루어져 있었는데, 만약 yarn build가 먼저 이루어졌다면 더 빠르게 에러를 해결했을 수도 있겠다 하는 생각이 들었다.

애초에 모든 과정은 필요 없었고 처음부터 정상이었을 수도 있겠다 하는 생각이 들었다. 다음에 같은 일이 발생한다면 부모 브랜치와 싱크를 먼저 맞춰보고 yarn, npm 패키지에 문제가 없는지 확인을 먼저 해보는 습관을 들여야겠다.

profile
프론트엔드 개발자 배준형입니다.

0개의 댓글