GitLab CI/CD와 WebdriverIO를 사용한 엔드 투 엔드 테스트

Tier: Free, Premium, Ultimate Offering: GitLab.com, Self-Managed, GitLab Dedicated

Review apps를 사용하면 좋습니다: 매번 Merge Request(또는 브랜치)마다 새 코드를 복사하고 배포하여 실제 프로덕션과 유사한 환경에 변경 사항의 영향을 평가하는 데 필요한 노력을 줄일 수 있습니다. 따라서 Dependencies.io와 같은 의존성 관리자를 사용하면 업데이트된 의존성으로 Merge Request을 제출할 수 있으며, 응용 프로그램이 여전히 올바르게 빌드되고 배포될 수 있음이 즉각 확인됩니다. 결국, 실행 중인 것을 직접 확인할 수 있습니다!

dependencies.io

그러나 새롭게 배포된 코드를 확인하여 여전히 예상대로 보이고 작동하는지 검사하는 것은 반복적인 매뉴얼 작업이며, 이는 자동화하기에 적합한 유력한 후보입니다. 여기서 자동화된 엔드 투 엔드 테스트가 등장합니다. 이는 응용 프로그램의 모든 레이어(프론트엔드부터 데이터베이스까지)의 올바른 기능을 요구하는 몇 가지 간단한 시나리오를 컴퓨터로 실행하는 것을 말합니다.

이 글에서는 이러한 종단간 테스트를 작성하는 방법과 GitLab CI/CD를 설정하여 새 코드에 대해 브랜치별로 이러한 테스트를 자동으로 실행하는 방법을 설명하겠습니다. 이 글의 범위에서는 WebdriverIO를 사용하여 JavaScript 기반 응용 프로그램에 대한 종단간 테스트용 GitLab CI/CD를 설정하는 과정을 안내합니다. 다만, 일반적인 전략은 다른 언어로도 적용할 수 있어야 합니다. 여러분은 GitLab, GitLab CI/CD, 리뷰 앱, 예를 들어 localhost:8000에서 로컬에서 앱을 실행하는 것에 익숙하다고 가정합니다.

무엇을 테스트할 것인가

널리 사용되는 테스트 피라미드 전략에서 엔드 투 엔드 테스트는 오히려 방어 장치 역할을 합니다: 대부분의 코드는 단위 테스트에 의해 커버되어 문제의 원인을 쉽게 식별할 수 있게 해주는 반면, 엔드 투 엔드 테스트의 수를 제한하여 배포가 의도대로 진행되었음을 확신할 수 있고, 인프라가 작동 중이며 코드 단위가 잘 작동하는지 확인하는 정도로 충분해야 할 것입니다.

Selenium 및 WebdriverIO

Selenium은 웹 브라우저를 제어할 수 있는 소프트웨어로, 특정 URL을 방문하거나 페이지의 요소와 상호작용하는 등의 작업을 수행할 수 있습니다. 다양한 프로그래밍 언어에서 프로그래밍적으로 제어될 수 있습니다. 본 글에서는 WebdriverIO JavaScript 바인딩을 사용할 것이지만, 이와 유사한 개념은 Selenium이 지원하는 다른 프로그래밍 언어에서도 잘 작동할 것으로 예상됩니다.

테스트 작성

WebdriverIO에서 지원하는 여러 테스트 프레임워크를 사용하여 테스트를 작성할 수 있습니다. 본 글에서는 Jasmine를 사용할 것입니다:

describe('계정이 없는 방문자', function(){
    it('404 페이지에서 홈페이지로 이동할 수 있어야 합니다', function(){
        browser.url('/page-that-does-not-exist');
        
        expect(browser.getUrl()).toMatch('page-that-does-not-exist');
        
        browser.element('.content a[href="/"]').click();
        
        expect(browser.getUrl()).not.toMatch('page-that-does-not-exist');
    });
});

describe, it, browser 함수는 WebdriverIO에서 제공됩니다. 각각을 하나씩 살펴보겠습니다.

describe 함수는 관련된 테스트를 그룹화할 수 있도록 합니다. 예를 들어 여러 테스트에 대해 동일한 초기화 명령(예: beforeEach를 사용하여 여러 테스트에 대해 동일한 초기화 명령을 실행하려는 경우에 유용합니다, 예를 들어 로그인 여부를 확인합니다.

it 함수는 개별 테스트를 정의합니다.

browser 객체는 WebdriverIO의 특별한 기능을 제공합니다. 이 객체는 페이지를 제어하는 데 필요한 대부분의 WebdriverIO API 메서드를 제공합니다. 이 경우 browser.url을 사용하여 /page-that-does-not-exist를 방문하여 404 페이지를 확인할 수 있습니다. 우리는 browser.getUrl를 사용하여 현재 페이지가 우리가 지정한 위치에 있음을 확인할 수 있습니다. 페이지와 상호작용하려면 페이지의 요소에 액세스하고 상호작용하기 위해 browser.element에 CSS 선택자를 전달할 수 있습니다. 예를 들어, 홈페이지로 돌아가는 링크를 선택하기 위해서입니다.

위에 표시된 간단한 테스트는 이미 통과하면 많은 확신을 줄 수 있습니다. 배포가 성공했음을 알 수 있고, 요소가 페이지에 표시되고 실제 브라우저가 상호작용할 수 있으며, 라우팅이 예상대로 작동함을 알 수 있습니다. 이 모든 것을 고려하면 단위 테스트가 성공하고 파이프라인이 성공적으로 완료되면 웹사이트를 실제로 확인하지 않아도 의존성 업그레이드가 웹사이트를 망가뜨리지 않았음을 상당히 확신할 수 있습니다.

로컬에서 실행

위의 테스트를 CI/CD에서 실행하는 방법에 대해 곧 다룰 것입니다. 그러나 테스트를 작성할 때 파이프라인이 올바르게 실행되는지 확인하기 위해 기다릴 필요가 없는 것이 도움이 됩니다. 다시 말해서, 로컬에서 실행할 수 있게 해놓는 것이 도움이 됩니다.

앱이 로컬에서 실행 중인지 확인하세요. Webpack을 사용하는 경우 Webpack Dev Server WebdriverIO 플러그인을 사용하여 테스트를 실행하기 전에 자동으로 개발 서버를 시작할 수 있습니다.

WebdriverIO 문서에는 모든 구성 옵션에 대한 개요가 있지만, 시작하는 가장 쉬운 방법은 해당 WebdriverIO의 기본 구성을 시작하도록 하는 것입니다. 현재 가장 관련성 있는 두 가지 옵션은 specs 옵션(테스트 파일의 경로 배열)과 baseUrl 옵션(앱이 실행 중인 위치를 가리킴)입니다. 마지막으로 WebdriverIO에게 어떤 브라우저에서 테스트를 실행하길 원하는지 알려야 합니다. 이것은 capabilities 옵션(예: firefox 또는 chrome과 같은 브라우저 이름의 배열)을 통해 구성할 수 있습니다. 모든 설치된 브라우저를 감지하기 위해 selenium-assistant를 설치하는 것이 좋습니다.

  const seleniumAssistant = require('selenium-assistant');
  const browsers = seleniumAssistant.getLocalBrowsers();
  config.capabilities = browsers.map(browser => ({ browserName: browser.getId() }));

물론, config.capabilities = ['firefox']와 같이 간단한 구성도 작동할 것입니다.

WebdriverIO를 의존성으로 설치했다면(npm install --save-dev webdriverio), package.jsonscripts 속성에 구성 파일 경로와 함께 wdio를 실행하는 줄을 추가할 수 있습니다. 예를 들어:

  "confidence-check": "wdio wdio.conf.js",

이후 npm run confidence-check를 사용하여 테스트를 실행할 수 있으며, 그 결과로 정확히 지정한대로 새 브라우저 창이 앱과 상호작용하는 것을 확인할 수 있게 될 것입니다.

GitLab CI/CD 구성

마침내 흥미로운 부분에 도달했습니다: GitLab CI/CD에서 어떻게 실행할까요? 이를 위해 해야 할 두 가지가 있습니다.

  1. 실제 브라우저를 사용할 수 있는 CI/CD 작업을 설정합니다.
  2. 웹드라이버IO 구성을 업데이트하여 브라우저를 사용하여 리뷰 앱을 방문하도록 합니다.

본 문서의 범위 내에서 추가적인 CI/CD 단계confidence-check를 정의했습니다. 이는 리뷰 앱을 배포하는 단계 _이후_에 실행되는 단계입니다. 이는 node:latest Docker 이미지를 사용합니다. 그러나 WebdriverIO는 실제 브라우저를 실행하여 응용 프로그램과 상호 작용하므로 이를 설치하고 실행해야 합니다. 게다가, WebdriverIO는 다양한 브라우저를 제어하기 위한 공통 인터페이스로 Selenium을 사용하므로 Selenium도 설치하고 실행해야 합니다. 다행스럽게도, Selenium 프로젝트는 Firefox를 위한 Docker 이미지 standalone-firefox와 Chrome을 위한 이미지 standalone-chrome를 제공합니다. (Safari 및 Internet Explorer/Edge는 오픈 소스가 아니므로 Linux에서 사용할 수 없습니다. 따라서 GitLab CI/CD에서는 사용할 수 없습니다).

GitLab CI/CD를 사용하면 services 속성을 사용하여 이러한 이미지를 confidence-check 작업에 링크하는 것이 매우 쉽습니다. 이는 Selenium 서버를 이미지 이름을 기반으로 한 호스트 이름으로 사용할 수 있게 합니다.

밑줄이 포함된 호스트 이름은 RFC(요청-응답 프로토콜) 유효하지 않으며, 외부 애플리케이션에서 문제를 일으킬 수 있습니다. 자세한 내용은 서비스 액세스를 참조하세요.

예를 들어, Firefox를 위한 작업 구성은 다음과 같이 보입니다:

e2e:firefox:
  stage: confidence-check
  services:
    - selenium/standalone-firefox
  script:
    - npm run confidence-check --host=selenium__standalone-firefox

그리고 Chrome을 위한 작업은 다음과 같습니다:

e2e:chrome:
  stage: confidence-check
  services:
    - selenium/standalone-chrome
  script:
    - npm run confidence-check --host=selenium__standalone-chrome

이제 우리는 최종적으로 실행할 작업을 갖게 되었으니, WebdriverIO에게 Selenium 서버에 연결하는 방법을 알려주어야 합니다. 위에서 이미 커맨드 라인에서 npm run confidence-check에 대한 host 옵션의 값을 전달하여 약간 부정직하게 처리했습니다. 그러나 여전히 WebdriverIO에게 사용 가능한 브라우저를 알려주어야 합니다.

GitLab CI/CD에서는 현재 CI 작업에 대한 정보를 포함한 여러 변수를 제공합니다. 이 정보를 사용하여 실행 중인 작업에 따라 우리의 WebdriverIO 구성을 동적으로 설정할 수 있습니다. 더 구체적으로는 실행 중인 작업의 이름에 따라 테스트를 실행할 브라우저를 WebdriverIO에게 알릴 수 있습니다. 이를 위해 이전에 wdio.conf.js로 명명한 WebdriverIO 구성 파일에 다음과 같은 내용을 추가할 수 있습니다:

if(process.env.CI_JOB_NAME) {
    dynamicConfig.capabilities = [
        { browserName: process.env.CI_JOB_NAME === 'e2e:chrome' ? 'chrome' : 'firefox' },
    ];
}

마찬가지로, WebdriverIO에게 리뷰 앱이 실행 중인 위치에 대한 정보를 전달할 수 있습니다. 이 예제의 경우에는:

if(process.env.CI_COMMIT_REF_SLUG) {
    dynamicConfig.baseUrl = `https://${process.env.CI_COMMIT_REF_SLUG}.flockademic.com`;
}

또한, if (!process.env.CI)를 사용하여 로컬별 구성이 CI에서 실행되지 않을 때만 사용되도록 할 수 있습니다. 이것이 GitLab CI/CD에서 end-to-end 테스트를 실행하는 데 필요한 모든 구성입니다!

요약하면, .gitlab-ci.yml 구성 파일은 다음과 같이 보일 것입니다:

default:
  image: node:8.10

stages:
  - deploy
  - confidence-check

deploy_terraform:
  stage: deploy
  script:
    # 리뷰 앱 배포 스크립트 - 작동하는 예제는 https://gitlab.com/Flockademic/Flockademic/blob/5a45f1c2412e93810fab50e2dab8949e2d0633c7/.gitlab-ci.yml#L315 참조
    - echo
  environment: production

e2e:firefox:
  stage: confidence-check
  services:
    - selenium/standalone-firefox
  script:
    - npm run confidence-check --host=selenium__standalone-firefox

e2e:chrome:
  stage: confidence-check
  services:
    - selenium/standalone-chrome
  script:
    - npm run confidence-check --host=selenium__standalone-chrome

다음 단계

자신을 위해 이를 설정하고 프로덕션 프로젝트의 작업 구성을 확인하고 싶다면 다음을 참조하세요:

WebdriverIO가 수행할 수 있는 많은 작업이 있습니다. 예를 들어, WebdriverIO에게 screenshotPath를 구성하여 테스트가 실패할 때 스크린샷을 찍도록 지시할 수 있습니다. 그런 다음 GitLab CI/CD에게 이러한 artifacts를 저장하도록 지시하면 GitLab 내에서 무엇이 잘못되었는지 확인할 수 있습니다.