🐙

Xcode Testを使おう

2022/08/03に公開

まずはプロジェクトを作成します
「Include Tests」のチェックボックスを入れておきます

テストの下準備として、「PlayData.swift」というSwiftファイルを作成します

PlayData.swift
import Foundation

class PlayData {
	var allWords = [String]()
}

PlayData.swiftの置き場所は本番プロジェクトフォルダの中です

次は「プロジェクト名Tests.swift」というファイルを開きます
(このプロジェクトの名前はUnitTestingなので、UnitTestingTests.swiftというファイルになります)

サンプルテストのtestExample()testPerformanceExample()を削除します

UnitTestingTests.swift
import XCTest
@testable import UnitTesting

class UnitTestingTests: XCTestCase {

	override func setUpWithError() throws {
	}

	override func tearDownWithError() throws {
	}

}

そして、オリジナルのテストを加えます
このメソッドは、playData.allWordsは空き配列ではないかを検証します
現在は空き配列なので、検証は通過できると予想されます
(もし、空き配列ではない場合、検証は失敗になり、「allWords must be 0」というメッセージが出ます)

UnitTestingTests.swift
class UnitTestingTests: XCTestCase {
	// ... 省略
	func testAllWordsLoaded() {
		let playData = PlayData()
		XCTAssertEqual(playData.allWords.count, 0, "allWords must be 0")
	}
}

テストメソッドを書くときの注意点は

  • テストメソッドの名前は「test〜」です、testというPrefixをつけることが必要です
  • メソッドは、引数を指定してはいけない、ノー引数は必須です
  • メソッドは、返り値をするのがいけない、返り値なしのが必須です

以上の3つを守ったら、テストメソッドは正確に識別でき、コードの左に灰色の菱形アイコンが出ます

XCTAssertEqual()の他には、XCTAssertGreaterThan()XCTAssertNotNil()などが使えます

そして、テストを行います
class UnitTestingTestsの左の菱形のアイコンをクリックします

テストは通過できます、菱形のアイコンが緑になりました

Performance Test

Performance Testは、10回コードを運行して、そして平均値の所要時間を表示します

UnitTestingTests.swiftに以下のコードを追加します

UnitTestingTests.swift
class UnitTestingTests: XCTestCase {
	...
+	func testWordsLoadQuickly() {
+		measure {
+			_ = PlayData()
+		}
+	}
}

func testWordsLoadQuickly()の左の菱形アイコンをクリック、テストを起動します

このように、平均時間は0.212secとなっております

緑の菱形アイコンの下のアイコンをクリックしたら
Performance Testの10回の詳細を見ることができます

Baselineを設定

Baselineは、今回のテストの結果を基準として設定
そして、以後のテストのかかる時間が良くなるか、悪くなるかをチェックすることができます

緑の菱形アイコンの下のアイコンをクリック、「Set Baseline」をクリックします

そして、もう一度緑の菱形アイコンをクリック、
もう一度テストを起動します

テスト完了後、所要時間の右には2% betterというテキストが出ました
さらに、前回はBaseline設定したので、今回の緑の菱形アイコンは
「所要時間はBaselineの±10%以内」ということを示しております

UI Test

Data Modelのテストと違い、UIが正常に作動しているかを確認するためにUIテストを書きましょう

「プロジェクト名UITests.swift」というファイルを開きます

testExample()testLaunchPerformance()削除しておきます

続いて、自分のUIテストを書きます
テーブルが7つ表示しているかのテストです

UnitTestingUITests.swift
class UnitTestingUITests: XCTestCase {
	...
+	func testInitialStateIsCorrect() {
+		let app = XCUIApplication()
+		app.launch()
+		let table = XCUIApplication().tables
+		XCTAssertEqual(table.cells.count, 7, "There should be 7 rows initially")
+	}
}

レコードしてUI Testコードを生成

上のようにUI Testのコードを書くのは一つの方法で、
そこでもう一つの手段としてのレコードでUI Testコードを生成させる方法を紹介します

まずはUnitTestingUITests.swiftに空きテストメソッドを書きます

UnitTestingUITests.swift
class UnitTestingUITests: XCTestCase {
	...
+	func testUserFilteringByString() {
+	// マウスはここにクリックしておく
+	}
}

そして、マウスはコメントの部分にクリックしておきます

次は、画面の下の赤いアイコンをクリックします

シミュレーターが起動され、操作を行います

操作を終えて、Xcodeに戻りまたアイコンをクリックします
これでレコードを終了させます

testUserFilteringByString()の中にコードが生成されることが確認できます

UnitTestingUITests.swift
func testUserFilteringByString() {
	let app = XCUIApplication()
	app.navigationBars["Unit Testing"].buttons["Search"].tap()

	let tKey = app/*@START_MENU_TOKEN@*/.keys["t"]/*[[".keyboards.keys[\"t\"]",".keys[\"t\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/
	tKey.tap()
	tKey.tap()

	let eKey = app/*@START_MENU_TOKEN@*/.keys["e"]/*[[".keyboards.keys[\"e\"]",".keys[\"e\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/
	eKey.tap()
	eKey.tap()

	let sKey = app/*@START_MENU_TOKEN@*/.keys["s"]/*[[".keyboards.keys[\"s\"]",".keys[\"s\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/
	sKey.tap()
	sKey.tap()
	tKey.tap()
	tKey.tap()
	app.alerts["Filter..."].scrollViews.otherElements.buttons["Filter"].tap()
    }

生成されたコードなので色々改善できる部分があります
こちらは改善されたバージョンです

UnitTestingUITests.swift
func testUserFilteringByString() {
	let app = XCUIApplication()
	app.buttons["Search"].tap()

	let filterAlert = app.alerts
	let textField = filterAlert.textFields.element
	textField.typeText("test")

	filterAlert.buttons["Filter"].tap()
}

最後に、自らAssertコードを入れます
「Test」という単語でフィルターしたら、テーブルの行数は56になるはずなのでこう書きます

UnitTestingUITests.swift
func testUserFilteringByString() {
	let app = XCUIApplication()
	app.buttons["Search"].tap()

	let filterAlert = app.alerts
	let textField = filterAlert.textFields.element
	textField.typeText("test")

	filterAlert.buttons["Filter"].tap()
	
+	XCTAssertEqual(app.tables.cells.count, 56, "There should be 56 words matching 'test'")
}

Discussion