$30 off During Our Annual Pro Sale. View Details »

iOSDC_2019_DeviceFarm.pdf

 iOSDC_2019_DeviceFarm.pdf

Fumihiko Shiroyama

September 06, 2019
Tweet

More Decks by Fumihiko Shiroyama

Other Decks in Technology

Transcript

  1. © 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
  2. © 2019, Amazon Web Services, Inc. or its Affiliates. All

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    rights reserved. ユニットテストの基礎の基礎 • 部品(ユニット)を作る • テストクラスを作る • テストメソッドを実装 • 機能が正しいことをアサーション • ⾃動テストを実⾏する
  18. © 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") } } テストクラス
  19. © 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を利⽤
  20. © 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") } } テスト対象のモジュールに アクセス可能にする
  21. © 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を継承
  22. © 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から始まる命名規則
  23. © 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") } } アサーション 意図した結果だと 主張(アサーション)
  24. © 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") } } 全体像
  25. © 2019, Amazon Web Services, Inc. or its Affiliates. All

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

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

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

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

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

    rights reserved. Result.swift じゃんけんの「結果」 enum Result { case win case lose case draw }
  31. © 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 } }
  32. © 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 } } 「⼿」を返す
  33. © 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 } } 乱数で抽選
  34. © 2019, Amazon Web Services, Inc. or its Affiliates. All

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

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

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

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

    rights reserved. func play(hand: Hands) -> Result { let generated = generator.generate() } 抽選
  39. © 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 } } 結果を場合分け
  40. © 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()メソッドをどうテストする?
  41. © 2019, Amazon Web Services, Inc. or its Affiliates. All

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

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

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

    rights reserved. PaperRockScissors.swift class PaperRockScissors { let generator: Generator func play(hand: Hands) -> (Result, Hands) { } }
  47. © 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) { } }
  48. © 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) { } } 初期値を与えつつ 差し替え可能に
  49. © 2019, Amazon Web Services, Inc. or its Affiliates. All

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

    rights reserved. PaperRockScissorsTest.swift class PaperRockScissorsTest: XCTestCase { }
  51. © 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クラス
  52. © 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 } } } 「⼿」は差し替え可能
  53. © 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) } コントロール可能な 依存オブジェクトを渡す
  54. © 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) } テストケース
  55. © 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) } ⾃分の「⼿」はパー
  56. © 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) } 相⼿の「⼿」はグー
  57. © 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) } 結果は勝利!
  58. © 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) }
  59. © 2019, Amazon Web Services, Inc. or its Affiliates. All

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

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

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

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

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

    rights reserved. UI要素の特定 Accessibility Identifier let opponentHand = app.images["opponent_hand"] XCTAssertTrue(opponentHand.exists) 要素の存在検証
  68. © 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() } それぞれの「⼿」をタップ
  69. © 2019, Amazon Web Services, Inc. or its Affiliates. All

    rights reserved. UI要素の変更を検知 初期画像が これに変わってることを テストしたい どうする?
  70. © 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" } 相⼿の「⼿」画像を更新するロジック
  71. © 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に値をセット
  72. © 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テストコード
  73. © 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)
  74. © 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) 相⼿が初期状態から グーチョキパーのいずれかに 変わったことを検知
  75. © 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!!
  76. © 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をセットすることを強く推奨するものではありません
  77. © 2019, Amazon Web Services, Inc. or its Affiliates. All

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    rights reserved. XCTest 追加のオプションや タイムアウトを設定して実⾏
  103. © 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にアップロードして実⾏
  104. © 2019, Amazon Web Services, Inc. or its Affiliates. All

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

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

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

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

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

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

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

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

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

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

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

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

    rights reserved. AWS Device Farmコンソール Device Farm側でも ビルドの成功を確認!
  122. © 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に通知する
  123. © 2019, Amazon Web Services, Inc. or its Affiliates. All

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

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

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