UITestについて


今更ながら、Xcode7のUITestを触ってみたのでそれについて書きました。
今後進化していくと思うので、随時書き足していこうと思っています。

###参考
http://dev.classmethod.jp/smartphone/iphone/xcode-7-ui-testing/
→簡潔でわかりやすいです

https://www.bignerdranch.com/blog/ui-testing-in-xcode-7-part-1-ui-testing-gotchas/
→細かいところも書いてある

https://developer.apple.com/library/ios/documentation/DeveloperTools/Conceptual/testing_with_xcode/chapters/01-introduction.html
→公式だが、UITestについてはあまり書いてない

https://developer.apple.com/videos/play/wwdc2015/406/
→こちらのWWDC2015のPDFに（UITestで利用するクラスについて）詳しく書かれている

#必要な環境

* Xcode7以上
* iOS9以上の実機または、Simulator


#UITestプロジェクト追加
File -> New -> Target... -> iOSのTest -> iOS UI Testing Bundle を選択して Next -> UITest Targetの設定を必要に応じて変更(多分、普通は変更しない)してFinish。


![UITestプロジェクト追加](https://bytebucket.org/hikarusatodev/xctestsample/raw/08ad3707600794f03e0da67a735e57723fcf6f8e/images/uitest_add_target.gif)



#UITestする操作の記録
下の方のRecord UI Testボタン[赤いボタン]をクリックすると、テスト対象のアプリが起動し、テストするUI操作のコードを記録できる。

![UITestする操作の記録](https://bytebucket.org/hikarusatodev/xctestsample/raw/3b11a1c10f69e58a059029248406fb83b9225ee2/images/uitest_preparation.gif)

* UI操作のコードを記録できるのはお手軽。
* もちろん、コードでも書ける。
* XCUIApplicationインスタンス.buttons[ボタンtext]で、現在表示中の画面から対象ボタンのXCUIElementQueryオブジェクトを取得できて、それに対して何か操作(tapしたり、text取得したり)するような感じ。
<br/>UIによって、XCUIApplicationインスタンス.webViews やXCUIApplicationインスタンス.maps など、いろいろ用意されている。

#UITest 実行
テストコードを書いて、NavigationBarの<->（右から４番目）クリックして開いて、対象のテストクラスのTest実行。

![UITest 実行](https://bytebucket.org/hikarusatodev/xctestsample/raw/1444d0fb9de814775415865bd4b90b4fb1bbbcdb/images/uitest_executation.gif)


```swift:テストコード
class XCTestSampleUITests: XCTestCase {
    override func setUp() {
        super.setUp()
        
        // Put setup code here. This method is called before the invocation of each test method in the class.
        
        // In UI tests it is usually best to stop immediately when a failure occurs.
        continueAfterFailure = false
        // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method.
        XCUIApplication().launch()
        

        // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
    }
    
    override func tearDown() {
        // Put teardown code here. This method is called after the invocation of each test method in the class.
        super.tearDown()
    }
    
    func testExample() {
        // Use recording to get started writing UI tests.
        // Use XCTAssert and related functions to verify your tests produce the correct results.
        let expectation = self.expectationWithDescription("test")
        let app = XCUIApplication()
        //テーブルビューの"2016-03-10 14:32:18 +0000"が表示されているセルをタップ
        app.tables.staticTexts["2016-03-22 13:34:34 +0000"].tap()
        //starボタンをタップ
        app.buttons["star"].tap()
        var delay = 1.0 * Double(NSEC_PER_SEC)
		var time  = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
		dispatch_after(time, dispatch_get_main_queue(), {
            //label.textに"Twinkle stars!"が表示されているか確認
            XCTAssert(app.staticTexts["Twinkle stars!"].exists, "failed to tap star button.")
            //loveボタンをタップ
            app.buttons["love"].tap()
		})
        
        
        delay = 2.0 * Double(NSEC_PER_SEC)
        time  = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
        dispatch_after(time, dispatch_get_main_queue(), {
            //label.textに"Many hearts!"が表示されているか確認
            XCTAssert(app.staticTexts["Many hearts!"].exists, "failed to tap love button.")
            expectation.fulfill()
        })
        
        
        self.waitForExpectationsWithTimeout(5.0) { (error) -> Void in
            if let err = error {
                XCTFail(err.description)
            }
            
        }
}
```

上記はテストメソッドがtestExample()の１つだけだが、２つ以上のテストメソッド(メソッド名:test〜のメソッド)がある場合はテストメソッドの数だけ、
Setup(),test〜1つ目のテストメソッド(),tearDown()
Setup(),test〜2つ目のテストメソッド(),tearDown()
・
・
・
のようにTestが実行されていく（これはXCTestの仕様だが）。



#バグ（と思われる現象）

* UITestsクラスに２つ以上のテストメソッドがあると、２つ目以降のメソッド実行時のSetup()内のXCUIApplication().launch()で下記のようなエラーが発生。

```
UI Test Activity: 
Assertion Failure: UI Testing Failure - App state for <XCUIApplicationProcess: 0x156e65870 (null) (4528)> is XCApplicationStateRunningActive (3), still not XCApplicationStateNotRunning (1)
```
→tearDown()でXCUIApplication().terminate()（アプリの終了）をしても解消できない。XCode7.3 beta5ではこの現象は解消されていた。

関連：https://forums.developer.apple.com/thread/28732
<br/>

* 絵文字のUIだと、Record UI Testボタンでうまく操作記録できない。UI操作用コードが文字化けする。

![UITest 実行](https://bytebucket.org/hikarusatodev/xctestsample/raw/3b11a1c10f69e58a059029248406fb83b9225ee2/images/uitest_simulator.gif)

<br/>


#まとめ

* 便利だがまだ改善の余地がありそう。
* 私自身もっとUITestの理解を深めて記事に追記していこうと思う。