테스트 코드는 왜 스펙인가

테스트 코드는 왜 스펙인가

cypress 가 앞으로도 다중 접속을 허용할 계획이 없다고 하여 e2e 테스트를 playwright 와 jest 조합으로 변경했다.

cypress 에서 익숙했던 selector 설정에서 다시 playwright 에서 지정한 selector 탐색 방법을 사용하다 보니 로컬 테스트를 통과시키는 과정도 꽤 험난했다.

이 과정에서 playwright 키워드로 검색 결과가 안 나오면 puppeteer 에서 어떻게 selector 를 인식하거나 element attribute 등을 다루는지 찾아보면 된다는 나름의 노하우도 얻었다. 최근에는 playwright 에서 page.isDisabled(‘selector’) 와 같은 나름 세련된 메소드를 제공하긴 한다.

어찌저찌 힘겹게 로컬에서 테스트를 성공시키면 그때부터 문제인게, 답도 없는 기도 시간에 들어가야 한다.

즉 로컬에서는 문제없이 통과하는 테스트가 CI/CD 단계에서는 실패하는 경우가 생긴다는 것인데, 일단 CICD 단계까지 거쳐야 하니 디버깅에 쏟는 시간이 배로 불어난다.

이 때 주로 접하는 에러 메시지는 timeout 시간을 초과했다는 메시지를 주로 본다.

그리고 이어서 따라오는 메시지는 테스트 시나리오에 따라가기 위해 지정한 element 에 지정한 이벤트가 발생하길 기다렸지만 끝내 timeout 때문에 예외를 던졌다고 한다.

여기서 지정한 이벤트란 다음과 같은 것들이다. 버튼 클릭, element 사라지기, 체크박스에 체크하기 등등.

파일을 업로드 하는 과정을 예로 들겠다.

  1. fixture 파일을 업로드한다.
  2. 업로드 대상을 선택하기 위해 미리보기 이미지가 나타난다.
  3. 미리보기 이미지는 개별 선택이 가능하고 전체 선택을 위한 체크 박스가 나타난다.
  4. 미리보기 이미지를 선택하지 않으면 업로드 확인 버튼은 disabled 상태이다.
  5. 전체 선택을 위해 체크박스를 체크한다
  6. 업로드 확인 버튼을 눌러 업로드한다.
  7. 원하는 파일을 업로드 했는지 확인한다.

이게 업로드 시나리오라고 생각했고 이 시나리오대로 테스트를 작성했을 뿐인데 CI/CD 에서 실패하는 것이다.

그럼 보통 이렇게 생각을 한다.

CI / CD 서버가 구려서 시나리오대로 진행하는 데에 좀 더 시간이 걸린다

즉 입장을 하는데에도 로컬보다 더 시간이 걸리고, 파일 업로드 과정 로컬에서조차 시간이 어느 정도 걸리는 작업이기 때문에 CI/CD 서버에서는 더 넉넉한 timeout 을 주어야 한다고 생각한다.

하지만 이 생각은 틀렸다.

CI / CD 에서 실패하는 테스트는 로컬에서도 실패한다고 생각해야 한다.

위에 내가 테스트 시나리오 과정으로 작성한 것과 실제 업로드 과정에는 세부적인 부분이 달랐다.

특히 테스트가 실패하는 가장 큰 원인은 바로 전체 선택을 위한 체크 박스 클릭이 문제였다.

이 체크박스 클릭은 서버에 업로드를 완료하고 서버로부터 메타 정보를 무사히 받아올 때까지 disabled 상태가 되는 상태를 가지고 있었다.

하지만 로컬에서 테스트할 때는 disabled 되는 상태가 너무나도 짧기 때문에 체크 박스를 클릭하려는 시점에 disabled 가 될 일이 거의 없었다. 하지만 CI/CD 서버에서는 체크박스를 클릭하기 이전에 먼저 체크박스가 disabled 상태인지 확인을 하고 disabled 라면 체크가 가능한 상태까지 기다려야만 한다.

그래서 CI/CD 를 위해 여러 selector 탐색 과정에 timeout 을 길게 배정해도, 3번 단계에서 이미 테스트는 진전을 하지 않고 있기 때문에 테스트가 실패하는 것이었다.

즉 위 테스트 시나리오의 3과 4 사이에 체크박스의 disabled 여부를 확인해야만 했던 것이다.

테스트 코드는 스펙이라는 말

테스트 코드가 스펙이라는 말은 굉장히 추상적이라고 생각한 적이 있다. 읽기 좋은 테스트 코드를 작성하지 못하기 때문일까, 과장 좀 많이 하면 테스트 코드를 읽는 과정이 실제 코드를 읽는 과정만큼 난해하다고 느낄 때가 있기 때문이다.

하지만 이번 일을 계기로 테스트 코드는 스펙이라는 말이 조금 더 와닿았다. 아직도 모호한 면이 있지만, 코드가 A -> B -> C 의 흐름을 가지고 있다면 테스트 코드도 A -> B -> C 를 따라가야 한다. 그리고 그것은 A -> B -> C 흐름의 명세가 될 것이다.

만약 테스트 코드가 A -> B -> C 의 흐름을 정확히 나타내고 있는데 테스트 코드가 실패한다면, 코드의 실제 흐름은 A -> B -> C 가 아니라 A -> C 는 아닌지, 또는 A -> B -> Z -> C 는 아닌지 확인할 필요가 있다.


Written by@irrationnelle
irrationnelle

GitHub