한국어
  • 테스트
  • 엔드 투 엔드 테스트
  • 통합 테스트
  • 개발
  • 생산성
  • jest
  • puppeteer

jest-puppeteer 를 사용하여 엔드 투 엔드 테스트 작성에 대한 빠른 가이드

이 글은 간단한 투두 앱을 예시로 사용하여 jest-puppeteer 를 통한 효율적인 엔드 투 엔드 테스트 작성에 대한 빠른 가이드를 제공하며, 설정 과정, 자주 사용되는 API 및 실용적인 테스트 시나리오를 강조합니다.

Yijun
Yijun
Developer

Logto 의 품질 보장을 위해 우리는 jest-puppeteer 를 사용하여 엔드 투 엔드 자동화 테스트를 수행합니다. 이를 통해 Logto 개발을 중단 없이 신속하게 반복할 수 있습니다.

이 글에서는 간단한 투두 앱을 예시로 사용하여 효율적인 jest-puppeteer 테스트 스크립트를 작성하는 경험을 공유하고자 합니다. 우리의 목표는 여러분이 자신만의 프로젝트를 위한 jest-puppeteer 테스트 코드를 신속하게 시작할 수 있도록 돕는 것입니다.

jest-puppeteer 와 함께하는 엔드 투 엔드 테스트 소개

엔드 투 엔드 테스트는 사용자의 관점에서 애플리케이션이 제대로 작동하는지 확인하는 방법입니다. 이를 달성하기 위해 우리는 두 가지 필수 도구를 사용합니다: JestPuppeteer.

Jest 는 테스트와 어설션을 작성하는 데 편리한 API 를 제공하는 인기 있는 JavaScript 테스트 프레임워크입니다. 주로 단위 테스트 및 통합 테스트에 사용됩니다.

Puppeteer 는 Chrome 팀이 개발한 Node.js 라이브러리로, 헤드리스 Chrome 또는 Chromium 브라우저를 제어하기 위한 고수준 API 를 제공합니다. 이는 엔드 투 엔드 테스트에서 브라우저 상호작용을 자동화하는 데 이상적입니다.

jest-puppeteer 는 Puppeteer 와 함께 엔드 투 엔드 테스트를 가능하게 해주는 Jest 프리셋입니다. 이를 통해 새로운 브라우저 인스턴스를 시작하고 이를 통해 웹 페이지와 상호작용하는 간단한 API 를 제공합니다.

이제 필수 도구들에 대한 기본적인 이해를 바탕으로 테스트 환경 설정에 대해 알아보겠습니다.

테스트 환경 설정

jest-puppeteer 를 사용한 엔드 투 엔드 테스트를 위한 테스트 환경 설정은 크게 세 가지 단계를 포함하는 간단한 과정입니다:

  1. 의존성 설치

프로젝트에서 (또는 새로운 프로젝트를 생성하여) 터미널을 열고 다음 명령을 실행하세요:

그런 다음 node_modules/puppeteer 로 이동하여 Puppeteer 에 필요한 Chromium 을 설치합니다:

  1. Jest 설정하기

다음 단계로 Jest 를 Puppeteer 와 원활하게 작동하도록 설정해야 합니다.

프로젝트의 루트 디렉토리에 Jest 설정 파일을 생성하세요 (예: jest.config.js). 이미 해당 파일이 없다면 말이죠. 이 설정 파일에서 jest-puppeteer 를 프리셋으로 지정합니다:

이 파일에서 필요한 대로 다른 Jest 설정을 사용자 정의할 수 있습니다. Jest 설정을 사용자 정의하는 방법에 대한 자세한 정보는 Jest 설정 문서를 참조하세요.

  1. 테스트 작성하기

프로젝트에 .test.js 확장자로 이름 붙여진 테스트 파일을 생성하세요. Jest 는 이러한 테스트 파일을 자동으로 검색하고 실행합니다.

다음은 Jest 문서 에서 온 예제입니다:

그런 다음 다음 명령어를 실행하여 테스트를 실행하세요:

이 세 가지 단계를 따라하면, jest-puppeteer 를 사용하여 엔드 투 엔드 테스트를 수행할 수 있는 잘 구성된 테스트 환경을 갖추게 됩니다.

그러나 이 예제는 기본적인 예에 불과합니다. 환경 구성에 대한 자세한 정보는 다음 문서를 참조하십시오:

자주 사용되는 API

앞으로의 단계에서는 Jest, Puppeteer 및 jest-puppeteer 가 제공하는 API 를 사용하여 테스트를 도와줄 것입니다.

Jest 는 주로 테스트를 구성하고 예상 결과를 어설션하는 API 를 제공합니다. 자세한 내용은 문서 에서 확인할 수 있습니다.

Puppeteer 의 API 는 주로 브라우저와 상호작용하는 데 사용되며, 이를 테스트에 활용하는 것이 직관적이지 않을 수 있습니다. 제공되는 기능을 이해하려면 Puppeteer 문서를 참조하십시오. 이후 테스트 예시에서 일부 일반적인 사용 사례를 다룰 것입니다.

Puppeteer 의 API 는 원래 테스트를 위해 설계된 것이 아니기 때문에 테스트 코드를 작성하는 것이 어려울 수 있습니다. 그러므로 Puppeteer 테스트 작성의 편의성을 위해 jest-puppeteer 는 expect-puppeteer 라는 간결하고 사용하기 쉬운 API를 제공하는 내장 라이브러리를 포함합니다. 이 라이브러리는 그 문서에서 다양한 유용한 기능을 소개하며 다음 예제와 같이 사용할 수 있습니다:

이제 이 라이브러리들이 제공하는 API 를 결합하여 우리의 테스트 시나리오를 완료할 차례입니다.

테스트 코드 작성 방법을 학습하기 위해 간단한 투두 앱을 사용해 보겠습니다.

간단한 투두 앱

http://localhost:3000 에서 실행 중인 간단한 투두 앱이 있다고 가정해보겠습니다. 이 앱의 주요 HTML 코드는 다음과 같습니다:

엔드 투 엔드 테스트를 수행할 때, 우리는 다음 시나리오를 다루고자 합니다:

  1. 앱의 URL 에 접근했을 때, 앱과 그 데이터가 올바르게 로드되어야 합니다.
  2. 아이템의 체크 버튼이 클릭할 수 있는지 확인합니다.
  3. 아이템 노트 안에 있는 외부 링크를 클릭하면 새 탭에서 링크가 열리는지 확인합니다.
  4. 양식을 통해 아이템을 추가할 수 있습니다.

나중에 이러한 시나리오를 검증하기 위한 테스트 코드를 작성할 것입니다.

앱과 데이터가 로드되었는지 확인

앱이 성공적으로 로드되었는지 판단하기 위해 다음 조건이 충족되는 것을 고려합니다:

  1. 앱의 URL 에 접근한 후 페이지에 "app" ID 를 가진 요소가 존재해야 합니다.
  2. 앱 내부의 데이터가 제대로 렌더링되어야 합니다.

따라서 우리는 다음과 같은 테스트 코드를 작성했습니다:

코드 내에서:

  • page.goto 는 브라우저에서 "http://localhost:3000" 을 입력하는 것과 동등하며, 이를 통해 원하는 URL 로 이동할 수 있습니다.
  • page.waitForSelector 는 특정 CSS 셀렉터가 특정 요소와 일치할 때까지 기다립니다. 기본 대기 시간은 30초입니다.
  • expect(page).toMatchElementexpect-puppeteer 라이브러리에서 사용되며, 이는 page.waitForSelector 와 유사하지만 요소 내부의 텍스트도 일치할 수 있습니다. 추가적으로, toMatchElement 의 기본 타임아웃은 500ms 입니다.

처음에는 완벽해 보일 수 있지만, 이 테스트를 실행하고 CI 환경에 배포했을 때, 여러 번 실행한 후 가끔 실패합니다. 실패 메시지는 다음과 같이 나타납니다:

실패 정보와 이 테스트가 대부분의 경우 성공한다는 사실을 기반으로, 앱에서 요청된 데이터가 앱 로드 후 첫 500ms 내에 항상 반환되지 않을 수 있음을 추론할 수 있습니다. 따라서 어설션을 수행하기 전에 데이터가 로드되기를 기다리기를 원합니다.

일반적으로 이를 달성하는 두 가지 일반적인 접근 방법이 있습니다:

  1. 어설션 대기 시간을 증가시킨다

오류 메시지에서 toMatchElement 의 기본 대기 시간이 500ms 로 설정되어 있음을 알 수 있습니다. 함수에 timeout 옵션을 추가하여 대기 시간을 다음과 같이 늘릴 수 있습니다:

이 접근 방식은 테스트 실패 빈도를 어느 정도 줄일 수 있지만, 데이터가 가져와지는 데 필요한 시간이 얼마나 소요될지 알 수 없기 때문에 문제를 완전히 해결하지는 못합니다.

따라서 우리는 대기 시간이 필요한지 확신할 수 있을 때만 이 접근 방식을 사용합니다. 예를 들어 "요소 위로 2초 이상 마우스를 올려 놓으면 툴팁이 나타난다" 와 같은 시나리오에서 사용합니다.

  1. 어설션을 수행하기 전에 네트워크 요청이 완료될 때까지 기다린다

이것이 올바른 접근 방법입니다. 데이터 요청에 시간이 얼마나 소요될지 알 수 없지만, 네트워크 요청이 완료될 때까지 기다리는 것은 항상 안전한 선택입니다. 이 시점에서 우리는 page.waitForNavigation({ waitUntil: 'networkidle0' }) 를 사용하여 네트워크 요청이 끝날 때까지 기다릴 수 있습니다:

이 방법으로 우리는 App 과 그 로드된 데이터에 대한 어설션을 실행하기 전에 네트워크 요청이 이미 종료되었는지 확인할 수 있습니다. 이로써 테스트는 일관되게 올바른 결과를 생성합니다.

특정 버튼을 클릭할 수 있는지 확인

다음으로, 우리는 항목 안의 체크 버튼을 클릭할 수 있는지 기능을 테스트하고자 합니다.

예제 앱에서는 모든 항목이 동일한 구조를 공유하고 있습니다. 이들의 체크 버튼은 CSS 셀렉터 li[class$=item] > button 을 가지고 있으며, 버튼 텍스트는 항상 "Check" 입니다. 이는 특정 항목의 체크 버튼을 직접 지정하여 클릭할 수 없음을 의미합니다. 따라서 새로운 해결책이 필요합니다.

만약 우리가 "Read Logto get-started document" 항목의 체크 버튼을 클릭하고 싶다면, 이를 두 단계로 나누어 해결할 수 있습니다:

  1. 먼저 해당 특정 항목에 대한 참조를 얻습니다.
  2. 그런 다음, 해당 항목 내에 위치한 체크 버튼을 클릭합니다.

코드에서 보시다시피, 우리는 toMatchElement 함수를 사용하여 itemName 이 "Read Logto get-started document" 로 설정된 항목을 일치시킵니다. 그런 다음, 우리는 readDocItem 참조를 사용하여 그 아래의 'Check' 텍스트 콘텐츠를 가진 버튼을 클릭합니다.

중요한 점은 readDocItem 을 얻을 때 사용되는 CSS 셀렉터가 li[class$=item]:has(div[class$=itemName]) 이라는 것입니다. 이 셀렉터는 항목의 루트 li 요소와 일치하며, 나중에 li 태그 아래에 있는 button 을 클릭할 의도이기 때문에 itemName 내부의 요소와 일치하지 않습니다.

expect(readDocItem).toClick 의 사용법은 toMatchElement 와 유사합니다. 예제 코드에서 우리는 버튼의 textContent 를 더 일치시키기 위해 { text: 'Check' } 을 전달합니다. 하지만 버튼의 텍스트 내용을 일치시킬지 여부는 필요에 따라 선택할 수 있습니다.

외부 링크가 새 탭에서 열리는지 확인

다음으로, 우리는 항목 노트 내에서 외부 링크를 클릭할 때 새 탭에서 해당 링크가 열리는지 여부를 테스트하고자 합니다.

예제 앱에서는 "Read Logto get-started document" 항목이 노트 내에 Logto 문서로 연결되는 외부 링크를 가지고 있는 것을 확인했습니다. 우리의 테스트 코드는 다음과 같습니다:

코드에서 우리는 toClick 을 사용하여 itemNotes 내 링크를 클릭합니다.

그 후에 우리는 browser.waitForTarget 을 사용하여 새로 열린 탭에서 "https://docs.logto.io/docs/tutorials/get-started" URL 을 캡처합니다.

탭을 얻은 후에는 target.page() 를 사용하여 Logto 문서 페이지의 인스턴스를 얻고 해당 페이지가 로드되었는지 확인합니다.

또한, 테스트를 마친 후 새로 열린 페이지를 닫는 것을 잊지 마세요.

양식을 통해 아이템을 생성할 수 있는지 확인

이번에는 아이템을 추가하는 기능을 테스트하고자 합니다.

양식에서 아이템 이름과 노트를 채운 후 "Add" 버튼을 클릭하고 우리가 추가한 내용이 목록에 나타나는지 확인합니다:

여기서 우리는 expect(page).toFill(inputSelector, content) 함수를 사용하여 양식의 내용을 채운다는 것을 알 수 있습니다. 이 함수는 입력 필드의 모든 내용을 content 로 대체합니다.

기존 내용을 유지하면서 입력 필드에 몇 자를 더 추가하고 싶다면 page.type(selector, content) 을 사용하여 입력 필드에 원하는 내용을 직접 추가할 수 있습니다.

그러나 작성해야 할 필드가 많을 경우, 여러 번 toFill 을 호출하는 것은 번거로울 수 있습니다. 이 경우, 다음과 같은 접근 방식을 사용하여 한 번에 모든 내용을 채울 수 있습니다:

제공된 객체 키는 input 요소의 이름 속성입니다.

요약

우리는 jest-puppeteer 를 사용하여 엔드 투 엔드 테스트를 수행할 때 흔히 요구되는 테스트 요구사항과 그에 따른 코드 작성 기술을 소개하기 위해 간단한 예제를 다루었습니다. 이를 통해 여러분이 jest-puppeteer 테스트 코드를 작성하는 기본 사항을 빠르게 익히는 데 도움이 되기를 바랍니다.