Slide 1

Slide 1 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. Application Prototyping Solutions Architect Fumihiko Shiroyama September 6, 2019 実機の管理とおさらば! AWS Device FarmでiOSのテストをしよう! iOSDC Japan 2019

Slide 2

Slide 2 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. スライドURL https://bit.ly/2m2Gfww

Slide 3

Slide 3 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. ⾃⼰紹介 名前:⽩⼭ ⽂彦(しろやま ふみひこ) 所属:アマゾン ウェブ サービス ジャパン株式会社 アプリケーションプロトタイピングソリューションアーキテクト 経歴:インフラエンジニア、バックエンド開発者 モバイルアプリ開発者、クラウドアーキテクト 趣味:⼦育て!、懸垂、⾃動テスト#

Slide 4

Slide 4 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. ⾃動テスト⼤好きです! • Androidテスト全書という本を出しました • ⾃動テストの種類やカバーする範囲、モック (テストダブル)の考え⽅、CI/CDとの統合 など、iOSにも応⽤できるトピックをカバー しています

Slide 5

Slide 5 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. このセッションの対象者と⽬的 対象者 • iOSのテスト初⼼者 • テストの⾃動実⾏にチャレンジしたい⽅ • デバイスの管理から開放されたい⽅ ゴール • iOSの⾃動テストを速習する • AWS Device Farmで⾃動テスト実⾏とリモートアクセス • AWS Device FarmとCI/CDツールとの統合

Slide 6

Slide 6 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. 対象となるアプリ

Slide 7

Slide 7 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. じゃんけんゲーム プレーヤーは 「⼿」を選ぶ

Slide 8

Slide 8 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. デモ

Slide 9

Slide 9 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. テストとはなんだろうか

Slide 10

Slide 10 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. テストの種類 • ユーザが⼿動で⾏うテスト • ⾃動テスト (Automated Tests) • ユニットテスト (Unit Test) • 統合テスト (Integration Test) • UIテスト 統合テストの定義は会社や個⼈によって広範にわたる傾向にある

Slide 11

Slide 11 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. テストピラミッド • The Test Pyramid • テストのバランスを⽰す指針 • 上から下にかけて数が増える • 上に⾏くほど⼿間がかかる 引⽤:https://martinfowler.com/articles/practical-test-pyramid.html

Slide 12

Slide 12 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. テストピラミッド • The Test Pyramid • テストのバランスを⽰す指針 • 上から下にかけて数が増える • 上に⾏くほど⼿間がかかる 引⽤:https://martinfowler.com/articles/practical-test-pyramid.html 今⽇はユニットテストとUIテストについてかいつまんで解説します%

Slide 13

Slide 13 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. iOSにおける⾃動テスト

Slide 14

Slide 14 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. XCTest • https://developer.apple.com/documentation/xctest • Appleが開発・提供するテストフレームワーク • ユニットテスト、パフォーマンステスト、UIテストを提供 • Xcode 5.0+ から利⽤可能 ☝XCTest以外にも⾊々あります

Slide 15

Slide 15 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. XCTestのはじめ⽅

Slide 16

Slide 16 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. XCTestのはじめ⽅ 後からプロジェクトに追加する場合

Slide 17

Slide 17 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. ユニットテスト

Slide 18

Slide 18 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. ユニットテストの基礎の基礎 • 部品(ユニット)を作る • テストクラスを作る • テストメソッドを実装 • 機能が正しいことをアサーション • ⾃動テストを実⾏する

Slide 19

Slide 19 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. import XCTest @testable import SampleTest class SampleUnitTest: XCTestCase { func testSample1() { XCTAssertTrue(1 + 1 == 2) } func testSample2() { XCTAssertEqual("foo" + "bar", "foobar") } } テストクラス

Slide 20

Slide 20 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. import XCTest @testable import SampleTest class SampleUnitTest: XCTestCase { func testSample1() { XCTAssertTrue(1 + 1 == 2) } func testSample2() { XCTAssertEqual("foo" + "bar", "foobar") } } XCTestを利⽤

Slide 21

Slide 21 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. import XCTest @testable import SampleTest class SampleUnitTest: XCTestCase { func testSample1() { XCTAssertTrue(1 + 1 == 2) } func testSample2() { XCTAssertEqual("foo" + "bar", "foobar") } } テスト対象のモジュールに アクセス可能にする

Slide 22

Slide 22 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. import XCTest @testable import SampleTest class SampleUnitTest: XCTestCase { func testSample1() { XCTAssertTrue(1 + 1 == 2) } func testSample2() { XCTAssertEqual("foo" + "bar", "foobar") } } XCTestCaseを継承

Slide 23

Slide 23 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. import XCTest @testable import SampleTest class SampleUnitTest: XCTestCase { func testSample1() { XCTAssertTrue(1 + 1 == 2) } func testSample2() { XCTAssertEqual("foo" + "bar", "foobar") } } テストメソッド testから始まる命名規則

Slide 24

Slide 24 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. import XCTest @testable import SampleTest class SampleUnitTest: XCTestCase { func testSample1() { XCTAssertTrue(1 + 1 == 2) } func testSample2() { XCTAssertEqual("foo" + "bar", "foobar") } } アサーション 意図した結果だと 主張(アサーション)

Slide 25

Slide 25 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. import XCTest @testable import SampleTest class SampleUnitTest: XCTestCase { func testSample1() { XCTAssertTrue(1 + 1 == 2) } func testSample2() { XCTAssertEqual("foo" + "bar", "foobar") } } 全体像

Slide 26

Slide 26 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. 実⾏ Pass! ✅ 個別実⾏ 全体実⾏

Slide 27

Slide 27 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. class SampleUnitTest: XCTestCase { override func setUp() { } override func tearDown() { } } 初期化・後処理 テストケース毎に実⾏される

Slide 28

Slide 28 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. じゃんけんゲームのユニットテスト

Slide 29

Slide 29 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. じゃんけんゲームのユニットテスト • 「じゃんけん」機能を考える • 「⼿」をランダム抽選し、相⼿の「⼿」と⽐較 • 勝敗を判定して「結果」を返す

Slide 30

Slide 30 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. Hands.swift enum Hands: Int, CaseIterable { case paper case rock case scissors } じゃんけんの「⼿」

Slide 31

Slide 31 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. Result.swift じゃんけんの「結果」 enum Result { case win case lose case draw }

Slide 32

Slide 32 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. HandGenerator.swift 「⼿」を抽選するクラス class HandGenerator { func generate() -> Hands { let i = Int.random(in: 0..<3) let hand = Hands(rawValue: i)! return hand } }

Slide 33

Slide 33 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. HandGenerator.swift class HandGenerator { func generate() -> Hands { let i = Int.random(in: 0..<3) let hand = Hands(rawValue: i)! return hand } } 「⼿」を返す

Slide 34

Slide 34 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. HandGenerator.swift class HandGenerator { func generate() -> Hands { let i = Int.random(in: 0..<3) let hand = Hands(rawValue: i)! return hand } } 乱数で抽選

Slide 35

Slide 35 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. PaperRockScissors.swift class PaperRockScissors { let generator = HandGenerator() func play(hand: Hands) -> Result { // 後述 } } じゃんけんクラス

Slide 36

Slide 36 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. PaperRockScissors.swift class PaperRockScissors { let generator = HandGenerator() func play(hand: Hands) -> Result { // 後述 } } 抽選オブジェクト

Slide 37

Slide 37 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. PaperRockScissors.swift class PaperRockScissors { let generator = HandGenerator() func play(hand: Hands) -> Result { // 後述 } } 結果を返す

Slide 38

Slide 38 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. func play(hand: Hands) -> Result { }

Slide 39

Slide 39 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. func play(hand: Hands) -> Result { let generated = generator.generate() } 抽選

Slide 40

Slide 40 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. func play(hand: Hands) -> Result { let generated = generator.generate() if (generated == hand) { return Result.draw } else if (generated == Hands.paper && hand == Hands.scissors) { return Result.win } else if (generated == Hands.rock && hand == Hands.paper) { return Result.win } else if (generated == Hands.scissors && hand == Hands.rock) { return Result.win } else { return Result.lose } } 結果を場合分け

Slide 41

Slide 41 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. func play(hand: Hands) -> Result { let generated = generator.generate() if (generated == hand) { return Result.draw } else if (generated == Hands.paper && hand == Hands.scissors) { return Result.win } else if (generated == Hands.rock && hand == Hands.paper) { return Result.win } else if (generated == Hands.scissors && hand == Hands.rock) { return Result.win } else { return Result.lose } } play()メソッドをどうテストする?

Slide 42

Slide 42 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. テストしにくい理由を考える • 実はPaperRockScissors#play(hand:)はテストしにくい • 「⼿」がランダム抽選されるので結果が不定 • 依存しているランダム抽選オブジェクトが変更不能なプロパティ • 依存オブジェクトを外から渡すことでコントロール可能に

Slide 43

Slide 43 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. Generator.swift protocol Generator { func generate() -> Hands } プロトコル定義で抽象化

Slide 44

Slide 44 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. HandGenerator.swift class HandGenerator { func generate() -> Hands { let i = Int.random(in: 0..<3) let hand = Hands(rawValue: i)! return hand } }

Slide 45

Slide 45 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. HandGenerator.swift class HandGenerator: Generator { func generate() -> Hands { let i = Int.random(in: 0..<3) let hand = Hands(rawValue: i)! return hand } }

Slide 46

Slide 46 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. PaperRockScissors.swift class PaperRockScissors { let generator = HandGenerator() func play(hand: Hands) -> (Result, Hands) { } }

Slide 47

Slide 47 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. PaperRockScissors.swift class PaperRockScissors { let generator: Generator func play(hand: Hands) -> (Result, Hands) { } }

Slide 48

Slide 48 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. PaperRockScissors.swift class PaperRockScissors { let generator: Generator init(generator: Generator = HandGenerator()) { self.generator = generator } func play(hand: Hands) -> (Result, Hands) { } }

Slide 49

Slide 49 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. PaperRockScissors.swift class PaperRockScissors { let generator: Generator init(generator: Generator = HandGenerator()) { self.generator = generator } func play(hand: Hands) -> (Result, Hands) { } } 初期値を与えつつ 差し替え可能に

Slide 50

Slide 50 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. テストコード

Slide 51

Slide 51 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. PaperRockScissorsTest.swift class PaperRockScissorsTest: XCTestCase { }

Slide 52

Slide 52 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. PaperRockScissorsTest.swift class PaperRockScissorsTest: XCTestCase { class MockGenerator: Generator { var hand = Hands.paper func generate() -> Hands { return hand } } } テスト⽤のGeneratorクラス

Slide 53

Slide 53 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. PaperRockScissorsTest.swift class PaperRockScissorsTest: XCTestCase { class MockGenerator: Generator { var hand = Hands.paper func generate() -> Hands { return hand } } } 「⼿」は差し替え可能

Slide 54

Slide 54 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. PaperRockScissorsTest.swift let generator = MockGenerator() var paperRockScissors: PaperRockScissors! override func setUp() { paperRockScissors = PaperRockScissors(generator: generator) } コントロール可能な 依存オブジェクトを渡す

Slide 55

Slide 55 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. PaperRockScissorsTest.swift func testPlay() { let myHand = Hands.paper generator.hand = Hands.rock let result = paperRockScissors.play(hand: myHand) XCTAssertEqual(result, Result.win) } テストケース

Slide 56

Slide 56 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. PaperRockScissorsTest.swift func testPlay() { let myHand = Hands.paper generator.hand = Hands.rock let result = paperRockScissors.play(hand: myHand) XCTAssertEqual(result, Result.win) } ⾃分の「⼿」はパー

Slide 57

Slide 57 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. PaperRockScissorsTest.swift func testPlay() { let myHand = Hands.paper generator.hand = Hands.rock let result = paperRockScissors.play(hand: myHand) XCTAssertEqual(result, Result.win) } 相⼿の「⼿」はグー

Slide 58

Slide 58 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. PaperRockScissorsTest.swift func testPlay() { let myHand = Hands.paper generator.hand = Hands.rock let result = paperRockScissors.play(hand: myHand) XCTAssertEqual(result, Result.win) } 結果は勝利!

Slide 59

Slide 59 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. PaperRockScissorsTest.swift func testPlay() { let myHand = Hands.paper generator.hand = Hands.rock let result = paperRockScissors.play(hand: myHand) XCTAssertEqual(result, Result.win) }

Slide 60

Slide 60 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. ユニットテストまとめ • ユニットテストは⽂字通りユニット(部品)単位で書く • テストしづらい場合は粒度をより細かく • 依存オブジェクトを外から渡すとテストしやすくなる • 継続的に実⾏する⼿順はこのあと学びます

Slide 61

Slide 61 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. UIテスト

Slide 62

Slide 62 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. • 画⾯を操作し、その結果を検証するテスト • XCTestはUIテストをサポートしており、便宜上XCTest UIと呼称する • UI要素へのアクセス以外はアサーション含めユニットテストとほとんど同じ • テストピラミッドの最上位に位置し、作成もメンテも最も⼿間がかかるので、⽤法⽤ 量を守って付き合う UIテスト

Slide 63

Slide 63 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. XCTest UIの基礎 import XCTest class SampleTestUITests: XCTestCase { var app: XCUIApplication! override func setUp() { continueAfterFailure = false app = XCUIApplication() app.launch() } override func tearDown() { }

Slide 64

Slide 64 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. XCTest UIの基礎 import XCTest class SampleTestUITests: XCTestCase { var app: XCUIApplication! override func setUp() { continueAfterFailure = false app = XCUIApplication() app.launch() } override func tearDown() { }

Slide 65

Slide 65 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. XCTest UIの基礎 import XCTest class SampleTestUITests: XCTestCase { var app: XCUIApplication! override func setUp() { continueAfterFailure = false app = XCUIApplication() app.launch() } override func tearDown() { } アプリの起動

Slide 66

Slide 66 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. UI要素の特定 let topLabel = app.staticTexts["top_label"] XCTAssertEqual(topLabel.label, "じゃんけん") Accessibility Identifier

Slide 67

Slide 67 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. UI要素の特定 let topLabel = app.staticTexts["top_label"] XCTAssertEqual(topLabel.label, "じゃんけん") Accessibility Identifier ラベルテキストの検証

Slide 68

Slide 68 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. UI要素の特定 Accessibility Identifier let opponentHand = app.images["opponent_hand"] XCTAssertTrue(opponentHand.exists)

Slide 69

Slide 69 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. UI要素の特定 Accessibility Identifier let opponentHand = app.images["opponent_hand"] XCTAssertTrue(opponentHand.exists) 要素の存在検証

Slide 70

Slide 70 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. UI要素の操作 同様に「⼿」も特定 let rock = app.images["my_hand_rock"] let paper = app.images["my_hand_paper"] let scissors = app.images["my_hand_scissors"] for hand in [rock, paper, scissors] { hand.tap() } それぞれの「⼿」をタップ

Slide 71

Slide 71 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. UI要素の変更を検知 初期画像が これに変わってることを テストしたい どうする?

Slide 72

Slide 72 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. ViewController.swift switch opponentHand { case .paper: self.boyHand.image = UIImage(named: "boy_paper") self.boyHand.accessibilityLabel = "boy_paper" case .rock: self.boyHand.image = UIImage(named: "boy_rock") self.boyHand.accessibilityLabel = "boy_rock" case .scissors: self.boyHand.image = UIImage(named: "boy_scissors") self.boyHand.accessibilityLabel = "boy_scissors" } 相⼿の「⼿」画像を更新するロジック

Slide 73

Slide 73 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. ViewController.swift switch opponentHand { case .paper: self.boyHand.image = UIImage(named: "boy_paper") self.boyHand.accessibilityLabel = "boy_paper" case .rock: self.boyHand.image = UIImage(named: "boy_rock") self.boyHand.accessibilityLabel = "boy_rock" case .scissors: self.boyHand.image = UIImage(named: "boy_scissors") self.boyHand.accessibilityLabel = "boy_scissors" } accessibilityLabelに値をセット

Slide 74

Slide 74 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. UI要素の変更を検知 let opponentHand = app.images["opponent_hand"] let accessibilityLabel = opponentHand.label let imageChanged = accessibilityLabel == "boy_paper" || accessibilityLabel == "boy_rock" || accessibilityLabel == "boy_scissors" XCTAssertTrue(imageChanged) 再びUIテストコード

Slide 75

Slide 75 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. UI要素の変更を検知 let opponentHand = app.images["opponent_hand"] let accessibilityLabel = opponentHand.label let imageChanged = accessibilityLabel == "boy_paper" || accessibilityLabel == "boy_rock" || accessibilityLabel == "boy_scissors" XCTAssertTrue(imageChanged)

Slide 76

Slide 76 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. UI要素の変更を検知 let opponentHand = app.images["opponent_hand"] let accessibilityLabel = opponentHand.label let imageChanged = accessibilityLabel == "boy_paper" || accessibilityLabel == "boy_rock" || accessibilityLabel == "boy_scissors" XCTAssertTrue(imageChanged) 相⼿が初期状態から グーチョキパーのいずれかに 変わったことを検知

Slide 77

Slide 77 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. UI要素の変更を検知 let opponentHand = app.images["opponent_hand"] let accessibilityLabel = opponentHand.label let imageChanged = accessibilityLabel == "boy_paper" || accessibilityLabel == "boy_rock" || accessibilityLabel == "boy_scissors" XCTAssertTrue(imageChanged) Pass!!

Slide 78

Slide 78 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. UI要素の変更を検知 let opponentHand = app.images["opponent_hand"] let accessibilityLabel = opponentHand.label let imageChanged = accessibilityLabel == "boy_paper" || accessibilityLabel == "boy_rock" || accessibilityLabel == "boy_scissors" XCTAssertTrue(imageChanged) ※あくまで⼀例であり、ViewController側でaccessibilityLabelをセットすることを強く推奨するものではありません

Slide 79

Slide 79 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. UIテストデモ

Slide 80

Slide 80 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.

Slide 81

Slide 81 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. バグの原因を突き⽌める

Slide 82

Slide 82 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. リリースしたが… なんか私のiPhone(iOS 10) でクラッシュするんですが… 弱ったな… こちらで確認できないぞ

Slide 83

Slide 83 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. バグの事実と原因を確かめたい • ユーザからiOS 10系で動かないという報告があった • 実はiOS 11.2未満でクラッシュする深刻なバグがある • 報告を確かめたいが古い端末は⼿元にない • AWS Device Farmを使ってみよう%

Slide 84

Slide 84 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. AWS Device Farm

Slide 85

Slide 85 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. 近年のモバイル開発が抱える課題 デバイスとOSの 多様化 複数のテストフレー ムワーク対応の困難 デバイスメンテ の⼿間 テストレポートの 可視化に対する要望

Slide 86

Slide 86 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. AWS Device Farm ⾃動テスト リモートアクセス リモートデバッグ ⾃動テストを好きな時に 好きな数だけクラウド上 で並列実⾏ クラウド上の好きな端末の 任意のバージョンですぐに リモートで動作確認 クラウド上のiOS端末や Android端末をローカルの IDEから接続してデバッグ ※プライベートデバイスのみ

Slide 87

Slide 87 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. 様々なテストパターンに対応 アプリ UI Automation UI Automator XCTest 各種テストフレームワーク AWS Device Farm 詳細なレポート

Slide 88

Slide 88 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. デバイスタイプ • パブリックデバイス • 0.17USD/デバイス分の従量課⾦(無料利⽤期間あり) • 250USD/⽉の定額プランで無制限利⽤ • 端末はテスト毎にデータ消去して再起動 • プライベートデバイス • 200USD/⽉から利⽤できる専有デバイス※1※2 • デバイスは専有で決して他者と共有されず、契約終了後に廃棄 • リモートデバッグ機能が利⽤可能 ※1 端末の取得価格によって変動 ※2 最低6ヶ⽉の利⽤など諸条件あり

Slide 89

Slide 89 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. 早速使ってみよう

Slide 90

Slide 90 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. Built-in: Fuzzテスト • いわゆるモンキーテスト • テストコードがなくてもUIをランダムに操作して落ちないか確認 • 複数のデバイスやOSを並列実⾏できる

Slide 91

Slide 91 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. ipaをビルド

Slide 92

Slide 92 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. ipaをビルド

Slide 93

Slide 93 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. AWS Device Farm プロジェクトの作成

Slide 94

Slide 94 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. Automated Tests ipaのアップロード ⾃動テストを選択

Slide 95

Slide 95 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. Automated Tests Device poolの選択 テストするデバイス群 Fuzzテスト

Slide 96

Slide 96 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. Fuzzテスト テスト結果 やはり古いバージョンで エラーが起こっているようだ

Slide 97

Slide 97 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. エラーの録画を確認する

Slide 98

Slide 98 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.

Slide 99

Slide 99 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. Screenshots やはりここで落ちている

Slide 100

Slide 100 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. Logs クラッシュ理由とソース コードの対応⾏を発⾒!

Slide 101

Slide 101 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. リモートアクセス

Slide 102

Slide 102 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. リモートアクセス ⽬当てのバージョンを選択 リモートアクセス

Slide 103

Slide 103 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. リモートアクセス ブラウザから操作

Slide 104

Slide 104 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. リモートアクセス やはりここでクラッシュ! このバージョンに間違いない

Slide 105

Slide 105 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. その他のテスト

Slide 106

Slide 106 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. その他のテストをAWS Device Farmで実⾏する • AWS Device FarmはFuzzテスト以外にも多数のテストをサポートしている • XCTestを選ぶことでユニットテストが実⾏できる • XCTest UIを選ぶことでUIテストが実⾏できる • 他にもAppiumやEspressoなど多数のフレームワークをサポートしている

Slide 107

Slide 107 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. XCTestをAWS Device Farmで利⽤する • https://docs.aws.amazon.com/ja_jp/devicefarm/latest/developerguide/test- types-ios-xctest.html 1. ⼿順を参考にアプリ本体のipaをビルドする 2. ⼿順を参考にXCTestパッケージをアーカイブする 3. AWS Device Farmにアップロードして実⾏

Slide 108

Slide 108 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. XCTest ユニットテスト XCTestパッケージをアップロード

Slide 109

Slide 109 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. XCTest ⽤途に応じて独⾃のDevice Poolも定義できる Deviceの台数や組み合わせも任意

Slide 110

Slide 110 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. XCTest 追加のオプションや タイムアウトを設定して実⾏

Slide 111

Slide 111 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. XCTest UIをAWS Device Farmで利⽤する • https://docs.aws.amazon.com/ja_jp/devicefarm/latest/developerguide/test- types-ios-xctest-ui.html 1. ⼿順を参考にアプリ本体のipaをビルドする 2. ⼿順を参考にXCTest UI Runnerバンドルをアーカイブする 3. AWS Device Farmにアップロードして実⾏

Slide 112

Slide 112 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. XCTest UI UIテスト XCTest UI⽤Runnerバンドルをアップロード

Slide 113

Slide 113 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. XCTest UI ユニットテスト同様 オプションを設定して実⾏

Slide 114

Slide 114 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. CI/CDとの統合

Slide 115

Slide 115 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. CI/CDとの統合 • AWS Device Farmは便利だが⼿動でアップロードは⼤変 • CI/CDツールと連携することで⽇々のテストを⾃動化できる • ここではCircleCIを使ってDevice Farmのテストを⾃動化する • https://circleci.com/

Slide 116

Slide 116 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. CircleCIとの連携 macOSでビルドするには有料プランが必要(無料トライアルあり)

Slide 117

Slide 117 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. CircleCIの設定 • プロジェクト直下に.circleci/config.ymlという設定ファイルを設置 • yamlファイルを編集しビルドとテストの実⾏を設定(後述) • Gitレポジトリへのプッシュを契機にビルドとテストが開始される • 設定例は後述する

Slide 118

Slide 118 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. fastlaneの組み込み • fastlaneはビルド、テスト、リリースなどのタスクを⾃動化するツール • https://github.com/fastlane/fastlane • ⼀連のタスクをlaneという単位にまとめて記述、管理できる • iOSの証明書やプロビジョニングプロファイルの管理を簡単にできる • AWS Device Farmと連携するプラグインが提供されている

Slide 119

Slide 119 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. fastlaneの組み込み % gem install bundler % bundle init % vim Gemfile gem "fastlane" % bundle install % bundle exec fastlane --version プロジェクト直下で 次のコマンドを実⾏

Slide 120

Slide 120 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. matchで証明書管理 • matchはfastlaneで利⽤できるアクションで、証明書やプロビジョニング ファイルをプライベートレポジトリに⼀元管理できるツール • はじめにcertificatesなどの名前でプライベートレポジトリを作成しておく • コマンドを実⾏すると秘匿情報が証明書にプッシュされる • 詳細は公式ドキュメントを参照してください • https://docs.fastlane.tools/actions/match/

Slide 121

Slide 121 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. matchを設定 git_url("[email protected]:your_account/certificates.git") storage_mode("git") type("development") # The default type app_identifier(["us.shiroyama.SampleTest", "us.shiroyama.SampleTestUITests"]) username("Your Apple ID") % bundle exec fastlane match init vim fastlane/Matchfile UIテスト⽤のIdentifierを 忘れないこと % bundle exec fastlane match development ここで設定したパスワードをCircleCIの 環境変数MATCH_PASSWORDに設定

Slide 122

Slide 122 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. fastlaneでビルドとテストまで設定 default_platform(:ios) before_all do |lane, options| setup_circle_ci end platform :ios do desc "Build and Test" lane :build_and_test do match( type: "development", readonly: true, ) scan() end CircleCIでmatchを使う設 定 ビルドとテストが実⾏できる状態

Slide 123

Slide 123 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. fastlaneにaws_device_farmプラグインを追加 Device Farmにテストを スケジュールする設定 % bundle exec fastlane add_plugin aws_device_farm % vim fastlane/Fastfile desc "AWS Device Farm" lane :device_farm do |options| test_type = "XCTEST_UI" derived_data_path = options[:derived_data_path] configuration = options[:configuration] aws_device_farm_package( derrived_data_path: derived_data_path, configuration: configuration, ) aws_device_farm( name: "My1stProject", device_pool: "Top Devices", test_type: test_type, run_name: "#{test_type} - #{Time.now}", ) end Device Farm⽤のlaneを定義

Slide 124

Slide 124 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. 定義したlaneを利⽤してテストをスケジュール derived_data_path = "aws" scan( build_for_testing: true, destination: "generic/platform=iOS", derived_data_path: derived_data_path, ) device_farm( derived_data_path: derived_data_path, configuration: "Debug", ) Device Farmのために Generic iOSでビルド

Slide 125

Slide 125 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. CircleCIへの組み込み 作ったlaneの呼び出し

Slide 126

Slide 126 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. CircleCIの環境変数設定 AWSのキー情報 matchのパスフレーズ

Slide 127

Slide 127 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. 証明書取得設定 Permissions > Checkout SSH Keys > Add user key > Authorize with GittHub 証明書レポジトリに アクセスできるよう設定

Slide 128

Slide 128 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. 満を持してプッシュ!

Slide 129

Slide 129 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. CircleCIコンソール プッシュをトリガに ビルドが成功

Slide 130

Slide 130 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. AWS Device Farmコンソール Device Farm側でも ビルドの成功を確認!

Slide 131

Slide 131 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. 処理の流れおさらい 1. Gitレポジトリへのプッシュを契機にCircleCIでビルドが始まる 2. CircleCIがfastlaneを使ってソースをビルド 3. fastlane awc_device_farmプラグインがビルドアーティファクトを元に Device Farmで利⽤できる形式に変換 4. プラグインからAWS APIが呼び出されDevice Farmにアップロード&テスト が実⾏される 5. fastlaneはテスト結果を受け取るまで待ってCircleCIに通知する

Slide 132

Slide 132 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. CI/CDとの統合まとめ • CI/CDパイプラインに組み込むことで、ビルドからテストまでロー カルに⼀切依存しない環境を構築できた • CircleCIのワークフロー機能を利⽤すると、特定のブランチへの プッシュのみを契機にしたテスト等も可能 • プラグインはその他多くのCI/CD⽤に提供されており、⾃分にあっ たものを選ぶことができる コスト削減

Slide 133

Slide 133 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. Yay!!!

Slide 134

Slide 134 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. ご清聴ありがとございました,

Slide 135

Slide 135 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. 質疑応答

Slide 136

Slide 136 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. Appendix

Slide 137

Slide 137 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. 参考⽂献 • 平⽥敏之, 細沼祐介(2019)『iOSアプリ開発⾃動テストの教科書 〜XCTestによる単体テスト・UIテストから、CI/CD、デバッグ技術 まで』技術評論社. • ⽩⼭⽂彦, 外⼭純⽣, 平⽥敏之, 菊池紘, 堀江亮介(2018)『Android テスト全書』PEAKS.

Slide 138

Slide 138 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. AWS Device Farm テストデバイス⼀覧 • Q. AWS Device Farmではどのようなデバイスが利⽤できますか • A. モバイルアプリケーションのテストに使⽤できる iOS、 Android、および Fire OS の実機は増え続けています。デバイス群 は、市場のデータとお客様からのフィードバックをもとに、今後も 引き続き更新していく予定です。⼀覧は次のリストをご覧くださ い。 • http://awsdevicefarm.info/

Slide 139

Slide 139 text

© 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved. CircleCIのWorkflow workflows: version: 2 build_and_test: jobs: - build - test: requires: - build filters: branches: only: - master - release Master, release のみDevice Farmを動かす