前回、TestCafeでもPage Modelという名前でPage Object Patternが使えそうなことを調べた。
ちょっと試してみようと思ったのでメモ。
環境
TestCafe v1.14.0。
インストールにはNode.js v14.16.1、およびYarn v1.22.5を使用。
Page Object Patternとは
ページオブジェクトモデル :: Seleniumドキュメント や PageFactory · SeleniumHQ/selenium Wiki · GitHub あたりに詳しい。
ページへのインターフェースとしてのページオブジェクトを用意し、ページの操作はそのページオブジェクトを通じて行うことで、UI/UXの変更があってもページオブジェクト内のコードを変更するだけで対応できるという、E2Eテストにおけるデザインパターン。
正直、しっかり設計しないと上手くはいかないが、操作処理をスクリプト的に書き連ねるよりもメンテナンスがしやすくなる。
Cypressのブログでは、 Page Object使うな とか書かれているが、まだまだPage Object Patternは有効じゃないかなと思う。
Googleの検索ページのオブジェクト化
URLを開くのに、 t.navigateTo
や t.openWindow
など、TestControllerのメソッドを使うため、ページオブジェクトのメソッドやコンストラクタにTestControllerを渡してやるか、テストスクリプト内の fixture
や test
でURLを指定する必要があるかと思ったら、リファレンスにページオブジェクト内でTestControllerを使用する方法が記載してあった。
使い方もシンプルで、 import { t } from 'testcafe';
してやれば、テストコンテキストを解決してくれるとのこと。
ということで、この手のサンプルでよくある、Google検索を書いてみる。
以下のJavaScriptを pages/GoogleSearchPage.js
に保存。
import { Selector, t } from 'testcafe'; class GoogleSearchPage { constructor() { this.searchInput = Selector('input[name=q]'); this.searchButton = Selector('input[type=submit]'); } // オブジェクト側にURLを持たせ、オブジェクト生成時にページを開いておきたいが、 // navigateToがPromiseを返し、コンストラクタにasyncが付けられないため、ファクトリーメソッドを定義 // ClientFunction を使えばコンストラクタ内でページ遷移できるかもだが、未検証 static open = async () => { await t.navigateTo('https://www.google.com/').then(); return new GoogleSearchPage(); }; async search(searchText) { await t.typeText(this.searchInput, searchText).click(this.searchButton); // TODO: 検索結果ページオブジェクトを返す } } export default GoogleSearchPage.open;
同じディレクトリに fixture/GoogleSearchFixture.js
として以下のJavaScriptを保存。
import { Selector } from 'testcafe'; import open from '../pages/GoogleSearchPage'; fixture `Google検索`; test('Googleで検索できる', async (t) => { const googleSearchPage = await open(); await googleSearchPage.search('Google'); // TODO: 判定を検索結果ページオブジェクト経由で行う await t.expect(Selector('input[name=q]').value).eql('Google'); });
これで testcafe chrome ./fixtures/GoogleSearchFixture.js
してやると、Google検索してその文字列を確認という、サンプルらしいテストができた。
振り返り
ページオブジェクト側にURLを持たせ、インスタンス生成の段階でページを開いておきたいと思ったが、 import/export
や async/await
がいまいち分かっていないので、冗長なコードになってしまった。
まあ、ログインページなど最初のページだけ我慢すれば、他のページ遷移は基本的にリンクをたどったりすればいいので、それぞれ遷移先のページオブジェクトを返すメソッドにすればよさそう。
毎回 async/await
を書かないといけないのは面倒だが、イメージはつかめた。
余談
TestCafeの公式ページが https://testcafe.io/ になっていて、前回記事の https://devexpress.github.io/testcafe/ から変わっていた。
Cypressも試してみたいんだけど、開発しているプロダクトがIE対応必須なので、2021年4月現在ではIE未対応なCypressは使えないんだよなぁ。