GitLab CI/CDとWebdriverIOによるエンドツーエンドのテスト

レビューアプリは素晴らしいものです。マージリクエスト(あるいはブランチ)ごとに、新しいコードをコピーして本番環境のような新しい環境にデプロイできるため、変更の影響を評価する労力が軽減されます。このように、Dependencies.ioのような依存関係マネージャを使用する場合、更新された依存関係でマージリクエストを送信することができ、アプリケーションがまだ適切にビルドおよびデプロイできることがすぐにわかります。結局のところ、実行されているのを_見る_ことができるのです!

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('A visitor without account', function(){
    it('should be able to navigate to the homepage from the 404 page', 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 が提供しています。1つずつ分解してみましょう。

関数describe を使うと、関連するテストをグループ化することができます。例えば、ログインしていることを確認するなど、複数のテストで同じ初期化コマンド (beforeEachを使用) を実行したい場合に便利です。

関数it は、個々のテストを定義します。

browser オブジェクト は WebdriverIO の特製ソースです。ブラウザを操作する鍵となるWebdriverIO APIメソッドのほとんどを提供します。この場合、browser.url を使って/page-that-does-not-exist にアクセスし、404ページをヒットさせます。そして、browser.getUrl を使って、現在のページが指定した場所にあることを確認します。ページと対話するために、browser.element に CSS セレクタを渡して、ページ上の要素にアクセスし、それらと対話することができます。

デプロイが成功し、要素がページ上に表示され、実際のブラウザがページとやりとりでき、ルーティングが期待どおりに動作することがわかります。そしてそのすべてが、ありがたい空白を含むわずか10行で実現されています!単体テストの成功やパイプラインの正常終了を加えると、依存関係のアップグレードが何も壊していないことを、ウェブサイトを見ることなく確信することができます。

ローカルでの実行

上記のテストをCI/CDで実行する方法については、後ほど説明します。しかし、テストを書くときには、パイプラインが期待通りに動くかどうかを判断するために、パイプラインが成功するのを待つ必要がないほうが便利です。つまり、ローカルで実行してみましょう。

アプリがローカルで実行されていることを確認します。Webpack を使用している場合は、テストを実行する前に開発サーバーを自動的に起動するWebpack Dev Server WebdriverIO プラグインを使用できます。

WebdriverIO のドキュメントにはすべての設定オプションの概要が記載されていますが、一番簡単なのはWebdriverIO のデフォルト設定から始めることです。specs オプションはテストへのパスを配列で指定し、baseUrl オプションはアプリの実行場所を指定します。最後に、WebdriverIO にテストを実行するブラウザを指定します。これはcapabilities オプションで設定します。firefoxchromeなどのブラウザ名を配列で指定します。インストールされているすべてのブラウザを検出するために、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でこれを実行するにはどうすればいいのでしょうか?そのために必要なことが2つあります:

  1. 実際にブラウザが使えるCI/CD ジョブをセットアップすることです。
  2. WebdriverIOの設定を更新し、レビューアプリにアクセスするためにそれらのブラウザを使用するようにします。

この記事の範囲では、レビューアプリをデプロイするステージの_後に_実行される追加のCI/CDステージ confidence-check 。これはnode:latest Dockerイメージを使用します。しかし、WebdriverIOはアプリケーションと対話するために実際のブラウザを起動するので、それらをインストールして実行する必要があります。さらに、WebdriverIOは異なるブラウザを制御するための共通インターフェースとしてSeleniumを使用するので、Seleniumもインストールして実行する必要があります。幸運なことに、SeleniumプロジェクトはFirefoxstandalone-firefoxとChromestandalone-chrome用のDockerイメージを提供しています。(SafariとInternet Explorer/Edgeはオープンソースではなく、Linuxでも利用できないため、残念ながらGitLab CI/CDでは使用できません)。

GitLab CI/CD では、service プロパティを使って、これらのイメージをconfidence-check ジョブに簡単にリンクすることができます。ジョブの設定はこのようになります:

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 サーバーへの接続方法を指示する必要があります。host オプションの値をコマンドラインのnpm run confidence-check への引数として渡すことで、少しごまかしました。しかし、WebdriverIOにどのブラウザを使用できるかを伝える必要があります。

GitLab CI/CDでは、現在の CI ジョブに関する情報を多くの変数で利用できるようにしています。この情報を使って、実行中のジョブに応じて WebdriverIO の設定を動的に設定することができます。具体的には、現在実行中のジョブの名前に応じて、どのブラウザでテストを実行するかを WebdriverIO に伝えることができます。この設定は WebdriverIO の設定ファイル (wdio.conf.js ) で行います:

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`;
}

また、ローカル固有の設定を CI で実行して_いない_ときにのみ使用するようにするにはif (!process.env.CI) を使用します。これで、GitLab CI/CD でエンドツーエンドのテストを実行するために必要なものは基本的にすべて揃いました!

まとめると、.gitlab-ci.yml の設定ファイルはこのようになります:

default:
  image: node:8.10

stages:
  - deploy
  - confidence-check

deploy_terraform:
  stage: deploy
  script:
    # Your Review App deployment scripts - for a working example please check 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にこれらのアーティファクトを保存するように指示すれば、GitLab内で何が問題だったのかを確認することができます。