Page History
Overview
In this tutorial, we will "create" some UI-based tests for an iOS application using XCTest testing framework along with XCUITest.
Requirements
- Xcode
- Xcode command line tools
xcode-select --install
- xcpretty
- (optional) fastlane and trainer plugin
Description
For this tutorial, we'll use a sample project composed of a calculator sample iOS app with unit UI tests by Ryan King Shashikant, with minor updates as tracked in this fork.
The iOS application is quite simple: it contains one interactor and one controller.has just a button and a text that appears whenever clicking on it.
The application has one interactor View Controller class.
Code Block | ||||
---|---|---|---|---|
| ||||
// // MARK: Frameworks import Foundation ViewController.swift // XCUITest101 // // MARK: CalculatorInteractor class CalculatorInteractor { func add(numberOne: Float, to numberTwo: Float) -> Float { return numberOne + numberTwo Created by Shashikant Jagtap on 24/09/2018. // Copyright © 2018 Shashikant Jagtap. All rights reserved. // import UIKit class ViewController: UIViewController { @IBOutlet weak var welcomeText: UILabel! @IBAction func enterPressed(_ sender: Any) { } func subtract(numberOne: Float, from numberTwo: Float) -> Float { welcomeText.text = "Welcome to XCUITest" welcomeText.isHidden return= numberTwofalse - numberOne } func multiply(numberOne: Float, by numberTwo:} Float) -> Float { override func viewDidLoad() { return numberOne * numberTwosuper.viewDidLoad() } welcomeText.isHidden = true func divide(numberOne: Float, by numberTwo: Float) ->// FloatDo { any additional setup after loading the view, typically returnfrom numberOne / numberTwoa nib. } } |
The project contains two tests for the interactorimplemented in Swift, along with two dummy, empty unit tests implemented in a different class: one named "testRecorded" (which is not an actual test case) and another, the real one, named "testRefactored".
Tests use XCTest framework and thus extend XCTestCase. The following class validates the arithmetic operationsXCUITest to load the application and execute the UI-baed tests.
Code Block | ||||
---|---|---|---|---|
| ||||
// MARK: Frameworks import XCTest // MARK: CalculatorInteractorTests class CalculatorInteractorTests: XCTestCase { XCUITest101UITests.swift // XCUITest101UITests // // MARK: Variables Created by Shashikant Jagtap var calculatorInteractor: CalculatorInteractor! on 24/09/2018. // MARK: SetupCopyright Methods © 2018 Shashikant Jagtap. overrideAll func setUp() { super.setUp() calculatorInteractor = CalculatorInteractor() } // MARK: Addition Tests func testAddition() { let numberOne: Float = 4 let numberTwo: Float = 9rights reserved. // import XCTest class XCUITest101UITests: XCTestCase { override let result = calculatorInteractor.add(numberOne: numberOne, to: numberTwo) XCTAssertEqual(result, 13)func setUp() { } func testAdditionNegativesuper.setUp() { letcontinueAfterFailure numberOne: Float = -3false let numberTwo: Float = -6 let result = calculatorInteractor.add(numberOne: numberOne, to: numberTwo) XCTAssertEqual(result, -9XCUIApplication().launch() } // MARK: Subtraction Tests func testSubtractionoverride func tearDown() { let numberOne: Float = 9 let numberTwo: Float = 4 let result = calculatorInteractor.subtract(numberOne: numberOne, from: numberTwosuper.tearDown() XCTAssertEqual(result, -5) } func testSubtractionNegativetestRecorded() { let// numberOne:this Floatis =not -6 an let numberTwo: Float = -12actual test... let resultapp = calculatorInteractor.subtract(numberOne: numberOne, from: numberTwoXCUIApplication() XCTAssertEqual(result, -6) } // MARK: Multiplication Tests func testMultiplication() { let numberOne: Float = 9 let numberTwo: Float = 4 let result = calculatorInteractor.multiply(numberOne: numberOne, by: numberTwoapp.otherElements.containing(.image, identifier:"wall1").element.tap() XCTAssertEqual(result, 36app.buttons["enter"].tap() } func testMultiplicationNegative() { let numberOne: Float = -2 let numberTwo: Float = -12 let result = calculatorInteractor.multiply(numberOne: numberOne, by: numberTwoapp.staticTexts["Welcome to XCUITest"].tap() XCTAssertEqual(result, 24) } // MARK: Division Tests func testDivisiontestRefactored() { let numberOne:app Float = 28 let numberTwo: Float = 2 let result = calculatorInteractor.divide(numberOne: numberOne, by: numberTwo= XCUIApplication() XCTAssertEqual(result, 14app.buttons["enter"].tap() } func testDivisionNegative() { let numberOne: Float = -9 let numberTwo: Float = -3 let result = calculatorInteractor.divide(numberOne: numberOne, by: numberTwoXCTAssert(app.staticTexts["Welcome to XCUITest"].exists) } XCTAssertEqual(result, 3) } } |
In order to run the tests from the command line or during CI, you can use xcodebuild
. By processing its output using xcpretty
, a JUnit XML can be generated (build/reports/junit.xml
, by default).
xcodebuild -project UnitTest-CalculatorXCUITest101.xcodeproj/ -scheme UnitTestXCUITest101 -Calculator -destination 'platform=iOS Simulator,OS=13.1,name=iPhone 811 Pro Max' clean build test CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcpretty -r junit
...
Info |
---|
If you're using Xcode, then you can also use it to write and run your tests (either all of them or just a specific one). However, by default, this won't produce a JUnit XML report by itself which can, later on, be uploaded to Xray. |
...
A Test Execution will be created containing information about the executed scenarios.
Each test is mapped to a Generic Test in Jira, and the Generic Test Definition field contains the name of the class concatenated with the method name of the corresponding automated test.
The Execution Details of the Generic Test contains information about the "Test Suite" (as per JUnit format), which in this case corresponds to the fully-qualified name of the class holding the test.
Notes
You should be able to use fastlane (docs here) to build and run your tests by using the trainer plugin.
...
Code Block | ||||
---|---|---|---|---|
| ||||
default_platform(:ios) platform :ios do desc "Run tests" lane :test do scan(scheme: "UnitTest-CalculatorXCUITest101", output_types: "", fail_build: false) trainer(output_directory: "build/reports/") end end |
Notes
- xcpretty project seems to be not very active
- xcpretty has a known limitation that inhibits the processing of multiline error descriptions (only the header is imported as the log/output of the assertion)
References
- https://developer.apple.com/documentation/xctest
https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/testing_with_xcode/chapters/09-ui_testing.html - https://github.com/xcpretty/xcpretty
- https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/testing_with_xcode/chapters/08-automation.html#//apple_ref/doc/uid/TP40014132-CH7-SW3
- https://www.slideshare.net/ShankarAnamalla/ios-app-testing-with-xctest-and-xcuitest
- https://github.com/zanizrules/fastlane-plugin-xcresult_to_junit