別用もあって、XCUITestをちゃんと触ってみました。
XCUITestに対して多くのプロジェクトで問題になるのって、多分既存コードに組み込むことができるか?だと思うのですよね。(私もそうです)
なので、iOS6.0をdeploy targetにしているAppiumのサンプルコードを引っ張ってきて、それを7.0にあげた状態で動くか、というところから確認してみました。結果的に、アプリは7.0を保ちつつ、XCUITestはiOS9.0を対象にしてテストを回す、ということができました。Xcode8頃から怪しくなりそうな気がしているInstruments越しのUIAutomationを見ると、できるところからiOS9系だけでもXCUITestを視野に入れ始めたほうがよさそうですね。
修正したコードは以下に入れています。
git clone して、iOS9シミュレータをターゲットに設定して command + u で実行できます。
https://github.com/KazuCocoa/XCUITestExample/tree/master
やったことは、
- サンプルコードを取得する
- iOS7.0をミニマムにする
- XCUItestのターゲットを追加する
- 幾つかのテストコード書く(ついでに、accessibility付与したり、要素の結合したり)
accessibilityIdentifierやtitle、private methodへの切り出し
くらいです。
そのほか
Storyboardから、 accessibilityIdentifier を設定できるようになっていました。Storyboard中心になるのは、実装とUIを切り分けできているので良いのではないかなと思います。
ただ、動的に変化する要素に対してaccessibilityIdentifierを付与する、というのはやっぱりコード読み書き必要だし、テストコードはやっぱりSwift/Objective-Cになるだろうので、中身見ることができる人は必要、というのは変わらなくて良いのですけどね。
XCWaitCompletionHandler がwaitとしてつかえそうだったりと、Instruments越しのUIAutomationよりは、個人的に安定した並列実行とかもできるのでは?と少し期待。
これの成果物、Amazon Device Farmなんかにも適用できるかなー。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import XCTest | |
| class TestAppUITests: XCTestCase { | |
| private let app: XCUIApplication = XCUIApplication() | |
| override func setUp() { | |
| super.setUp() | |
| continueAfterFailure = false | |
| app.launch() | |
| } | |
| override func tearDown() { | |
| super.tearDown() | |
| } | |
| func testShouldDisplayAlertWithText(){ | |
| app.buttons["show alert"] | |
| .tap() | |
| app.alerts["Cool title"].collectionViews.buttons["OK"] | |
| .tap() | |
| XCTAssertFalse(app.alerts["Cool title"].exists) | |
| } | |
| func testShouldDisplayAlertWithAccessibility() { | |
| app.buttons.matchingIdentifier("accessibilityShowAlert").element | |
| .tap() | |
| app.alerts["Cool title"].collectionViews.buttons["OK"] | |
| .tap() | |
| XCTAssertFalse(app.alerts["Cool title"].exists) | |
| } | |
| func testShouldOpenGuesteView() { | |
| app.buttons["Test Gesture"] | |
| .tap() | |
| app.childrenMatchingType(.Window).elementBoundByIndex(0).childrenMatchingType(.Other).element.childrenMatchingType(.Other).element.childrenMatchingType(.Other).element.childrenMatchingType(.Other).element.childrenMatchingType(.Map).element.tap() | |
| XCTAssertNotNil(app.maps.matchingIdentifier("map view").element.exists, "don't display map view") | |
| } | |
| func testCulc() { | |
| tap("IntegerA", ele_type: "text") | |
| fillText("IntegerA", "1") | |
| tap("IntegerB", ele_type: "text") | |
| fillText("IntegerB", "1") | |
| tap("ComputeSumButton", ele_type: "button") | |
| XCTAssert(app.staticTexts["2"].exists) | |
| } | |
| private func tap(title: String, ele_type type : String){ | |
| switch type { | |
| case "text": | |
| app.textFields[title].tap() | |
| case "button": | |
| app.buttons[title].tap() | |
| default: | |
| app.textFields[title].tap() | |
| } | |
| } | |
| private func fillText(title: String, _ body: String){ | |
| app.textFields[title].typeText(body) | |
| } | |
| } |