テスターとして作業をより効率的かつ高速にするためにできる最も重要なことの1つは、テストしているアプリを自動化することです。手動テストのみに依存することは現実的ではありません。アプリコードにプッシュされたすべての変更をテストするために、テストのフルセットを毎日(場合によっては1日に複数回)実行する必要があるためです。
この記事では、チームが特定するまでの道のりについて説明します GoogleのEarlGrey1.0 iOSの自動化のコンテキストで私たちに最適なツールとして ApeeScapeTalentアプリ 。私たちがそれを使用しているという事実は、EarlGreyがすべての人にとって最高のテストツールであることを意味するわけではありません-それはたまたま私たちのニーズに合ったものです。
長年にわたり、私たちのチームはiOSとAndroidの両方でさまざまなモバイルアプリを構築してきました。最初は、クロスプラットフォームのUIテストツールを使用して、単一のテストセットを作成し、さまざまなモバイルオペレーティングシステムで実行できるようにすることを検討しました。まず、 appium 、利用可能な最も人気のあるオープンソースオプション。
しかし、時間が経つにつれて、 appiumの制限 ますます明白になりました。私たちの場合、Appiumの2つの主な欠点は次のとおりです。
Appiumの最初の欠点を軽減するために、テストをより安定させるために、あらゆる種類のコードの調整とハックを作成しました。しかし、2番目に対処するために私たちにできることは何もありませんでした。 iOSまたはAndroidの新しいバージョンがリリースされるたびに、Appiumは追いつくのに長い時間がかかりました。そして、非常に多くの場合、多くのバグがあるため、最初の更新は使用できませんでした。その結果、古いプラットフォームバージョンでテストを実行し続けるか、有効なAppiumアップデートが利用可能になるまでテストを完全にオフにすることを余儀なくされることがよくありました。
このアプローチは理想からはほど遠いものでした。これらの問題と、詳細には説明しない追加の問題があるため、代替案を探すことにしました。新しいテストツールの最上位の基準は次のとおりです。 安定性の向上 そして より速い更新 。調査の結果、プラットフォームごとにネイティブテストツールを使用することにしました。
だから、私たちはに移行しました 表現 Androidプロジェクトの場合とEarlGrey1.0の場合 iOS開発 。後から考えると、これは良い決断だったと言えます。プラットフォームごとに1つずつ、2つの異なるテストセットを作成して維持する必要があるために「失われた」時間は、多くの不安定なテストを調査する必要がなく、バージョンの更新でダウンタイムが発生しないことで埋め合わせられました。
開発しているアプリと同じXcodeプロジェクトにフレームワークを含める必要があります。そこで、UIテストをホストするためにルートディレクトリにフォルダを作成しました。 EarlGrey.swift
の作成テストフレームワークをインストールする場合、ファイルは必須であり、その内容は事前定義されています。
EarlGreyBase
すべてのテストクラスの親クラスです。一般的なsetUp
が含まれていますおよびtearDown
XCTestCase
から拡張されたメソッド。 setUp
では、ほとんどのテストで一般的に使用されるスタブをロードし(スタブについては後で詳しく説明します)、テストの安定性を高めることに気付いたいくつかの構成フラグも設定します。
// Turn off EarlGrey's network requests tracking since we don't use it and it can block tests execution GREYConfiguration.sharedInstance().setValue(['.*'], forConfigKey: kGREYConfigKeyURLBlacklistRegex) GREYConfiguration.sharedInstance().setValue(false, forConfigKey: kGREYConfigKeyAnalyticsEnabled)
ページオブジェクトデザインパターンを使用します。アプリの各画面には、すべてのUI要素とそれらの可能な相互作用が定義されている対応するクラスがあります。このクラスは「ページ」と呼ばれます。テストメソッドは、ページとは別のファイルおよびクラスにある機能ごとにグループ化されています。
Webサーバーとしてラズベリーパイを使用する
すべてがどのように表示されるかをよりよく理解するために、これは、アプリの[ログイン]画面と[パスワードを忘れた場合]画面がどのように表示され、ページオブジェクトによってどのように表されるかを示しています。
この記事の後半で、Loginページオブジェクトのコードコンテンツを紹介します。
EarlGreyがテストアクションをアプリと同期する方法は、必ずしも完璧ではありません。たとえば、UI階層にまだロードされていないボタンをクリックしようとして、テストが失敗する場合があります。この問題を回避するために、要素を操作する前に、要素が目的の状態で表示されるまで待機するカスタムメソッドを作成しました。
次にいくつかの例を示します。
static func asyncWaitForVisibility(on element: GREYInteraction) { // By default, EarlGrey blocks test execution while // the app is animating or doing anything in the background. //https://github.com/google/EarlGrey/blob/master/docs/api.md#synchronization GREYConfiguration.sharedInstance().setValue(false, forConfigKey: kGREYConfigKeySynchronizationEnabled) element.assert(grey_sufficientlyVisible()) GREYConfiguration.sharedInstance().setValue(true, forConfigKey: kGREYConfigKeySynchronizationEnabled) } static func waitElementVisibility(for element: GREYInteraction, timeout: Double = 15.0) -> Bool { GREYCondition(name: 'Wait for element to appear', block: { var error: NSError? element.assert(grey_notNil(), error: &error) return error == nil }).wait(withTimeout: timeout, pollInterval: 0.5) if !elementVisible(element) { XCTFail('Element didn't appear') } return true }
EarlGreyが単独で行っていないもう1つのことは、目的の要素が表示されるまで画面をスクロールすることです。これを行う方法は次のとおりです。
static func elementVisible(_ element: GREYInteraction) -> Bool { var error: NSError? element.assert(grey_notVisible(), error: &error) if error != nil { return true } else { return false } } static func scrollUntilElementVisible(_ scrollDirection: GREYDirection, _ speed: String, _ searchedElement: GREYInteraction, _ actionElement: GREYInteraction) -> Bool { var swipes = 0 while !elementVisible(searchedElement) && swipes = 10 { return false } else { return true } }
私たちが特定したEarlGreyのAPIにない他のユーティリティメソッドは、要素のカウントとテキスト値の読み取りです。これらのユーティリティのコードはGitHubで入手できます。 ここに そして ここに 。
バックエンドサーバーの問題によって引き起こされる誤ったテスト結果を確実に回避するために、 OHHTTPStubs 図書館 サーバー呼び出しをモックします。彼らのホームページのドキュメントは非常に単純ですが、GraphQLAPIを使用するアプリで応答をスタブ化する方法を示します。
class StubsHelper { static let testURL = URL(string: 'https://[our backend server]')! static func setupOHTTPStub(for request: StubbedRequest, delayed: Bool = false) { stub(condition: isHost(testURL.host!) && hasJsonBody(request.bodyDict())) { _ in let fix = appFixture(forRequest: request) if delayed { return fix.requestTime(0.1, responseTime: 7.0) } else { return fix } } } static let stubbedEmail = ' [email protected] ' static let stubbedPassword = 'password' enum StubbedRequest { case login func bodyDict() -> [String: Any] { switch self { case .login: return EmailPasswordSignInMutation( email: stubbedTalentLogin, password: stubbedTalentPassword ).makeBodyIdentifier() } } func statusCode() -> Int32 { return 200 } func jsonFileName() -> String { let fileName: String switch self { case .login: fileName = 'login' } return '(fileName).json' } } private extension GraphQLOperation { func makeBodyIdentifier() -> [String: Any] { let body: GraphQLMap = [ 'query': queryDocument, 'variables': variables, 'operationName': operationName ] // Normalize values like enums here, otherwise body comparison will fail guard let normalizedBody = body.jsonValue as? [String: Any] else { fatalError() } return normalizedBody } }
スタブのロードは、setupOHTTPStub
を呼び出すことによって実行されます。方法:
StubsHelper.setupOHTTPStub(for: .login)
このセクションでは、上記のすべての原則を使用して、実際のエンドツーエンドのログインテストを作成する方法を示します。
import EarlGrey final class LoginPage { func login() -> HomePage { fillLoginForm() loginButton().perform(grey_tap()) return HomePage() } func fillLoginForm() { ElementsHelper.waitElementVisibility(emailField()) emailField().perform(grey_replaceText(StubsHelper.stubbedTalentLogin)) passwordField().perform(grey_tap()) passwordField().perform(grey_replaceText(StubsHelper.stubbedTalentPassword)) } func clearAllInputs() { if ElementsHelper.elementVisible(passwordField()) { passwordField().perform(grey_tap()) passwordField().perform(grey_replaceText('')) } emailField().perform(grey_tap()) emailField().perform(grey_replaceText('')) } } private extension LoginPage { func emailField(file: StaticString = #file, line: UInt = #line) -> GREYInteraction { return EarlGrey.selectElement(with: grey_accessibilityLabel('Email'), file: file, line: line) } func passwordField(file: StaticString = #file, line: UInt = #line) -> GREYInteraction { return EarlGrey.selectElement( with: grey_allOf([ grey_accessibilityLabel('Password'), grey_sufficientlyVisible(), grey_userInteractionEnabled() ]), file: file, line: line ) } func loginButton(file: StaticString = #file, line: UInt = #line) -> GREYInteraction { return EarlGrey.selectElement(with: grey_accessibilityID('login_button'), file: file, line: line) } } class BBucketTests: EarlGreyBase { func testLogin() { StubsHelper.setupOHTTPStub(for: .login) LoginPage().clearAllInputs() let homePage = LoginPage().login() GREYAssertTrue( homePage.assertVisible(), reason: 'Home screen not displayed after successful login' ) } }
継続的インテグレーションシステムとしてJenkinsを使用し、すべてのプルリクエストでコミットごとにUIテストを実行します。
オプション値の計算方法
を使用しております fastlane scan
CIでテストを実行し、レポートを生成します。失敗したテストのスクリーンショットをこれらのレポートに添付しておくと便利です。残念ながら、scan
この機能は提供されていないため、カスタムメイドする必要がありました。
tearDown()
で関数では、テストが失敗したかどうかを検出し、失敗した場合はiOSシミュレーターのスクリーンショットを保存します。
import EarlGrey import XCTest import UIScreenCapture override func tearDown() { if testRun!.failureCount > 0 { // name is a property of the XCTest instance // https://developer.apple.com/documentation/xctest/xctest/1500990-name takeScreenshotAndSave(as: name) } super.tearDown() } func takeScreenshotAndSave(as testCaseName: String) { let imageData = UIScreenCapture.takeSnapshotGetJPEG() let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true) let filePath = '(paths[0])/(testCaseName).jpg' do { try imageData?.write(to: URL.init(fileURLWithPath: filePath)) } catch { XCTFail('Screenshot not written.') } }
スクリーンショットはSimulatorフォルダーに保存され、ビルドアーティファクトとして添付するには、そこからスクリーンショットをフェッチする必要があります。 Rake
を使用しますCIスクリプトを管理します。これは、テスト成果物を収集する方法です。
次のルールのうち、資本予算分析に正しいものはどれですか?
def gather_test_artifacts(booted_sim_id, destination_folder) app_container_on_sim = `xcrun simctl get_app_container #{booted_sim_id} [your bundle id] data`.strip FileUtils.cp_r '#{app_container_on_sim}/Documents', destination_folder end
あなたがあなたの自動化するための速くて信頼できる方法を探しているなら iOS テスト、EarlGrey以上のものを探す必要はありません。これはGoogleによって開発および保守されており(もっと言う必要がありますか?)、多くの点で、現在利用可能な他のツールよりも優れています。
テストの安定性を促進するユーティリティメソッドを準備するには、フレームワークを少しいじる必要があります。これを行うには、カスタムユーティリティメソッドの例から始めることができます。
スタブ化されたデータでテストして、テストが失敗しないことを確認することをお勧めします。バックエンドサーバーには、期待するすべてのテストデータがないためです。 OHHTTPStubs
を使用するまたは同様のローカルWebサーバーで作業を完了します。
CIでテストを実行するときは、デバッグを容易にするために、失敗したケースのスクリーンショットを必ず提供してください。
なぜEarlGrey2.0にまだ移行しなかったのか疑問に思われるかもしれませんが、ここで簡単に説明します。新しいバージョンは昨年リリースされ、v1.0を超えるいくつかの機能強化が約束されています。残念ながら、EarlGreyを採用したとき、v2.0は特に安定していませんでした。したがって、まだv2.0に移行していません。ただし、私たちのチームは、将来的にインフラストラクチャを移行できるように、新しいバージョンのバグ修正を熱心に待っています。
に関するEarlGreyの入門ガイド GitHubホームページ です インクルード プロジェクトのテストフレームワークを検討している場合は、最初から始めたい場所です。そこには、使いやすいインストールガイド、ツールのAPIドキュメント、およびテストの作成中に簡単に使用できる方法ですべてのフレームワークのメソッドをリストした便利なチートシートがあります。
iOSの自動テストの作成に関する追加情報については、次のいずれかを確認することもできます。 以前のブログ投稿 。
機能テストは、システムの機能を検証するプロセスです。したがって、UIテストは、ユーザーインターフェイスを介した機能テストです。
UIテストでは、アプリケーションの複数のレイヤーが正常に連携しているかどうかを検証します。単体テストやAPIテストなどの低レベルのテストでは、UIテストのように、バグを探すためにこのような幅広いネットをキャストすることはできません。
テストする必要のあるシステムの性質に応じて、使用できる特定のツールがあります。よく知られているUI自動化テストツールには、Selenium、Appium、Ranorex、またはAutoItがあります。