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

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

依存関係.io

しかしながら、デプロイされたばかりのコードを見て、期待通りの外観と動作をしているかどうかをチェックすることは、繰り返しの手作業です。 これは、自動化されたエンドツーエンドテストの出番です。これは、フロントエンドからデータベースに至るまで、アプリケーションのすべてのレイヤーが適切に機能することを要求するいくつかの単純なシナリオをコンピュータに実行させることです。

この記事では、このようなエンドツーエンドのテストを書く方法と、新しいコードに対してこれらのテストをブランチごとに自動的に実行するために GitLab CI/CD をセットアップする方法について説明します。 この記事の範囲では、WebdriverIO を使って JavaScript ベースのアプリケーションをエンドツーエンドでテストするために GitLab CI/CD をセットアップするプロセスを説明しますが、一般的な戦略は他の言語にも引き継ぐことができるはずです。 GitLab、GitLab CI/CDレビューアプリ、ローカルでのアプリの実行(例:localhost:8000)に慣れていることを前提としています。

テスト内容

広く使われているテストピラミッド戦略では、エンドツーエンドのテストは、どちらかというと安全装置のような役割を果たします。 万が一問題が発生した場合に、その原因を簡単に特定できるような単体テストによって、コードの大部分をカバーすべきです。 そうではなく、デプロイが意図したとおりに行われ、インフラが稼働し、コードのユニットがうまく連携して動作しているという確信を得られるだけの数に、エンドツーエンドのテストの数を制限したいと考えるでしょう。

SeleniumとWebdriverIO

Seleniumは、Webブラウザを制御するソフトウェアの一部です。 例えば、特定の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 が提供しています。

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

関数it は内部テストを定義します。

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

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

ローカルで実行

上記のテストをCI/CDで実行する方法については、後ほど説明します。 しかし、テストを書く際には、パイプラインが期待通りの動作をするかどうかをチェックするために、パイプラインが成功するのを待つ必要がなければ助かります。 つまり、ローカルで実行できるようにしましょう。

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

WebdriverIO のドキュメントには、すべての設定オプションの概要が記載されていますが、最も簡単な方法は、WebdriverIO のデフォルトの設定から始めることです。 今、最も関連性のある 2 つのオプションは、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-checknode:latest Dockerイメージを使用します。 しかし、WebdriverIOはアプリケーションと対話するために実際のブラウザを起動するので、それらをインストールして実行する必要があります。 さらに、WebdriverIOは異なるブラウザを制御するための共通インターフェイスとしてSeleniumを使用するので、Seleniumもインストールして実行する必要があります。 幸運なことに、Seleniumプロジェクトは、FirefoxとChromeのためのDockerイメージstandalone-firefoxとstandalone-chromeをそれぞれ提供しています(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

クロームも同様です:

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 に使用可能なブラウザを教える必要があります。

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

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

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

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
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 内で何が問題だったのかを確認することができます。