A Splash of EarlGrey – UI Menguji Aplikasi Toptal Talent

Diterbitkan: 2022-03-11

Salah satu hal terpenting yang dapat Anda lakukan sebagai penguji untuk membuat pekerjaan Anda lebih efisien dan cepat adalah mengotomatiskan aplikasi yang Anda uji. Mengandalkan hanya pada pengujian manual tidak layak karena Anda perlu menjalankan set lengkap pengujian setiap hari, terkadang beberapa kali sehari, menguji setiap perubahan yang didorong ke kode aplikasi.

Artikel ini akan menjelaskan perjalanan tim kami untuk mengidentifikasi EarlGrey 1.0 Google sebagai alat yang paling berhasil bagi kami dalam konteks otomatisasi aplikasi iOS Toptal Talent. Fakta bahwa kami menggunakannya tidak berarti EarlGrey adalah alat pengujian terbaik untuk semua orang - itu hanya alat yang sesuai dengan kebutuhan kami.

Mengapa Kami Bertransisi ke EarlGrey

Selama bertahun-tahun, tim kami telah membuat berbagai aplikasi seluler di iOS dan Android. Pada awalnya, kami mempertimbangkan untuk menggunakan alat pengujian UI lintas platform yang memungkinkan kami untuk menulis satu set pengujian dan menjalankannya pada sistem operasi seluler yang berbeda. Pertama, kami menggunakan Appium, opsi sumber terbuka paling populer yang tersedia.

Namun seiring berjalannya waktu, batasan Appium menjadi semakin jelas. Dalam kasus kami, dua kelemahan utama Appium adalah:

  • Stabilitas kerangka kerja yang dipertanyakan menyebabkan banyak serpihan uji.
  • Proses pembaruan yang relatif lambat menghambat pekerjaan kami.

Untuk mengurangi kekurangan Appium pertama, kami menulis segala macam tweak kode dan peretasan untuk membuat pengujian lebih stabil. Namun, tidak ada yang bisa kami lakukan untuk mengatasi yang kedua. Setiap kali versi baru iOS atau Android dirilis, Appium membutuhkan waktu lama untuk mengejar ketinggalan. Dan sangat sering, karena banyak bug, pembaruan awal tidak dapat digunakan. Akibatnya, kami sering dipaksa untuk terus menjalankan pengujian kami pada versi platform yang lebih lama atau mematikannya sepenuhnya hingga pembaruan Appium yang berfungsi tersedia.

Pendekatan ini jauh dari ideal, dan karena masalah ini, bersama dengan masalah tambahan yang tidak akan kami bahas secara rinci, kami memutuskan untuk mencari alternatif. Kriteria teratas untuk alat pengujian baru adalah peningkatan stabilitas dan pembaruan yang lebih cepat . Setelah beberapa penyelidikan, kami memutuskan untuk menggunakan alat pengujian asli untuk setiap platform.

Jadi, kami beralih ke Espresso untuk proyek Android dan ke EarlGrey 1.0 untuk pengembangan iOS. Di belakang, kita sekarang dapat mengatakan bahwa ini adalah keputusan yang baik. Waktu yang "hilang" karena kebutuhan untuk menulis dan memelihara dua set pengujian yang berbeda, satu untuk setiap platform, lebih dari sekadar dibuat dengan tidak perlu menyelidiki begitu banyak pengujian yang tidak stabil dan tidak mengalami downtime pada pembaruan versi.

Struktur Proyek Lokal

Anda harus menyertakan kerangka kerja dalam proyek Xcode yang sama dengan aplikasi yang sedang Anda kembangkan. Jadi kami membuat folder di direktori root untuk meng-host tes UI. Membuat file EarlGrey.swift adalah wajib saat menginstal kerangka pengujian dan kontennya telah ditentukan sebelumnya.

Aplikasi Bakat Toptal: Struktur Proyek Lokal

EarlGreyBase adalah kelas induk untuk semua kelas uji. Ini berisi metode tearDown dan setUp umum, diperluas dari XCTestCase . Di setUp , kami memuat stub yang umumnya akan digunakan oleh sebagian besar pengujian (lebih lanjut tentang stub nanti) dan kami juga menyetel beberapa tanda konfigurasi yang kami perhatikan meningkatkan stabilitas pengujian:

 // 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)

Kami menggunakan pola desain Objek Halaman - setiap layar di aplikasi memiliki kelas yang sesuai di mana semua elemen UI dan kemungkinan interaksinya ditentukan. Kelas ini disebut “halaman.” Metode pengujian dikelompokkan berdasarkan fitur yang berada di file dan kelas terpisah dari halaman.

Untuk memberi Anda gambaran yang lebih baik tentang bagaimana semuanya ditampilkan, seperti inilah tampilan layar Login dan Lupa Kata Sandi di aplikasi kami dan bagaimana tampilannya diwakili oleh objek halaman.

Ini adalah tampilan layar Login dan Lupa Kata Sandi di aplikasi kami.

Nanti pada artikel tersebut akan kami sajikan isi kode dari objek halaman Login.

Metode Utilitas Kustom

Cara EarlGrey menyinkronkan tindakan pengujian dengan aplikasi tidak selalu sempurna. Misalnya, mungkin mencoba mengklik tombol yang belum dimuat dalam hierarki UI, menyebabkan pengujian gagal. Untuk menghindari masalah ini, kami membuat metode khusus untuk menunggu hingga elemen muncul dalam status yang diinginkan sebelum kami berinteraksi dengannya.

Berikut adalah beberapa contoh:

 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 }

Satu hal lain yang EarlGrey tidak lakukan sendiri adalah menggulir layar hingga elemen yang diinginkan terlihat. Berikut adalah bagaimana kita bisa melakukannya:

 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 { if speed == "slow" { actionElement.perform(grey_swipeSlowInDirection(scrollDirection)) } else { actionElement.perform(grey_swipeFastInDirection(scrollDirection)) } swipes += 1 } if swipes >= 10 { return false } else { return true } }

Metode utilitas lain yang hilang dari API EarlGrey yang kami identifikasi adalah menghitung elemen dan membaca nilai teks. Kode untuk utilitas ini tersedia di GitHub: di sini dan di sini.

Menghentikan Panggilan API

Untuk memastikan kami menghindari hasil tes palsu yang disebabkan oleh masalah server back-end, kami menggunakan perpustakaan OHHTTPStubs untuk mengejek panggilan server. Dokumentasi di beranda mereka cukup mudah, tetapi kami akan menyajikan bagaimana kami menghentikan respons di aplikasi kami, yang menggunakan GraphQL API.

 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 } }

Memuat rintisan dilakukan dengan memanggil metode setupOHTTPStub :

 StubsHelper.setupOHTTPStub(for: .login)

Menyatukan Semuanya

Bagian ini akan mendemonstrasikan bagaimana kami menggunakan semua prinsip yang dijelaskan di atas untuk menulis tes login end-to-end yang sebenarnya.

 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" ) } }

Menjalankan Tes di CI

Kami menggunakan Jenkins sebagai sistem integrasi berkelanjutan kami, dan kami menjalankan tes UI untuk setiap komit di setiap permintaan tarik.

Kami menggunakan fastlane scan untuk menjalankan tes di CI dan menghasilkan laporan. Ini berguna untuk memiliki tangkapan layar yang dilampirkan pada laporan ini untuk pengujian yang gagal. Sayangnya, scan tidak menyediakan fungsionalitas ini, jadi kami harus membuatnya sendiri.

Dalam fungsi tearDown() , kami mendeteksi jika pengujian gagal dan menyimpan tangkapan layar simulator iOS jika gagal.

 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.") } }

Tangkapan layar disimpan di folder Simulator, dan Anda harus mengambilnya dari sana untuk melampirkannya sebagai artefak build. Kami menggunakan Rake untuk mengelola skrip CI kami. Inilah cara kami mengumpulkan artefak uji:

 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

Takeaways Kunci

Jika Anda mencari cara yang cepat dan andal untuk mengotomatiskan pengujian iOS Anda, tidak perlu mencari yang lain selain EarlGrey. Ini dikembangkan dan dikelola oleh Google (perlu saya katakan lebih banyak?), Dan dalam banyak hal, ini lebih unggul dari alat lain yang tersedia saat ini.

Anda perlu sedikit mengutak-atik kerangka kerja untuk menyiapkan metode utilitas guna meningkatkan stabilitas pengujian. Untuk melakukan ini, Anda dapat mulai dengan contoh metode utilitas kustom kami.

Kami merekomendasikan pengujian pada data yang di-stub untuk memastikan pengujian Anda tidak gagal karena server back-end tidak memiliki semua data pengujian yang Anda harapkan. Gunakan OHHTTPStubs atau server web lokal serupa untuk menyelesaikan pekerjaan.

Saat menjalankan pengujian Anda di CI, pastikan untuk memberikan tangkapan layar untuk kasus yang gagal agar proses debug lebih mudah.

Anda mungkin bertanya-tanya mengapa kami belum bermigrasi ke EarlGrey 2.0, dan inilah penjelasan singkatnya. Versi baru dirilis tahun lalu dan menjanjikan beberapa peningkatan di atas v1.0. Sayangnya, ketika kami mengadopsi EarlGrey, v2.0 tidak terlalu stabil. Oleh karena itu, kami belum melakukan transisi ke v2.0. Namun, tim kami sangat menantikan perbaikan bug untuk versi baru sehingga kami dapat memigrasikan infrastruktur kami di masa mendatang.

Sumber Daya Online

Panduan Memulai EarlGrey di beranda GitHub adalah tempat Anda ingin memulai jika Anda mempertimbangkan kerangka pengujian untuk proyek Anda. Di sana, Anda akan menemukan panduan penginstalan yang mudah digunakan, dokumentasi API alat, dan lembar contekan praktis yang mencantumkan semua metode kerangka kerja dengan cara yang mudah digunakan saat menulis pengujian Anda.

Untuk informasi tambahan tentang menulis tes otomatis untuk iOS, Anda juga dapat melihat salah satu posting blog kami sebelumnya.