前回のCypressでの画面表示テストに続き、今回はログイン・ログアウトのテストです。カスタムコマンドの作り方やHTTPリクエストの実行など、Cypressのテストに欠かせないアプローチが出てきますので参考になれば嬉しいです。

Cypressのセットアップや画面表示テストなど、以前の記事は以下からご覧いただけます。

Cypressセットアップ・実行方法
Cypress画面表示のテスト

フォーム入力・ボタンクリック

Breezeログイン画面

ログインのテストは前回に続きLaravel Breezeのログイン画面で行います。フォーム画面のテストには、テキスト入力・ボタンクリックの操作が必要ですので、それぞれCypressのtype()click()コマンドを使用します。

どちらのコマンドもcy.type()...のようにcyに直接繋げるのではなく、操作対象の要素を取得した後に繋いで使用します。例えばメールアドレス入力欄にメールアドレスを入力、またsubmitボタンをクリックといった操作は、以下のように記述します。

  cy.get('input[name="email"]').type('test@example.com')
  cy.get('button[type="submit"]').click()

cypress.env.jsonに環境変数をセット

ログイン情報など、テストの実行環境によって異なる値はcypress.env.jsonを作成してそちらにまとめておくと便利です。

今回使用するユーザーのメールアドレス・パスワードは、以下のように定義しました。

{
  "email": "test@example.com",
  "password": "password"
}

テストコードの中でこの値を呼び出すには、Cypress.env()を使います。

  cy.get('input[name="email"]').type(Cypress.env('email'));
  cy.get('input[name="password"]').type(Cypress.env('password'));

ログイン成功・失敗のテストコード

type()click()を使用した、ユーザーがログインに成功するケースとエラーが発生するケースのテストコードは以下のようになります。アサーションは前回と同じくshould()を使って検証します。

it('正しいメールアドレス・パスワードでログイン成功', () => {

  cy.visit('/login')

  cy.get('input[name="email"]').type(Cypress.env('email'))
  cy.get('input[name="password"]').type(Cypress.env('password'))
  cy.get('button[type="submit"]').click()

  // ダッシュボードに遷移
  cy.url().should('eq', Cypress.config().baseUrl + '/dashboard')
})

it('パスワード間違いでログイン失敗', () => {

  cy.visit('/login')

  cy.get('input[name="email"]').type(Cypress.env('email'))
  cy.get('input[name="password"]').type('wrong-password') //間違ったパスワード
  cy.get('button[type="submit"]').click()

  //ログイン画面のまま
  cy.url().should('include', '/login')

  // 画面のエラー表示を確認
  cy.get('input[name="email"]')
    .next('ul')
    .should('contain', 'ログイン情報が存在しません。')
})

ログイン失敗のテストでは、「ログイン情報が存在しません。」のエラーメッセージが存在することを確認しています。このエラーメッセージはLaravel Breezeでは以下のHTMLで出力されます。

<input class="border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm block mt-1 w-full" id="email" type="email" name="email" value="test@example.com" required="required" autofocus="autofocus" autocomplete="username">
<ul class="text-sm text-red-600 space-y-1 mt-2">
    <li>ログイン情報が存在しません。</li>
</ul>

メッセージ出力箇所のulliには要素特定に使えそうなidclassが割り当てられていないため、input[name="email"]要素の次のul要素内をチェックします。

request・thenコマンド

さて次はログアウトですが、テストを作成する前にrequest()then()コマンドをご紹介します。

request()は、HTTPリクエストを送信するために使用するコマンドです。以下のように記述すると、引数のURLに対してGETリクエストを送信します。

cy.request('/login')

POST送信の場合はオブジェクト形式でオプションを渡す必要があります。以下のように記述すると、POSTメソッドでURL/loginに対してリクエストを送信、かつリクエストbodyにはパスワード情報を含める。という意味になります。

    cy.request({
      method: 'POST',
      url: '/login',
      body: {
        password:'password',
      }
    })

では次にthen()コマンドです。then()は単独で使うのではなく他のコマンドにチェーンして、前のコマンドからの結果を受け取る形で記述します。具体的な使用方法として、先ほどのPOST送信にthen()を繋げました。

    cy.request({
      method: 'POST',
      url: '/login',
      body: {
        password:'password',
      }
    }).then((response) => {
      expect(response.status).to.eq(200)
    })

このコードでは、HTTPリクエストが完了するのを待ってから、そのレスポンスをresponseという引数で受け取ります。その後、コールバック関数内でレスポンスのステータスコードを検証しています。

then()は他にもHTML要素や任意の値やオブジェクトなども扱えます。詳しくはドキュメントをご参照ください。

ログアウトのテスト

cypressログアウト

ではログアウトのテストを作成してゆきましょう。画像のように、ドロップダウンを開いてログアウトリンクをクリックするとログアウトすることをテストします。

こういったログアウトのようなテストでは、ユーザーがすでにログインしている状態を準備する必要があります。これを、直接フォームに入力してボタンをクリックする方法で行なっても良いのですが、それでは画面操作の分余計な時間がかかってしまいます。

そこで、先ほどご紹介したrequest()を使用してHTTPリクエストでログインすることにします。これにより、UIを介さずに効率的にログイン処理を完了できます。

ログイン処理のカスタムコマンドを作成

テスト内で繰り返し使用する処理はカスタムコマンドとして定義します。これにより、どのテストからでも簡単に呼び出して使用できるようになります。

cypress/support/commands.jsファイルにカスタムコマンドを追加し、その際Cypressが定めた以下の構文を使用します。

Cypress.Commands.add('カスタムコマンド名', (引数) => { })

以下が作成したカスタムコマンドです。ユーザー画面のログインなのでコマンド名はloginAsUserとしました。

Cypress.Commands.add('loginAsUser', (email, password) => {
  // Step 1: ログインページにアクセスしてCSRFトークンを取得
  cy.visit('/login')
  cy.get('input[name="_token"]').invoke('val').then((csrfToken) => {
    // Step 2: 取得したCSRFトークンを使用してログインリクエストを送信
    cy.request({
      method: 'POST',
      url: '/login',
      form: true, // フォームデータとして送信
      body: {
        email: email,
        password: password,
        _token: csrfToken  // CSRFトークンをリクエストに含める
      }
    }).then((response) => {
      // Step 3: ログインが成功したかステータスコードで確認
      expect(response.status).to.eq(200);
    });
  });
})

ログインするだけなのに少し長いコマンドになっていますが、内容は大きく以下の3ステップに分かれています。

  1. ログインに必要なCSRFトークンを取得する処理
  2. cy.requestを使用してログインリクエストを実行
  3. ログイン成功のステータス確認

まず、ステップ1ではログイン画面へアクセスし、トークンのデータを含むHTML要素を取得。その要素に対しinvoke('val')を使用し、トークンの値だけを抜き出しています。invoke('val')とはjQueryのval()メソッドを呼び出すということを意味します。なぜjQueryが使えるのか?というと、これはCypressがjQueryを内包しているためこのような値の取得方法が可能になっています。

さて、こうして取得したトークンは次のHTTPリクエストで使用します。そのためthen()で繋ぎ、トークンをcsrfTokenとして受け取ります。

そしてステップ2は、request()でのHTTPリクエスト実行です。リクエストメソッド、URL、リクエストデータなどを引数に渡しています。_tokenとしてcsrfTokenも含まれていますね。

最後のステップ3はレスポンスの検証です。then()でレスポンスを受け取り、expect()を使ってHTTPステータスを確認しています。expect()はCypressが内部で使用しているChai.jsというアサーションライブラリのメソッドであるため、cy.に繋げず単独で使用できます。

コマンドが完成しました!これをテストの中では、以下のように呼び出して使います。

  cy.loginAsUser()

ログアウトのテストコード

カスタムコマンドを使用したログアウトのテストコードは以下のようになります。

it('ログアウトリンククリックでログアウト', () => {

  //カスタムコマンドでログイン
  cy.loginAsUser(Cypress.env('email'), Cypress.env('password'))

  //テスト対象画面へ遷移
  cy.visit('/dashboard')

  //ドロップダウンメニューを開くためのボタンクリック・ログアウトボタンをクリック
  cy.get('.relative').click()
  cy.contains('a', 'ログアウト').click()

  //ホーム画面へ遷移したことを確認
  cy.url().should('eq', Cypress.config().baseUrl + '/')
});

カスタムコマンドによるログイン処理、ログアウト実行、アサーション、という構成です。ログアウトテストに直接関係のないログイン処理を別ファイルに切り出しているので、テストコードがすっきりしていいですね。

では、ここまでのログイン成功・ログイン失敗・ログアウトのテストを全て実行してみます。

無事成功しました!次回は、Cypressでメール確認のテストを作成します。

参照

CypressでE2Eテストを自動化(1)セットアップ
CypressでE2Eテストを自動化(2)画面表示のテスト

メルマガ購読の申し込みはこちらから。

By hmatsu