TestCafe で E2E テストを始めよう #2 - ベーシック認証とユーザーロール(アカウント認証)

前回は TestCafe の大まかな概要と Getting Started をご紹介しました。

今回は ベーシック認証ユーザーロール という二つの認証についてご紹介します。

シリーズ一覧

ベーシック認証が効いている環境にアクセスする

通常、ベーシック認証(HTTP Authentication)が効いているサイトへ( curl といった)プログラムからアクセスする際は、下記の様に認証情報を URL に直接埋め込むことが多いかと思います。

# https://www.example.com
# ユーザー名: username
# パスワード: password
https://userame:password@www.example.com # @といった記号は URL エンコードすること

しかし E2E テストでは実際に web ブラウザからアクセスするためこの手法は使えません。そこで TestCafe ではベーシック認証情報を下記のように宣言的に記述します。

fixture('My Fixture')
  .page('https://www.example.com')
  .httpAuth({
    username: 'username',
    password: 'password',
  });
test('Test 1', async t => {...});

page で対象 URL を指定してからメソッドチェーンでベーシック認証情報を指定します。まるで自然言語のようで可読性が高いですね。

User Role 機能を使って認証処理を共通化する

アカウント認証が必要なプロダクトのテストをする時に役立ちます。通常であれば各テストケースの中にこういった認証フローまで含めてしまいがちですが、全てのシナリオに全く同じコードを書くのは避けたいところです。TestCafe の User Role 機能は認証処理に特化したもので、そこを綺麗に分離して管理できるというものです。

使い方

Role コンストラクタを使ってロール(アカウント)を定義します。

import { Role, TestController } from 'testcafe';
const regularUser = Role(
  'http://example.com/login',
  async (t: TestController) => {
    await t
      .typeText('#login', 'TestUser')
      .typeText('#password', 'user-password')
      .click('#sign-in');
  }
);
const adminUser = Role(
  'http://example.com/login',
  async (t: TestController) => {
    await t
      .typeText('#login', 'AdminUser')
      .typeText('#password', 'admin-password')
      .click('#sign-in');
  }
);

第一引数に認証を行う URL を渡し、第二引数にユーザを認証するロジックを含む内容を関数として定義します。構文自体はテストケースと殆ど同じですね。

ではこれらのロールをテストケースから読み込んでみます。

import 'testcafe';
import { Selector } from 'testcafe';
const entry = Selector('#entry');
const removeButton = Selector('#remove-entry');
fixture('My Fixture').page('https://www.example.com');
test('test that involves two users', async (t: TestController) => {
  await t
    .useRole(regularUser)
    .expect(entry.exists)
    .ok()
    .expect(removeButton.visible)
    .notOk();
});

TestController オブジェクトの useRole というメソッドに Role オブジェクトを渡します。すると regularUser 内に定義したアクションが実際に行われ、そのロール(アカウント情報)が元いたページ( page メソッドに渡した URL )に読み込まれた状態で後続のステップが続行されます。

上記の例ではテストケースの最初のステップでロールを読み込んでいますが、ステップの途中で読み込むことも可能です。

test('test that involves two users', async (t: TestController) => {
  await t
    .useRole(regularUser)
    .expect(entry.exists)
    .ok()
    .expect(removeButton.visible)
    .notOk()
    .useRole(adminUser)
    .expect(removeButton.visible)
    .ok()
    .click(removeButton)
    .expect(entry.exists)
    .notOk();
});

最初に regularUser ロールを読み込んだ後に adminUser を読み込んでいます。つまりこれ以降のステップは adminUser として行われるということです。

このように複数のロールを定義しておくことで管理が一元化され、またテストケースからの利用もシームレスに行えるようになります。

preserveUrl

Role メソッド内で実施する認証処理によっては、最後にセッション情報が URL ハッシュとして追記されたページへリダイレクトされることがあります。通常 Role メソッドはそのページに進んだところですぐに元いたページに戻ってしまいますが、 preserveUrl というオプションを true にしておくと、認証後にリダイレクトしたページの URL 情報を記憶でき、次回以降ロールを切り替える際はこの記憶された URL を開くようになります。

const regularUser = Role(
  'http://example.com/login',
  async (t: TestController) => {
    await t
      .typeText('#login', 'TestUser')
      .typeText('#password', 'user-password')
      .click('#sign-in');
  },
  {
    preserveUrl: true
  }
);

例えば Firebase AuthenticationAuth0 のような IDaaS を採用しているプロダクトの E2E テストをする際に役立つと思われます。

今回はここまで。次回は TestCafe をよりプログラマブルに実行する方法についてご紹介します。