ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • vitest에서 Found multiple elements by [data-testid= ]에러 해결하기
    카테고리 없음 2024. 11. 25. 07:37

    문제 상황

    vitest로 통합테스트를 작성하고 있었습니다.

    이 때 Found multiple elements by [data-testid= 에러가 나타났습니다.

    에러를 살펴보니 spec.tsx에서 render를 한 갯수만큼 DOM이 생겨나고 있었습니다.

    그래서 저는 cleanup이 제대로 되고있지 않다고 생각을 했고, @testing-library/react에서 cleanup함수를 찾았습니다.

    이를 afterEach에 넣고 해결을 하였습니다.

    // setup.ts
    
    import { afterEach } from 'vitest'
    
    afterEach(cleanup)
    

    하지만 의문점이 생겼습니다.

    제가 기억하기로는 자동을 cleanup을 시켜준다고 알고 있었고, 위의 문서를 보아도 테스트 프레임워크가 자동을 global afterEach에 주입을 해준다고 합니다.

    그래서 vitest github에서 이슈를 찾아보았습니다.

    vitest를 살펴보자

    issue에 따르면 vitest.config.js파일에 아래와 같이 추가하면 된다고 합니다.

    export default defineConfig({
       ...
      test: {
        globals: true,
        ...
      },
    })
    

    직접 해보니 이제 afterEach에서 cleanup을 해주지 않아도 cleanup이 자동으로 됩니다!

    이게 어떻게 동작하는지 궁금해 vitest의 코드를 살펴보았습니다.

    setup-common.ts를 보면 integration/globals의 registerApiGlobally함수를 실행하는것을 알 수 있습니다.

    registrationApiGlobally함수의 구현체는 아래와 같은데요, globalThis에 @vitest/runner들을 등록해주는것을 알 수 있습니다.

    // setup-common.ts
    if (config.globals) {
        (await import('../integrations/globals')).registerApiGlobally()
      }
    }
    
    // integrations/globals.ts 
    import { globalApis } from '../constants'
    import * as index from '../public/index'
    
    export function registerApiGlobally() {
      globalApis.forEach((api) => {
        // @ts-expect-error I know what I am doing :P
        globalThis[api] = index[api]
      })
    }
    
    // public/index.ts
    export {
      afterAll,
      afterEach,
      beforeAll,
      beforeEach,
      describe,
      it,
      onTestFailed,
      onTestFinished,
      suite,
      test,
    } from '@vitest/runner'
    
    // constants.ts
    export const globalApis = [
      // suite
      'suite',
      'test',
      'describe',
      'it',
      // chai
      'chai',
      'expect',
      'assert',
      // typecheck
      'expectTypeOf',
      'assertType',
      // utils
      'vitest',
      'vi',
      // hooks
      'beforeAll',
      'afterAll',
      'beforeEach',
      'afterEach',
      'onTestFinished',
      'onTestFailed',
    ]
    

    setup-common.ts에서 globals가 true라면 globalThis에 vi, vitest, afterEach와 같은 메서드들을 등록해주는것을 알 수 있습니다.

    다만 이렇게만 설정한다면 타입추론이 되지 않아 globalThis에서 vitest와 같은 메서드들을 가져왔을 때 타입스크립트 컴파일러는 오류를 뱉습니다.

    tsconfig.json에게 타입추론을 할 수 있도록 도와주면 이제 오류없이 사용할 수 있습니다.

    // tsconfig.json
    {
      "compilerOptions": {
        "types": ["vitest/globals"]
      }
    }
    

    @testing-library/react 코드를 살펴보자

    이제 vitest의 globals 옵션이 메서드들을 globalThis에 등록해준다는것을 알게 되었습니다.

    하지만 vitest에서는 afterEach에서 cleanup을 자동으로 해주는 부분을 찾을 수 없었습니다.

    그래서 @testing-library/react의 코드를 살펴보았습니다.

    @testing-library/react의 코드를 살펴보면 index.js에서 afterEach의 타입이 function이면 cleanup을 해주는것을 알 수 있습니다.

    요약

    1. defineConfig에서 globals를 true로 주지 않음
    2. globalThis에서 vitest에서 제공하는 api를 가져올 수 없었음
    3. @testing-library/react에서는 tyepof afterEach가 undefined이기 때문에 cleanup을 해주지 않았음

    그럼 왜 처음부터 globals: true를 하지 않았냐고 물어볼 수 있을것 같습니다.

    e2e테스트를 위해 playwright를 사용했던 적이 있습니다.

    이때 vitest.config에서 globals option을 true로 주었었는데요, playwright에서도 vitest와 이름이 같은 메서드들이 존재합니다.

    저는 vitest api들만 global로 사용하는것이 조금 어색하게 느껴졌습니다.

    ex)

    • viteset는 global로 test메서드를 가져옴
    • playwright는 import로 test메서드를 가져옴

    때문에 이번 프로젝트에서는 globals를 true로 설정하지 않았기 때문에 이런 상황을 처음 마주하게 되었습니다.

    댓글

Designed by Tistory.