automated ui testing for ios apps using kif framework and swift

Post on 23-Jan-2018

598 Views

Category:

Software

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

USER INTERFACE

TESTING WITH KIF & SWIFT

CASE FOR UI TESTINGINTEGRATION

NAVIGATION FLOWDATA FLOW

USER EXPERIENCEACCESSIBILITY

REFACTORINGS / REGRESSIONS

UI TESTING IS HARD AND TIME CONSUMING

UNLESSAUTOMATED

EASY TO MAINTAIN FAST

NATIVE SUPPORT FOR UI TESTING

UI AUTOMATION (JavaScript)XCUI (Swift/Objective C)

BASED ON UIAcessibility PROTOCOL

KIFKeep It Functional - An iOS Functional

Testing Framework

https://github.com/kif-framework/KIF

UICONF 2016 TALK BY ELLEN SHAPIRO https://youtu.be/hYCUy-9yq_M

OBJECTIVE C - KIFTestCase RUNS IN APP TARGET

beforeAll, beforeEach, afterAll, afterEachKIFUITestActor - USER ACTIONS

KIFSystemTestActor - SYSTEM/DEVICE ACTIONS SWIFT - XCTestCase

ADD SIMPLE EXTENSION TO ACCESS KIFUITestActor AND KIFSystemTestActor

OBJECTIVE C [tester tapViewWithAccessibilityLabel:@"Login"];

SWIFT

tester().tapView(withAccessibilityLabel : "Login")

EXTENSIONS FOR READIBILITY extension KIFUITestActor {

@discardableResult func waitForButton(_ label: String) -> UIView! { let button = self.waitForTappableView(withAccessibilityLabel: label, traits: UIAccessibilityTraitButton)

return button }

func tapButton(_ accessibilityLabel: String, value: String) { let button = waitForButton(accessibilityLabel, value: value) button?.tap() }

}

RESULT

tester().tapButton(“Login”) // or even better – tester().tapLoginButton()

PROTOCOL TO ACCESS APP DATA public protocol UsesCoreDataDatabase { func dbContext() -> NSManagedObjectContext func isDbEmpty(_ context: NSManagedObjectContext) -> Bool func deleteDbData(_ context: NSManagedObjectContext) }

public extension UsesCoreDataDatabase { func dbContext() -> NSManagedObjectContext { let appDelegate: YourAppDelegate = UIApplication.shared.delegate as! YourAppDelegate let context = appDelegate.value(forKey: "context") as! NSManagedObjectContext XCTAssertNotNil(context) return context! }

func isDbEmpty(_ context: NSManagedObjectContext) -> Bool { // access model objects from context let count = …. return (count == 0) }

func deleteDbData(_ context: NSManagedObjectContext) { // Delete data }

CUSTOM TEST CASES class MyTestCaseWithEmptyDatabase: KIFTestCase, UsesCoreDataDatabase {

override func beforeEach() { let context = dbContext()

if ! sDbEmpty(context) { let name = MyAppServices.deviceModel() if name == "iPhone Simulator" { deleteDbData(context) } else { } // Are you sure want to delete data from device? }

} }

class MyTestCaseWithWizardFilled: MyTestCaseWithEmptyDatabase { var rentAmount = “100”; var unitName= “M10”; var tenantName = “Anna" override func beforeEach() { super.beforeEach() let context = dbContext() MyWizardController.saveFromWizard(in: context, address: unitName, tenant: tenantName amount: rentAmount) }

}

class MyTestCaseWithFixturesLoaded : MyTestCaseWithEmptyDatabase { // load fixture data in database

}

DRY, READABLE TESTS class PaymentTests: MyTestCaseWithWizardFilled { func testAddPayment_fromUnitDashboard() {

// given - tenantName, unitName, today are defined as MyTestCaseWithWizardFilled class variables let amount = “5.1”; let paymentAmount = “$5.10”

tester().tapPropertiesTabButton() tester().tapUnit(unitName, tenant: tenantName) tester().waitForTenantBalanceScreen(tenantName, currentBalance: "$0.00") tester().waitForLastPaymentLine("No rent payments received", amount: “") // even this should be replaced by waitForNoLastPaymentLine // when tester().tapAddPaymentButton() tester().enterOnKeyboard(amount) tester().tapSaveButton() // then tester().waitForTenantBalanceScreen(tenantName, currentBalance: paymentAmount) tester().waitForLastRentPaymentLine(today, amount: paymentAmount)

} } … public extension KIFUITestActor { /// Returns last payment line in rental unit dashboard @discardableResult func waitForLastPaymentLine(_ date: String, amount: String) -> UIView! { let view = waitForView("Last rent payment", value: "\(date), \(amount)”) // Accessibility label & accessibility value return view } }

FAST

NOT REALLY

MAKE UI TESTS FASTERclass MyTestCaseWithEmptyDatabase: KIFTestCase, UsesCoreDataDatabase { override func beforeAll() { UIApplication.shared.keyWindow!.layer.speed = 100; // faster animations } }

GOTCHAS & HINTSOCCASIONALLY CAN FAIL WITHOUT A GOOD REASON

¯\_( )_/¯

BEWARE OF UITableViewCellsADD EVERYTHING AS SUBVIEW TO .contentView

SAVE SCREENSHOTS ON TEST FAILURESET ENV VARIABLE WITH FOLDER LOCATION- KIF_SCREENSHOTS= …

AFTER FAILING TEST IN MODAL VIEW ALL OTHER TEST RESULTS = USELESS

do { try tester().trySomething(); // then test } catch {}

ACCESSIBILITYhttps://youtu.be/no12EfZUSQo

“You have no clue about your app accessibility until you write app UI tests

that rely on accessibility” / me to myself /

UIAccessibility PROTOCOLUIAccessibilityElement CLASS

UIAccessibilityContainer PROTOCOL UIAccessibilityTraits

BUNDLED FREE WHEN USING UIKit, JUST SET

accessibilityLabel, accessibilityValue, accessibilityHint IN IB AND/OR CODE

QUESTIONS?

Jurģis Ķiršakmens jki@jki.lv @jki / @xjki

top related