別用もあって、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なんかにも適用できるかなー。
| 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) | |
| } | |
| } |