가장 비용이 적게 든다는 단위 테스트를 열심히 작성한다. 하나의 컴포넌트에 필요한 테스트 케이스가 10개를 넘어갈 때도 있지만 옆에서 테스트는 필요하다고 한다. 아직 하나의 화면도 제대로 렌더링되고 있지 않지만 괜찮다. 테스트 커버리지가 높으니 나중에 큰 도움이 될 것이다. Show 작업이 남은 컴포넌트가 수십개는 되는 상황인데 갑자기 디자인이 변경되었다. 컴포넌트의 계층 구조가 변경되면서 내 컴포넌트 테스트 코드는 하나 둘씩 빨간 피를 토하기 시작했다.
UI 검증을 위한 테스트컴포넌트는 브라우저에 렌더링이 될 때, 부모 컴포넌트의 영향을 많이 받는다. 그리고 다른 컴포넌트에 영향을 준다. 애초에 그럴 수 밖에 없다. children에 또다른 컴포넌트가 들어온다면 CSS Style 속성에 의해 애써 만든 버튼 컴포넌트의 UI가 깨질 수 있다. 여기에서 핵심은 UI가 언제든 깨질 수 있다는 것이다. 위에서 언급한 1~4번에 모두 제대로 테스트를 통과함에도 불구하고 UI는 언제든지 브라우저에서 깨질 수 있다. 즉 HTML Tree 상 제대로 된 위치에 렌더링이 되었더라도 부모 컴포넌트에 의해 UI가 망가질 수 있다. className이 제대로 변경되었다고 하더라도 스타일이 제대로 안 들어갔을 수도 있다. 그렇다면 우리는 이 버튼 컴포넌트의 테스트 코드를 작성함에 있어 변경에 자유로울 수 있을까? 버그가 없다고 확신할 수 있을까? 왜 테스트해야 하는지 명심하기사용자가 우리가 만든 애플리케이션을 사용할 때, 의도한대로 동작하는 것에 대해 확신을 갖기 위해 테스트를 작성한다. 우리가 의도한 것이 사용자의 동작에 의해 제대로 작동하는지를 테스트해야 한다. 다시 버튼 컴포넌트의 예시로 돌아가자. 1~4번 중, 사용자의 동작은 무엇일까? 사용자가 버튼을 클릭하는 행위. 4번 뿐이다. 우린 4번에 집중하면 된다. ‘동작’에 집중하자정적인 UI는 잠시 미뤄두고 동적인 동작에 집중하자. 하지만 4번 그대로를 테스트 하지 않는다. 여기서 ‘그대로’ 테스트 한다는 것은 이벤트에 클릭 이벤트가 제대로 트리거(trigger or dispatchEvent)되는지 테스트 하는 것을 의미하며 이것은 플랫폼을 믿지 않는 것이라고 생각한다.
사용자가 버튼을 클릭했을 때, 이벤트가 발생하여 어떠한 부수 효과(side effect)를 가져오는가를 테스트 한다. 버튼을 클릭하여 어떤 컴포넌트의 상태가 변경될 수 있고 다른 페이지로 이동할 수도 있고 상태에 따라 모달 컴포넌트가 보이게 될 수도 있다. 방금 언급한 것들이 애플리케이션의 흐름(Flow)이 되며 프론트엔드 환경에서 비즈니스 로직에 해당된다. 그리고 이것은 개발자가 애플리케이션 코드를 작성할 때 의도한 동작에 해당된다. 화면이 잘 그려질지, 내가 그래서 무엇을 테스트하는가?동작을 테스트할 것이고 동작이란 사용자로부터 발생한 이벤트의 부수효과를 의미한다. 그리고 이 부수 효과들이 발생하게 되는 조건들과 결과를 테스트 한다. 대부분의 테스트 코드가 커버(cover)하게 되는 영역은 뒤에서 설명할 Store의 무엇을 테스트하지 않을 것인가?무엇을 테스트 할 지 결정했다면 무엇을 테스트하지 않을지도 자연스럽게 결정이 될 것이다. 사실 프론트엔드 테스트 전략을 세울 때는 무엇을 테스트하지 않을 것인가를 고민하는 것이 더 중요하다고 생각한다. 테스트에 들어가는 비용이 많이 들어가기 때문이다. 개발자는 평균적으로 버그를 고치는데 매주 4-8 시간 정도를 사용한다고 합니다. 버그가 프로덕션(production)에 몰래 끼어들기 시작하면, 상황은 나빠지기만 합니다. 이 경우, 버그를 고치는데에는 5-10배 더 많은 시간이 듭니다. 이러한 이유로 UI 테스팅이 질 높은 사용자 경험을 전달하는데 필수적이지만, 동시에 엄청난 시간 낭비가 될 수 있습니다. 코드를 변경할 때마다 모든 테스트를 수작업으로 하나하나 하려하면 일이 지나치게 커집니다. 이런 작업 흐름(workflow)를 자동화해서 개발자가 코드를 push할 때마다 테스트가 작동하게 만들 수 있습니다. 테스트들은 백그라운드에서 실행되고 완료되면 결과를 보고합니다. 이는 회귀(regressions) 에러를 자동으로 감지할 수 있게 해줍니다. 이 챕터에서는 어떻게 이런 작업 흐름을 Github Actions을 이용해 구축할 수 있는지 보여줍니다. 더불어, 테스트 실행을 최적화하는 방법도 짚어보겠습니다. 지속적인 UI 테스트코드 리뷰는 개발자가 되는데 중요한 부분입니다. 여러분이 버그를 조기에 발견하고 높은 코드 품질을 유지할 수 있게 돕습니다. 풀 리퀘스트(Pull request)가 프로덕션 코드를 망가트리지 않는다는 걸 보장하기 위해서, 여러분은 보통 코드를 pull 해와서 테스트 스위트를 로컬에서 실행해볼 겁니다. 이런 작업은 작업 흐름을 끊고 시간도 오래 걸립니다. 지속적 통합(CI)을 통해서, 손으로 하나하나 개입하지 않고도 테스트의 이득은 모두 챙길 수 있습니다. 여러분은 UI를 바꿀 수도, 새로운 기능을 만들 수도, 의존성을 최신으로 업데이트할 수도 있습니다. 풀 리퀘스트를 열었을 때, CI 서버는 자동적으로 포괄적인 UI테스트를 -시각적 요소, 구성, 접근성, 상호작용, 사용자 흐름- 실행해줍니다. 테스트 결과로 PR 뱃지를 받게 되고, 이를 통해 모든 검사 결과를 개략적으로 확인할 수 있습니다. 풀 리퀘스트가 모든 품질 검사를 통과했는지 한눈에 알 수 있습니다. 그 답이 "네(yes)"라면 실제 코드를 리뷰하는 단계로 넘어가면 됩니다. 그렇지 못한 경우에는, 로그를 살펴보면서 무엇이 잘못됐는지 찾습니다.
튜토리얼지난 다섯 챕터들에서는 Taskbox UI의 다양한 면을 어떻게 테스트할 수 있는지 보여주었습니다. 여기에 더해서 우리는 이제 Github Actions를 이용해 지속적 통합(CI)을 구축해볼 것입니다. CI 구축하기먼저 저장소에 우리가 만들 작업 흐름은 코드를 저장소의 브랜치에 push하는 순간 실행되고, 3가지 작업(job)을 가지고 있습니다.
.github/workflows/ui-tests.yml
여기서 몇 가지 주목할 것이 있습니다. 테스트 러너를 위해서, concurrently, http-server 와 wait-on 라이브러리들의 조합을 이용해 테스트를 수행할 스토리북을 빌드하고 서빙합니다. 크로마틱을 실행하려면, 마지막으로, 새 commit을 만들고, 변경사항을 깃허브에 push하면, 작업 흐름이 실행되는 걸 볼 수 있어야 합니다! 의존성 캐시하기각 작업은 독립적으로 실행되고, 이는 CI 서버가 의존성을 세 작업 모두에서 설치해야 한다는 뜻입니다. 때문에 테스트 실행은 느려집니다. 이를 피하기 위해서 의존성(dependencies)을 캐시해놓고, 오직 lock file이 변경되었을 때에만 .github/workflows/ui-tests.yml
나머지 세 작업도 성공! 여러분은 테스팅 작업 흐름을 자동화했습니다. 이제 PR을 열면 알아서 제스트(Jest), 크로마틱 그리고 사이프레스를 병렬로 실행하고 PR 페이지에 그 결과를 보여줄 것입니다. UI 테스팅 작업 흐름을 정복하기테스트 작업 흐름은 스토리북을 이용해 컴포넌트를 분리시키는 것에서부터 시작합니다. 코드를 짜는 동안 검사를 실행하면서 더 빠른 피드백 고리(feedback loop)를 만들 수 있습니다. 마지막으로 여러분의 모든 테스트 스위트를 지속적 통합(Continuous Integration)을 이용해서 실행해보도록 합시다. |