GameWith Developer Blog

GameWith のエンジニア、デザイナーが技術について日々発信していきます。

Autifyを用いたE2EテストをJSで工夫した話 #GameWith #TechWith #Autify

この記事は GameWith アドベントカレンダー2022 及び Autify アドベントカレンダー2022の5日目の記事です。

はじめまして!サービス開発部のdanaです。
GameWithではサーバーサイド開発を担当しています。

今回はE2Eテストで採用しているAutifyの話をします。
E2EテストとはEnd to End Testのことで、アプリケーションが正しく画面表示され、適切に動くかどうかのテストを行うテストを表します。
結合後の画面テストが近い表現になるかと思います。

GameWithではAutifyを利用しています

autify.com

Autifyは画面操作を録画してテストを作成することができ、AIを用いて要素を判別してくれる便利なサービスです。
ノーコードでテストを作成することができるので、エンジニア以外でもテストを作成することができます。
また、多少のデザイン修正であれば適切に判定してくれるので、Seleniumなどを用いたコードベースのテストと比較してメンテナンスコストを削減することができます。

テストが増えるとうまくいかないパターンもある

Autifyは複数シナリオやCSVデータを用いた並列テストが行えるのも強みなのですが、並列であるが故うまくいかないパターンが存在します。
登録内容が正しく表示されている事を確認するテストが複数存在する場合です。
GameWithの場合ではゲーム情報や記事情報のテストが該当します。
他のサービスであればチャットや掲示板などが該当するでしょうか。

並列でデータ追加をした場合に影響を受けるかどうか

影響を受けないテストシナリオ

  • 親要素全体を対象にテキストが存在するかだけを確認する
  • 対象と兄弟位置にある要素を利用しない

影響を受けるテストシナリオ

  • 確認要素をCSSセレクターで指定している
  • 対象と兄弟位置にある要素を利用する
    • 例1) 削除を押下してデータの削除後に存在しないことを確認したい
      • 異なるデータを削除してテスト失敗になることが想定されます
    • 例2) リンク先のデータが同じかどうかを確認したい
      • リンク先が正しいか確認するテストシナリオ
      • 別の行のリンク先に遷移してテスト失敗になることが想定されます

並列実行でテストがうまくいかないケース

下記の表で特定行にある内容を確認したりクリックするパターンを例とします。

前提条件

  • ランダム文字列ステップで生成された文字列がタイトルと本文に含まれる
  • IDは登録時には発行され、テスト実行段階では登録したデータのIDは不明
  • テーブルはID降順で表示されている
  • 同じ行に存在する要素を利用したい
    • テキストの内容を検証したい
    • リンクやボタンをクリックしたい

並列実行時に全件成功するケース

  1. テストAがデータ登録
  2. テストAが最上位のデータを確認する
  3. テストBがデータ登録
  4. テストBが最上位のデータを確認する

並列実行時に片方失敗するケース

  1. テストAがデータ登録
  2. テストBがデータ登録
  3. テストAが最上位のデータを確認する→テストBの内容のため失敗
  4. テストBが最上位のデータを確認する

並列実行時に片方失敗するケース2

  1. テストAがデータ登録
  2. テストBがデータ登録
  3. テストBが最上位のデータを確認する
  4. テストAが最上位のデータを確認する→テストBの内容のため失敗

失敗する要因

並列だと最上位のデータが自身が作成したデータである保証がなくなります。
そのため成功したり失敗したりするようになってしまいます。

解決方法

AutifyはCSSセレクタで要素を指定する事は可能ですが、n番目の要素(:nth-child())を動的に設定することはできません。
そのため行を指定する場合はJSで要素を特定する必要があります。
正しい行を取得するためには表示されているユニークな値で行を検索する方法がありますので、今回はJSステップを追加して説明します。

また、要素を選択してJSステップの追加でも可能ですが要素の選択を少し工夫する必要があります。
(tableタグであればtableを選択する必要があります。td要素やtr要素を選択すると対象範囲がその時点で絞られてしまうため。)

行で処理するJSステップの追加

JSステップの引数の値としてtargetTextを用意し、ランダム文字列ステップで生成した文字列を渡します。
下記の例では2列目に存在する文字列で検索しています。(検索に正規表現を用いていますがindexOfなどでもOKです。)
IDや完全な文字列が分かっているのであれば === で検索しても良いですね。
注意 Autifyの引数は文字型になるので数値で厳密な比較を行う場合は適切にキャストする必要があります

const reg = new RegExp(targetText);
const elem = Array.from(document.getElementsByClassName("trのクラス名")).filter(v => reg.test(v.children[1].innerText))[0];
if(!elem) {
  throw new Error(`要素が存在しません: ${targetText}`);
}

要素指定のJSステップの場合(Autifyで選択した要素をelementとしています)

const reg = new RegExp(targetText);
const elem = Array.from(element).filter(v => reg.test(v.children[1].innerText))[0];
if(!elem) {
  throw new Error(`要素が存在しません: ${targetText}`);
}

コードの解説(クリックで詳細表示)

Array.from(document.getElementsByClassName("trのクラス名"))

document.getElementsByClassNameHTMLCollection型を返却します。
HTMLCollection型は添え字でのアクセスは可能ですが、配列として扱われません。
一度配列として変換することでfilter関数を利用可能にして検索をしています。

MDNではArray.prototype.filter.call(element, (v) => v)で呼び出していますが、個人的にはArray.from(element).filter(v =>v)の方が若干短くて好きです。
配列として変換しておけば何度も使うパターンでも使いやすいですね。

filter(v => reg.test(v.children[1].innerText))[0]

v.children[1]で2行目(厳密には2要素目)の値を取得できます。
今回であればtd内のテキスト全てで判定することが可能ですので、v.children[1].innerTextとしてテキスト全体をステップに渡した文字列の正規表現で判定しています。
様々な内容が含まれる複雑な要素であればv.getElementsByTagName('a')などで絞り込むと良いでしょう。
最後に[0]としているのはユニークな文字列で検索しているため基本的に1件しかヒットしないためです。
JSで要素の無い添え字でアクセスした場合はundefinedが返却されますので、if(!elem)でテスト失敗にしています。

if(!elem) {
  throw new Error(`要素が存在しません: ${targetText}`);
}

以後はelemの値を用いて必要な情報へアクセスすればOKです。
今回であればelemが行の内容を持っているので、必要なカラムを添え字としてアクセスしてテストしたい要素を選択していきます。

例1) 該当行の2列目に存在するリンクをクリックする

elem.children[1].getElementsByTagName("a")[0].click()

例2) 該当行でクラスを指定した要素に指定した文字列が存在するか確認し、値が異なったらテスト失敗とする

const elemBody = elem.getElementsByClassName("クラス名").innerText;
if (body !== elemBody) {
  throw new Error(`bodyが一致しません: ${body} - ${elemBody}`);
}

例3) 該当行に含まれるデータを取得して別ステップで利用する

/* IDを取得して他のステップでIDと一致するかの検証を行ったり、遷移先のURLにIDが含まれているかの確認に利用する */
return elem.children[0].innerText;

ステップ名をIDの取得など分かりやすい名前にしておけば、別のステップで利用するときに分かりやすくなります。

開発/デバッグ方法

開発/デバッグはブラウザのコンソールで可能です。
テストを行いたいページで開発者ツールを起動し、コンソールにJSを書けば実行できますので、AutifyにJSステップとして追加する前はこちらで検証することをおすすめします!
画像の例では本文の取得と編集ボタンのクリックができるか検証しています。
また、AutifyのJSステップに記載した際はconsole.log出力も実行ログで確認できますので記載しておくと便利です。

JSステップ 実行ログ

まとめ

並列テストでハマりがちなデータ登録&表形式表示でのテストについてお届けしました。
今回は表形式として紹介しましたが、複数要素を含む要素であれば応用可能です。
新規追加→編集→削除の流れをテストするシナリオなどで参考になれば幸いです。

AIを用いたノーコードでE2Eテストを行えるのがAutifyの強みですが、このようなJSステップを組み合わせて柔軟なテストを作成できる部分も面白いですね。
それでは皆様良いテストライフを!