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

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

Review Apps는 훌륭합니다: 매번 병합 요청(또는 브랜치)마다 새 코드를 복사하여 생산 환경과 유사한 새로운 환경에 배포할 수 있으므로, 변경 사항의 영향을 평가하는 노력을 줄일 수 있습니다. 그래서 Dependencies.io와 같은 의존성 관리자를 사용하면 업데이트된 종속성이 있는 병합 요청을 제출할 수 있고, 즉시 애플리케이션이 여전히 제대로 빌드되고 배포될 수 있는지 확인할 수 있습니다. 결국, 실행 중인 것을 확인할 수 있습니다!

dependencies.io

그러나 새로 배포된 코드를 살펴보고 여전히 예상대로 보이고 작동하는지 확인하는 것은 반복적인 수동 작업으로, 이는 자동화하기에 가장 적합한 후보입니다. 여기서 자동화된 엔드 투 엔드 테스트가 필요합니다: 컴퓨터가 애플리케이션의 모든 레이어, 프론트엔드부터 데이터베이스까지의 올바른 기능을 요구하는 몇 가지 간단한 시나리오를 실행하도록 하는 것입니다.

이 글에서는 이러한 엔드 투 엔드 테스트를 작성하는 방법과 GitLab CI/CD를 설정하여 브랜치당 새 코드에 대해 이러한 테스트를 자동으로 실행하는 방법을 살펴보겠습니다. 본 문서의 범위 내에서는 WebdriverIO를 사용하여 JavaScript 기반 애플리케이션의 엔드 투 엔드 테스트용 GitLab CI/CD 설정하는 과정을 안내하지만, 일반적인 전략은 다른 언어로도 적용될 것으로 예상됩니다. GitLab, GitLab CI/CD, Review Apps, 앱을 로컬에서 실행하는 등에 익숙하다고 가정합니다. 예를 들어, 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 메서드 대부분을 제공합니다. 이 경우, 우리는 404 페이지에 접근하기 위해 browser.url을 사용할 수 있고, 현재 페이지가 우리가 지정한 위치에 있는지 확인하기 위해 browser.getUrl을 사용할 수 있습니다. 페이지와 상호 작용하기 위해 우리는 browser.element에 CSS 선택자를 전달하여 페이지의 요소에 액세스하고 상호 작용할 수 있습니다. 예를 들어, 홈페이지로의 링크를 선택합니다.

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

로컬에서 실행

우리는 이전 테스트를 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. WebdriverIO 구성을 업데이트하여 해당 브라우저가 리뷰 앱을 방문하도록합니다.

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

GitLab CI/CD는 이러한 이미지를 service 속성을 사용하여 confidence-check 작업에 연결하는 것을 손쉽게 만들어 주며, 이를 통해 Selenium 서버를 이미지 이름을 기반으로 한 호스트 이름으로 사용할 수 있게 됩니다. 그런 다음 우리 작업 구성은 다음과 같이 보입니다:

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

이제 우리는 엔드 투 엔드(End-to-End) 테스트를 실행할 작업이 있으므로, WebdriverIO에 Selenium 서버에 연결하는 방법을 알려주어야 합니다. 우리는 이미 조금 위에서 host 옵션의 값을 명령줄에서 npm run confidence-check에 전달하여 속임수쳤지만, 여전히 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에게 리뷰 앱이 어디에서 실행되는지 알려줄 수 있습니다. 이 예제의 경우, <branch name>.flockademic.com입니다.

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에서 엔드 투 엔드 테스트를 실행하는 데 필요한 모든 구성입니다!

요약하면, .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가 수행할 수 있는 작업은 훨씬 많습니다. 예를 들어, screenshotPath를 구성하여 테스트가 실패할 때 WebdriverIO에게 스크린샷을 촬영하도록 지시할 수 있습니다. 그런 다음 GitLab CI/CD에게 그 아티팩트를 저장하도록 지시하면 GitLab 내에서 무엇이 잘못되었는지 확인할 수 있습니다.