This document discusses using Gherkin with XCUITest for iOS automation testing. It begins with an introduction to XCUITest, including recording tests, interacting with elements, and refactoring code. It then demonstrates building a simple test suite for a weather app. Finally, it shows how to implement Gherkin feature files to specify tests in a human-readable format, including steps to set up tests and make assertions. Gherkin allows for acceptance test driven development and collaboration across teams.
4. XCUITest
• Introduced in Xcode 7 in 2015
• xUnit Test Cases (XCTestCase)
• UITest targets cant see production codes
• Builds and tests are all executed using XCode
(ObjC/Swift)
• Record functionality
1. Introduction (iOS Automation Tools, XCUITest)
6. XCUITest - XCUIElement
1. Introduction (iOS Automation Tools, XCUITest)
XCUIElement
//Elements are objects encapsulating the information needed to dynamically locate a user interface.element in an
application. Elements are described in terms of queries [Apple Documentation - XCTest]
let app = XCUIApplication() //Object that queries views on the app
app.staticTexts //returns XCUIElementQuery “collection” representing “UILabels”
app.buttons //returns XCUIElementQuery “collection” of representing “UIButtons”
app.tables //returns XCUIElementQuery “collection” of representing “UITables”
app.tables.staticTexts
//returns XCUIElementQuery “collection” representing “UILabels” which has superviews of type “UITable”
app.staticTexts[“Hello World”]
//returns a label element with accessibilityIdentifier or text value “Hello World”
app.tables[“myTable”]
//returns a table element with accessibilityIdentifier “myTable”
8. XCUITest - Interactions
1. Introduction (iOS Automation Tools, XCUITest)
let app = XCUIApplication() //Object that queries views on the app
app.staticTexts[“Hello World”].exists //returns true if element exists
app.buttons[“Save”].tap() //taps the “Save” button
app.tables[“myTable”].swipeUp() //swipes up the table
app.textFields[“myField”].typeText(“John Doe”) //types value in textField
Other interactions: pinchWithScale, pressForDuration, doubleTap()
XCTAssertTrue(app.staticTexts[“Hello World”].exists) //Throws exception if label does not exists
9. Requirements
2. Building a simple test suite
Create a Weather App
1. Given I am at the City Search Screen
When I search for a valid city (eg “London”)
Then I should see a weather details page of that city
2. Given I am at the City Search Screen
When I search for an invalid city (eg “NotACity”)
Then I should see an error message
14. Generated Code - Valid City
2. Building a simple test suite
func testUserAbleToSearchForValidCity() {
let app = app2
app.searchFields["Search a city"].tap()
app.searchFields["Search a city"]
let app2 = app
app2.buttons["Search"].tap()
app.searchFields["Search a city"]
let tablesQuery = app2.tables
tablesQuery.staticTexts["London"].tap()
tablesQuery.staticTexts["53"].tap()
tablesQuery.staticTexts["Partly Cloudy"].tap()
}
15. Generated Code - Invalid City
2. Building a simple test suite
func testUserSeesErrorMessageForInvalidCity() {
let app = XCUIApplciation()
app.searchFields["Search a city"].tap()
app.searchFields["Search a city"]
app.buttons["Search"].tap()
app.searchFields["Search a city"]
let errorAlert = app.alerts["Error"]
errorAlert.staticTexts["Error"].tap()
errorAlert.staticTexts[
"Unable to find any matching weather location to the query submitted!”
].tap()
errorAlert.collectionViews["OK"].tap()
}
16. 2. Building a simple test suite
func testUserAbleToSearchForValidCity() {
let app = XCUIApplication()
let searchField = app.searchFields["Search a city"]
searchField.tap()
searchField.typeText("London")
app.buttons["Search"].tap()
self.userWaitsToSeeText("London")
self.userWaitsToSeeText("53")
self.userWaitsToSeeText("Partly Cloudy”)
self.waitForExpectationsWithTimeout(5, handler: nil)
}
private func userWaitsToSeeText(text: String){
self.expectationForPredicate(
NSPredicate(format: "exists == 1"),
evaluatedWithObject: XCUIApplication().tables.staticTexts[text],
handler: nil
)
// XCUIApplication().tables.staticTexts[text].exists <— boolean
}
Refactored Code
17. 2. Building a simple test suite
func testUserSeesErrorMessageForInvalidCity() {
let app = XCUIApplication()
let searchField = app.searchFields["Search a city”]
searchField.tap()
searchField.typeText("NotACity")
app.buttons["Search"].tap()
self.userWaitsToSeeText("Error")
self.userWaitsToSeeText(
"Unable to find any matching weather location to the query submitted!"
)
self.waitForExpectationsWithTimeout(5, handler: nil)
self.userTapsOnAlertButton("OK")
}
private func userTapsOnAlertButton(buttonTitle: String){
XCUIApplication().buttons[buttonTitle].tap()
}
Refactored Code
21. (User Registration)
Given I am at the user registration page
When I enter invalid email address
And I tap “Register“
Then I should see “Invalid Email Address”
Gherkin - Example 1
3. Use Gherkins
22. (Shopping with existing Items in shopping cart)
Given I have 1 Wallet and 1 Belt in my shopping cart
And I am at the Shopping Item Details Page for a Bag
When I select quantity as “1”
And I tap on “Add to Shopping Cart”
Then I should be at Shopping Cart Screen
And I should see “3” total items in my shopping cart
Gherkin - Example 2
3. Use Gherkins
23. 2. Building a simple test suite
Given I am at Weather Search Form Page
When I enter city search for “London”
Then I should be at Weather Details Page
And I wait to see “London”
And I wait to see “53”
Gherkin - Our Acceptance Tests
24. 2. Building a simple test suite
Given I am at Weather Search Form Page
When I enter city search for “NotACity”
Then I wait to see “Error”
And I wait to see “Unable to …”
And I tap on alert button “OK”
Gherkin - Our Acceptance Tests
25. 3. Use Gherkins
• Language used in Behavioural Driven Development (BDD) to
specify software requirements with examples
• Executable, facilitate collaboration with non developers
• Lets add Gherkin to our project in Xcode
https://github.com/net-a-porter-mobile/XCTest-Gherkin
pod 'XCTest-Gherkin'
Gherkin
27. 3. Use Gherkins
Given I am at Weather Search Form Page
When I enter city search for “London”
Then I should be at Weather Details Page
And I wait to see “London”
And I wait to see “53”
step("I (am|should be) at Weather Search Form Page”) {
let weatherSearchPage = WeatherSearchPage(self.test)
}
struct WeatherSearchPage{
init(testCase: XCTestCase){
testCase.expectationForPredicate(
NSPredicate(format: “exists == 1”),
evaluatedWithObject: XCUIApplication().otherElements[“WeatherSearchPage”],
handler: nil
)
testCase.waitForExpectationsWithTimeout(5, handler: nil)
}
}
28. 3. Use Gherkins
Given I am at Weather Search Form Screen
When I enter city search for “London”
Then I should be at Weather Details Page
And I wait to see “London”
And I wait to see “53”
step("I enter city search for ”(.*?)””) { (matches: [String]) in
let weatherSearchPage = WeatherSearchPage(testCase: self.test)
weatherSearchPage.userSearchForCity(matches.first!)
}
struct WeatherSearchPage{
func userSearchForCity(city: String){
let app = XCUIApplication()
let searchField = app.searchFields[“Search a city”]
searchField.tap()
searchField.typeText(city)
app.buttons[“Search”].tap()
}
}
29. 3. Use Gherkins
Given I am at Weather Search Form Page
When I enter city search for “London”
Then I should be at Weather Details Page
And I wait to see “London”
And I wait to see “53”
step("I (am|should be) at Weather Details Page”) {
WeatherDetailsPage(testCase: self.test)
}
struct WeatherDetailsPage{
init(testCase: XCTestCase){
testCase.expectationForPredicate(
NSPredicate(format: “exists == 1”),
evaluatedWithObject: XCUIApplication().otherElements[“Weather Forecast”],
handler: nil
)
testCase.waitForExpectationsWithTimeout(5, handler: nil)
}
}
30. 3. Use Gherkins
Given I am at Weather Search Form Page
When I enter city search for “London”
Then I should be at Weather Details Page
And I wait to see “London”
And I wait to see “53”
step("I wait to see ”(.*?)””) { (matches: [String]) in
self.test.expectationForPredicate(
NSPredicate(format: “exists == 1”),
evaluatedWithObject: XCUIApplication().staticTexts[matches.first!],
handler: nil
)
self.test.waitForExpectationsWithTimeout(5, handler: nil)
}
31. 3. Use Gherkins
Given I am at Weather Search Form Page
When I enter city search for “NotACity”
Then I wait to see “Error”
Then I wait to see “Unable to …”
Then I tap on alert button “OK”
step("I tap on alert button ”(.*?)””) {
XCUIApplication().buttons[matches.first!].tap()
}
32. 3. Use Gherkins
Using an additional pod to simply statements -
https://github.com/joemasilotti/JAMTestHelper
Gherkin
33. 3. Use Gherkins
Using an additional pod to simply statements -
https://github.com/joemasilotti/JAMTestHelper
Gherkin
40. Why should I use Gherkin with
XCUITest?
3. Use Gherkins
• Acceptance Test Driven Development
• Cross platform testing is still possible without need for 3rd
party tools
• Objective C + Swift
The codes mentioned in the later slides can be found here
show of hands: How Many people in the room are testers, developers, non-technical
How many developers write Unit Tests? UI Automated Tests?
Based on a couple of requirements, we will attempt to write XCUITest cases.
Introducing the Gherkin Language and how we can bring this tool to XCUITest
Wanted to share a segment on setting up fixtures for XCUITest
Out of the box test automation Tool introduced in Xcode7. the way you would write tests is by querying for elements/subviews on the main app window
XCUITest uses the XCTest framework which generally xUnitTest patterns. (abt testCases, setup/teardown, assertion
when we run XCUITest, we build/install testTargetApp + testRunner. TestsR will launch+query elements on screen (blackbox)
No Mocking
Helpful for developers to discover how to use the api
Look at the codes… explain
you’ll mainly be working with XCUIElement
you can also use “other elements’ to return a collection of anything that is subclass of UIView
Now that you have some idea of what the api can do, lets go ahead and write our first XCUITest case!
show the plus sign before running
We will record the test case for the first requirement for Valid city
One importantly tip here, you may want to click on the elements during recordings as Xcode will help generate the statements we may need to use later
Explain code
As u can see, Xcode may not always generate the desired codes.
For this case, the codes for typing the search string is missing
As you can see here…. we simply filled in the blanks for the missing/incorrect codes… and we organised them little bit
You can use Scheme to manage your tests.
This allows you to build tests against just 1 test target and use different scheme to decide which tests you want to run.
Smoke vs Regression. Local vs Running in a particular env.
of course, changing env requires you to change api target in your app
If you want to change which env you want your app to be run in, you can use scheme to modify your config
- ** remember to encrypt your config file before you package your .app / .ipa file
If you don't know what’s Gherkin, this is an example of a feature file written in Gherkin
Gherkin is a human readable syntax use to construct acceptance test cases. BDD
Keywords: Feature, Scenario, (below scenario are steps) Given, When Then
Today, focus on how to use Given When Then
- Btw, this is taken from our iOS offline interview assignment where we ask candidates to code, and the interview panel conducts very thorough code review… Look at how you architect/decouple codes, name variables, any abuse of coding structures/design patterns, how you write tests, whether you have proper code coverage
- Btw, this is taken from our iOS offline interview assignment where we ask candidates to code, and the interview panel conducts very thorough code review… Look at how you architect/decouple codes, name variables, any abuse of coding structures/design patterns, how you write tests, whether you have proper code coverage
In our test case file, specify the Given When Then in a test method
create a subclass of StepDefiner that evaluates the GWT steps definition
Specify steps without using “GWT”
specify “am” or “should be” … so that i can reuse it in Given or Then
Where do i put this string? i can go to VC.viewDidLoad
ATTD: Work with QA, PO. They can fill up features. 1st specify features, hook it into CI and get it to pass. in PG we asked PO to write and commit the feature before we start any work
android/ios dev… u only need feature files to bind same requirements together
No need to learn other programming language required by any 3rd party tool
I hope you guys gotten a good picture on how to use XCUITest and Gherkin.
If any of you guys need help or tips to kickstart UITest+Gherkin in your personal or company work, feel free to chat with me later during the break or buzz me via my emails