Page Object デザインパターン
UI テストにおける Page Object デザインパターンとは、1つの画面を1つのオブジェクトとして定義し、画面に対する操作や検証をオブジェクト経由で行う設計指針です。このデザインパターンを適用させることにより、可読性や保守性を向上させることができます。
コードを見た方がわかりやすいと思うので、今回は簡単な具体例で考えてみます。
ログイン処理の UI テスト
Espresso を使った次のようなログイン処理の UI テストを例に考えます。
@Test
fun loginSuccess() {
// ユーザー ID を入力
onView(withId(R.id.user_id)).perform(
ViewActions.replaceText("test_user_id")
)
// パスワードを入力
onView(withId(R.id.password)).perform(
ViewActions.replaceText("test_user_password")
)
// ログインボタンをクリック
onView(withId(R.id.login_button)).perform(click())
// ホーム画面に遷移して「ようこそ」が表示されていることを確認
onView(withId(R.id.welcome)).check(
matches(withText("ようこそ"))
)
}
ログインに必要な情報を入力してログインをクリックし、次のページで「ようこそ」と表示されることを確認する UI テストです。
ここではログイン画面とログイン後表示されるホーム画面の2つの画面が登場します。この2つの画面について、それぞれ Page Object デザインパターンに沿ってオブジェクト化してみます。
ログイン画面のオブジェクト化
まずはログイン画面をオブジェクト化します。
Page Object デザインパターンに従って画面をオブジェクト化するときは「その画面でできること」を抽出して1つずつメソッド化します。今回のログイン画面で言えば下記のとおり。
- ユーザー ID 欄にテキストを入力する
- パスワード欄にテキストを入力する
- ログインボタンを押してログインする
この3つをオブジェクトに実装します。
class LoginPage() {
fun inputUserId(userId: String): LoginPage {
// ユーザー ID を入力
onView(withId(R.id.user_id)).perform(
ViewActions.replaceText("test_user_id")
)
return this
}
fun inputPassword(): LoginPage {
// パスワードを入力
onView(withId(R.id.password)).perform(
ViewActions.replaceText("test_user_password")
)
return this
}
fun clickLoginButton(userId: String, password: String): HomePage {
// ログインボタンをクリック
onView(withId(R.id.login_button)).perform(click())
// 遷移先であるホーム画面のオブジェクトを返却する
return HomePage()
}
}
3つのメソッドが実装できました。ここでポイントとなるのはメソッドの戻り値です。メソッドの戻り値は必ず Page Object にします。処理のあと画面に止まるのであれば this
を、画面を遷移するのであれば遷移先のオブジェクトを返します。こうすることでメソッドチェーンが可能となり可読性が向上します。
LoginPage().inputUserId("...")
.inputPassword("...")
.clickLoginButton()
ホーム画面のオブジェクト化
次にホーム画面をオブジェクト化します。
class HomePage() {
fun assertWelcomeMessage(): HomePage {
//「ようこそ」が表示されていることを確認
onView(withId(R.id.welcome)).check(
matches(withText("ようこそ"))
)
return this
}
}
今回は特にホーム画面で操作することがないので「ようこそ」が表示されていることを確認するアサーションだけ実装します。
完成形
下記がテストの完成形になります。
@Test
fun loginSuccess() {
LoginPage().inputUserId("test_user_id")
.inputPassword("test_user_password")
.clickLoginButton()
.assertWelcomeMessage()
}
一番最初に示したコードよりも、何をやっているか直感的でわかりやすくなっているかと思います。
また、誤ったパスワードが入力されたときにログインが失敗するテストを作りたい場合、inputPassword()
に不正なパスワードを入力することでログインに失敗させることができます。ログイン成功、失敗それぞれのテストでメソッドを使いまわせるため、保守の側面においても効果的かと思います。