今日もヤバさをI/O中。

(物理的)大型エンジニアのブログです。基本的に何かが足りません。

Javascript 製のE2Eテスト(puppeteer)に纏わるちょっとした工夫

この記事は、ソフトウェアテストの小ネタ Qiita Advent Calendar 2019 の24日目の記事です。 puppeteer を触っているときにちょっとした工夫を2つほど思いついたので、コード例と共に記事にしようと思います。

テスト部分を try ~ catch ~ finally で括ると、テストに落ちた時や終了した時の処理をカスタマイズできる

puppeteer は node 製のライブラリなので、javascript で使われている try ~ catch ~ finally句が使用できます。

テスト部分を try ~ catch ~ finally 句で括ると、catch 句ではテストに落ちた時の処理を、finally 句では終了した時の処理をカスタマイズできます。

コード例

import Puppeteer from 'puppeteer';
import test from 'ava';

test.serial('ログイン_成功', async (t) => {
    const browser = await Puppeteer.launch({
        "ignoreHTTPSErrors" : true,
    });
    const page = await browser.newPage();

      try {
        await page.goto("https://hoge.com");
        await page.type(".mail-address", "hoge@huga.jp");
        await page.waitFor(500);
        await page.type('.password', "test");
        await page.waitFor(500);
        await page.click('.login');
        await page.waitFor('.logout');
        const result = await page.evaluate(() => {
            return document.querySelector('.logout').innerHTML;
        });
        t.true(result.includes('ログアウト'));
    } catch (e) {
        // テストに失敗したら、スクリーンショットを撮って保存する
        const todayDateTime = new Date();
        const formatTodayDateTime = String(todayDateTime.getFullYear())
            + String(todayDateTime.getMonth() + 1)
            + String(todayDateTime.getDate())
            + String(todayDateTime.getHours())
            + String(todayDateTime.getMinutes());
        + String(todayDateTime.getSeconds());

        await page.screenshot({
            path: 'screenshot/' + formatTodayDateTime + '_error_login.png',
            fullPage: true
        });

        t.fail('ログインテストに失敗しました。スクリーンショットをご確認ください。\nエラーメッセージ内容:' + e.message);
    } finally {
        // テストに成功しようが失敗しようがブラウザは閉じる
        await browser.close();
    } 
});

私は上記のように、「失敗した時はスクリーンショットを取って保存する」「テスト終了した時は必ずブラウザを閉じる」処理を追加しています。

環境によって違う値は、定数ファイルに保存しておくと管理が楽

ログインIDやパスワードといった環境ごとに値が違う場合は、定数ファイルにまとめて保存すると管理が楽になります。 仮にテストデータのリセットが行われたとしても、1カ所直すだけですむためです。

私は configと呼ばれる、環境変数(env)によって定数の値を変えるライブラリを使っています。

コード例

test.js

import Puppeteer from 'puppeteer';
import test from 'ava';

test.serial('ログイン_成功', async (t) => {
    const browser = await Puppeteer.launch({
        "ignoreHTTPSErrors" : true,
    });
    const page = await browser.newPage();

    await page.goto("https://hoge.com");
    await page.type(".mail-address", config.get("front.user.mail")
);
    await page.waitFor(500);
    await page.type('.password', config.get("front.user.password"));
    await page.waitFor(500);
    await page.click('.login');
    await page.waitFor('.logout');
    const result = await page.evaluate(() => {
            return document.querySelector('.logout').innerHTML;
    });

    t.true(result.includes('ログアウト'));
    await browser.close();
});

testing.json

{
  "front": {
    "user": {
      "mail": "hoge@huga.jp",
      "password": "Test2"
    },
}

staging.json

{
  "front": {
    "user": {
      "mail": "piyo@foo.jp",
      "password": "Test2"
    },
}

package.json

{
    "name": "e2e-test",
    "version": "1.0.0",
    "description": "",
    "scripts": {
        "test_testing": "NODE_ENV=testing ava --verbose",
        "test_staging": "NODE_ENV=staging ava --verbose"
    },
    "devDependencies": {
        "ava": "*",
        "config": "*",
        "puppeteer": "*"
    }
}

テスト実行時に NODE_ENV という環境変数を使って、使用する定数ファイルを指定しています。