3
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Android】UI テストに Page Object デザインパターンを適用する

Posted at

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() に不正なパスワードを入力することでログインに失敗させることができます。ログイン成功、失敗それぞれのテストでメソッドを使いまわせるため、保守の側面においても効果的かと思います。

3
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?