Swift
Quick
EarlGrey

EarlGreyのコードにQuickのコードを付け合わせて可読性を上げる

例えばこんなUITestコードがある場合、若干見にくいかと思います

import XCTest
import EarlGrey

@testable import QiitaPresents

class QiitaPresentsTests: XCTest {


  func testButton() {
    EarlGrey.select(elementWithMatcher: grey_kindOfClass(UIButton.self)).perform(grey_tap())

    EarlGrey.select(elementWithMatcher: grey_kindOfClass(UIButton.self)).assert(grey_selected())

    var success: Bool = GREYCondition(name: "値が変わるまで5秒間待つ") { () -> Bool in
      var error: NSError?

      EarlGrey.select(elementWithMatcher: grey_accessibilityID("resultLabel")).assert(grey_text("Hoge"), error: &error)

      return error == nil
      }.wait(withTimeout: 5)

    GREYAssertTrue(success, reason: "Hogeに変わらなかった、、、、")

    EarlGrey.select(elementWithMatcher: grey_kindOfClass(UIButton.self)).perform(grey_tap())

    EarlGrey.select(elementWithMatcher: grey_kindOfClass(UIButton.self)).assert(grey_not(grey_selected()))

    success = GREYCondition(name: "値が変わるまで5秒間待つ") { () -> Bool in
      var error: NSError?

      EarlGrey.select(elementWithMatcher: grey_accessibilityID("resultLabel")).assert(grey_text("Fuga"), error: &error)

      return error == nil
      }.wait(withTimeout: 5)

    GREYAssertTrue(success, reason: "Fugaに変わらなかった、、、、")

  }
}


こんな時はEarlGreyとQuickを併用して使うようにしましょう。

Quickのdescribe/context/itを使おう!

先ずは、describe/context/itを使いましょう。
そうするとかなり可読性が上がると思います

各Quickメソッドの引数(String型)は以下のように書きます。

describe: どんなテストか
context : どんな条件のテストをするのか
it : 期待する動作は何か

import EarlGrey
import Quick

@testable import QiitaPresents

class QiitaPresentsTests: QuickSpec {

  override func spec() {
    describe("画面テスト") {
      context("メイン画面上") {
        it("Hogeが表示されるか") {
          EarlGrey.select(elementWithMatcher: grey_kindOfClass(UIButton.self)).perform(grey_tap())

          EarlGrey.select(elementWithMatcher: grey_kindOfClass(UIButton.self)).assert(grey_selected())

          let success: Bool = GREYCondition(name: "値が変わるまで5秒間待つ") { () -> Bool in
            var error: NSError?

            EarlGrey.select(elementWithMatcher: grey_accessibilityID("resultLabel")).assert(grey_text("Hoge"), error: &error)

            return error == nil
          }.wait(withTimeout: 5)

          GREYAssertTrue(success, reason: "値が変わらなかった、、、、")
        }

        it("Fugaが表示されるか") {
          EarlGrey.select(elementWithMatcher: grey_kindOfClass(UIButton.self)).perform(grey_tap())

          EarlGrey.select(elementWithMatcher: grey_kindOfClass(UIButton.self)).assert(grey_not(grey_selected()))

          let success = GREYCondition(name: "値が変わるまで5秒間待つ") { () -> Bool in
            var error: NSError?

            EarlGrey.select(elementWithMatcher: grey_accessibilityID("resultLabel")).assert(grey_text("Fuga"), error: &error)

            return error == nil
          }.wait(withTimeout: 5)

          GREYAssertTrue(success, reason: "値が変わらなかった、、、、")
        }
      }
    }
  }
}

beforeEach/afterEachを使ってテストの前後で確認したい事を書こう!

Quickには各itテストの始まりの前と終わりの後に処理を挟むことができます。
beforeEach: itテストの前に処理を行う
afterEach : itテストの後に処理を行う

import EarlGrey
import Quick

@testable import QiitaPresents

class QiitaPresentsTests: QuickSpec {

  override func spec() {
    describe("画面テスト") {

      beforeEach {
        /// ラベルが表示されているか確認
        EarlGrey.select(elementWithMatcher: grey_accessibilityID("resultLabel")).assert(grey_sufficientlyVisible())
      }

      afterEach {
        /// ラベルが表示されているか確認
        EarlGrey.select(elementWithMatcher: grey_accessibilityID("resultLabel")).assert(grey_sufficientlyVisible())
      }
      context("メイン画面") {
        it("Hogeが表示されるか") {
          EarlGrey.select(elementWithMatcher: grey_kindOfClass(UIButton.self)).perform(grey_tap())

          EarlGrey.select(elementWithMatcher: grey_kindOfClass(UIButton.self)).assert(grey_selected())

          let success: Bool = GREYCondition(name: "値が変わるまで5秒間待つ") { () -> Bool in
            var error: NSError?

            EarlGrey.select(elementWithMatcher: grey_accessibilityID("resultLabel")).assert(grey_text("Hoge"), error: &error)

            return error == nil
            }.wait(withTimeout: 5)

          GREYAssertTrue(success, reason: "値が変わらなかった、、、、")
        }

        it("Fugaが表示されるか") {
          EarlGrey.select(elementWithMatcher: grey_kindOfClass(UIButton.self)).perform(grey_tap())

          EarlGrey.select(elementWithMatcher: grey_kindOfClass(UIButton.self)).assert(grey_not(grey_selected()))

          let success = GREYCondition(name: "値が変わるまで5秒間待つ") { () -> Bool in
            var error: NSError?

            EarlGrey.select(elementWithMatcher: grey_accessibilityID("resultLabel")).assert(grey_text("Fuga"), error: &error)

            return error == nil
            }.wait(withTimeout: 5)

          GREYAssertTrue(success, reason: "値が変わらなかった、、、、")
        }
      }
    }
  }
}

今回は、grey_sufficientlyVisible()を使っていますが(アホみたいな例です。すいません)、お好きな処理を記述してください。

beforeSuite/afterSuiteを使ってテストの最初と最後に確認を行う。

beforeEach/afterEachとはまた別に、テストの最初と最後に処理を挟むことができます。
beforeSuite: テストを行う前に処理を行う
afterSuite : テスト行った後に処理を行う

import EarlGrey
import Quick

@testable import QiitaPresents

class QiitaPresentsTests: QuickSpec {

  override func spec() {
    describe("画面テスト") {

      beforeSuite {
        /// テスト前は"Fuga"か
        EarlGrey.select(elementWithMatcher: grey_accessibilityID("resultLabel")).assert(grey_text("Fuga"))
      }

      afterSuite {
        /// テスト後は"Fuga"か
        EarlGrey.select(elementWithMatcher: grey_accessibilityID("resultLabel")).assert(grey_text("Fuga"))
      }

      beforeEach {
        EarlGrey.select(elementWithMatcher: grey_accessibilityID("resultLabel")).assert(grey_sufficientlyVisible())
      }

      afterEach {
        EarlGrey.select(elementWithMatcher: grey_accessibilityID("resultLabel")).assert(grey_sufficientlyVisible())
      }

      context("メイン画面") {
        it("Hogeが表示されるか") {
          EarlGrey.select(elementWithMatcher: grey_kindOfClass(UIButton.self)).perform(grey_tap())

          EarlGrey.select(elementWithMatcher: grey_kindOfClass(UIButton.self)).assert(grey_selected())

          let success: Bool = GREYCondition(name: "値が変わるまで5秒間待つ") { () -> Bool in
            var error: NSError?

            EarlGrey.select(elementWithMatcher: grey_accessibilityID("resultLabel")).assert(grey_text("Hoge"), error: &error)

            return error == nil
            }.wait(withTimeout: 5)

          GREYAssertTrue(success, reason: "値が変わらなかった、、、、")
        }

        it("Fugaが表示されるか") {
          EarlGrey.select(elementWithMatcher: grey_kindOfClass(UIButton.self)).perform(grey_tap())

          EarlGrey.select(elementWithMatcher: grey_kindOfClass(UIButton.self)).assert(grey_not(grey_selected()))

          let success = GREYCondition(name: "値が変わるまで5秒間待つ") { () -> Bool in
            var error: NSError?

            EarlGrey.select(elementWithMatcher: grey_accessibilityID("resultLabel")).assert(grey_text("Fuga"), error: &error)

            return error == nil
            }.wait(withTimeout: 5)

          GREYAssertTrue(success, reason: "値が変わらなかった、、、、")
        }
      }
    }
  }
}

これを使えばUITest前にAPIを叩いて、画面の準備を行うことができるかと思います。

お願い

間違い箇所などがある場合は、指摘していただけると嬉しいです

最後に

いかがでしたでしょうか。
Quickはテストをするタイミングをコントールできるので、EarlGreyと併用すると楽にテストコードが書けるかと思います。
よかったら試してみてください。

※一応、クソースコドを載せておきます。

import UIKit

class ViewController: UIViewController {

  @IBOutlet weak var resultLabel: UILabel!
  @IBOutlet weak var button: UIButton!

  override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    resultLabel.accessibilityIdentifier = "resultLabel"
  }

  override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
  }
  @IBAction func didPushButtoon(_ sender: Any) {

    button.isSelected = !button.isSelected

    resultLabel.text = button.isSelected ? "Hoge" : "Fuga"
  }
}

おまけ

もし、afterSuite/beforeSuiteが使えなかった場合は以下のメソッドを変わりに書きましょう。

class QiitaPresentsTests: QuickSpec {

   /// テストの前に処理を行う
  class func setUp() {
    /// 処理
  }

  /// テストの後に処理を行う
  class func tearDown() {
    /// 処理
  }
}

代わりに使えるかと思います。