パート 3: コードを再利用可能テストに更新する

パート2では、Marionetteのクライアントコマンドを使用するとFirefox OSの制御が簡単にできることを学びましたが、Pythonのコンソールにそれらを入力するのは、遅くて退屈です。テスト自動化の重要な利点は、それが自律的に実行できることです。すべてのコードをPythonのファイルにコマンドを置き、次に一度にすべてを実行することができるように、このパートでそれを行う方法を学習します。

テストケースの要約

連絡先アプリを開き、新しい連絡先を追加する - パート2では、こんな典型的なテストケースを実行する手順をたどってきました:

  1. Firefox OSのロックを解除 (オプション; 第2部では、手動でロック画面をオフにしました。それゆえ、以下のコードでこれを含みません。)
  2. 連絡先アプリに切り替え
  3. 新しい連絡先の追加アイコンをタップします
  4. 連絡先の名前を入力
  5. タップして完了
  6. しばらく待って、連絡先が存在していることを確認します

Pythonのファイルに我々のテストを置きます

Pythonのファイルにこれらのステップをすべて入れた場合、それを再利用し、はるかに素早く実行できます。あなたに都合の良いディレクトリを選んで、そこに test_add_contact.py と呼ばれる新しいテキストファイルを作成します。

以下に示すように、このファイルにパート2で見たコマンドを入力します。良い習慣であるため、Pythonのクラス構造を使用し、チュートリアルの今後のステップでの良い基盤を作ります。

import time
from marionette import Marionette

class TestContacts:

    def __init__(self):
        self.test_add_contacts()

    def test_add_contacts(self):
        # Create the client for this session. Assuming you're using the default port on a Marionette instance running locally
        self.marionette = Marionette()
        self.marionette.start_session()

        # Switch context to the homescreen iframe and tap on the contacts icon
        time.sleep(2)
        home_frame = self.marionette.find_element('css selector', 'div.homescreen iframe')
        self.marionette.switch_to_frame(home_frame)
        contacts_icon = self.marionette.find_element('xpath', "//div[@class='icon']//span[contains(text(),'Contacts')]")
        contacts_icon.tap()

        # Switch context back to the base frame
        self.marionette.switch_to_frame()
        time.sleep(2)

        # Switch context to the contacts app
        contacts_frame = self.marionette.find_element('css selector', "iframe[data-url*='contacts']")
        self.marionette.switch_to_frame(contacts_frame)

        # Tap [+] to add a new Contact
        self.marionette.find_element('id', 'add-contact-button').tap()
        time.sleep(2)

        # Type name into the fields
        self.marionette.find_element('id', 'givenName').send_keys('John')
        self.marionette.find_element('id', 'familyName').send_keys('Doe')

        # Tap done
        self.marionette.find_element('id', 'save-button').tap()
        time.sleep(2)

        # Close the Marionette session now that the test is finished
        self.marionette.delete_session()

if __name__ == '__main__':
    TestContacts()

注意: コード内で、パート2でカバーしていないと気づく1つの追加事項は、Python time.sleep()関数です— これは、次の行に継続する前に、一定時間スクリプトを停止します (秒単位で定義) 。ユーザが手動でボタンをタップすることなどや、FirefoxのOSが結果のアクションを完了するのを待つことをシミュレートする必要があるため、自動テストにこれらの行を追加しました。遅延なしにこのスクリプトを実行した場合は、 Pythonはすべてを瞬時に完了し、Firefox OSが追いつくことができないために、おそらくテストは失敗となるだろう。

今、ターミナルでテストが保存されているディレクトリに移動し、次のコマンドを実行して、テストを実行することができます:

python test_add_contact.py

注意: Pythonのインデントルールに注意してください。 コピーして貼り付けた後は、コードを実行するためにすべてを正しくインデントする必要があるかもしれません。これに関連するエラーが発生した場合は、すべてのインデントレベルはタブで区切られていることを確認します。

注意: また、上記のコードを使用して挿入された名前はは"John Doe"であることがわかります。パート2での"Foo Bar"という名前と違います。コードが正常に実行し、別の連絡先を追加するように、我々はこうしました。 同じ名前の連絡先を追加しようとする場合、Firefox OSでは重複する連絡先についての警告が表示されます。現時点では、テストの実行を繰り返す最善の方法は、FirefoxのOSのインターフェースに入り、毎回実行する前に、手動で連絡先を削除することです。

アサーションを追加する

自動テストに重要となり、我々のテストにまだ欠落している一点は、アサーションです — Firefox OSが望む状態に達しているかどうか (つまりテストが成功したかどうか)、というレポートまたは指標となるもの。新しい連絡先がアプリ内に存在するかどうかを確認するためにいくつかのコードを追加することでこれをやります。
 
# Close the Marionette session...行の直前に、このコードに追加し、クラスの他の行と同じレベルにインデントされていることを確認します:

# Now let's find the contact item and get its text
contact_name = self.marionette.find_element('css selector', 'li.contact-item:not([data-group$="ice"]) p').text     
assert contact_name == 'John Doe'

古い連絡先を削除し、次のようにテストを再実行してみてください:

python test_add_contact.py

全てがうまく実行できらた素晴らしい、今度は機能テストがあります!

注意: アサーションが失敗した場合は、以前の'Foo Bar'の連絡先はもう存在していないことを確認してください。アサートの前にCSSセレクタは、実際には、リスト内の最初の連絡先を拾っています。(アサートを呼ぶ前に print "Contact name: %s" % contact_name と呼ぶことで、見ることができます。).

注意: アサーションは現在何もしないように見えますが、Part 5: Introducing a test runnerで紹介されているように、テストランナーを使用し始めるときアサーションはとても重要です。unittestのようなテストランナーは、アサーションを使ってテストが正常か完了したかどうかを確認し、次にこれらのテストの結果 (OK or FAIL)を返します。

タイミングに関する注意

自動テストを書く時に、対処するのが最も困難なことの一つは、タイミングです。Firefox OSが最後の一つを完了する前に、テストが次のステップに移行するなら、失敗を得る可能性が高いです。
 
上述したように、サンプルコードではこの問題を解決するためにtime.sleep(x)コマンドを追加しました。しかしながら、time.sleep(x)を使用することは良い方法ではありません。ハードコードされた設定時間を使用すると、テスト実行に長すぎたり、長さが足りなかったりする可能性があります。後者は最悪のケースであります; それは、偽陰性のテスト結果 (実際にアプリは完全に機能するが、テストが期待するより少し遅く振る舞う時に、失敗とレポートされるテストの意味) を生じます。

次のパートでは、テストの特定部分を抽象化して、独立したPythonの関数にするよう進行します。そして、sleep() 関数を、適切な動的待機で置き換えます。

ドキュメントのタグと貢献者

 このページの貢献者: chrisdavidmills, hamasaki, Uemmra3, shide55
 最終更新者: chrisdavidmills,