Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Snapshot Testing in iOS

Snapshot Testing in iOS

yohei sugigami

April 16, 2019
Tweet

More Decks by yohei sugigami

Other Decks in Technology

Transcript

  1. Snapshot Testing ɹ ɹ ɹ ɹ iOS test Night #10

    2019/04/16@גࣜձࣾσΟʔɾΤψɾΤʔ Yohei Suginami ( @susieyy )
  2. Profile — Yohei Sugigami — @susieyy — Twitter / Github

    / Qiita — Freelance iOS App Developer — @ FOLIO Co., Ltd.
  3. ςετίʔυྫ import FBSnapshotTestCase class FBSnapshotTestCaseSwiftTest: FBSnapshotTestCase { override func setUp()

    { super.setUp() // ↓ true ʹ͢ΔͱςετͰ͸ͳ͘ϦϑΝϨϯεը૾Λग़ྗ recordMode = false } func testExample() { let vc = UIViewController() vc.view.size = CGSize(width: 370, height: 675) FBSnapshotVerifyView(vc.view) } }
  4. ςετίʔυྫ class HogeViewControllerTests: SnapshotTestCase { func testAuthenticated() { stub(uri("/api/endpoins1"), jsonData(fixtureData("test_data1.json")))

    stub(uri("/api/endpoins2"), jsonData(fixtureData("test_data2.json"))) let store = environment.createRedux() login(store) let viewController = HogeViewController(reduxStore: store) let navigationController = UINavigationController(rootViewController: viewController) viewController.request() verifyViewController(navigationController) verifyViewController(viewController, identifier: "fullscreen", options: [.fullscreen]) } }
  5. ௨৴ͷϨεϙϯεΛMockԽ(2/2) — ࢦఆͨ͠ϦΫΤετʢύε΍ύϥϝʔλʣ͕Ϛονͨ͠৔ ߹ʹ೚ҙͷϨεϙϯεΛฦ͢Α͏ʹมߋͰ͖Δ — status 200 & JSON body

    or status 500 — Ϩεϙϯεͷdelay΋ઃఆՄೳ — ௨৴தͷঢ়ଶ΍ϦΫΤετλΠϜΞ΢τͷ֬ೝ — ը૾΋ઃఆՄೳ
  6. CI͚ͩTest͕Fail͢Δ໰୊͕Ұ࣌ظൃੜ (1/2) — BitriseͷϚγϯϦιʔεෆ଍͕ݪҼͩͬͨ — ૝ఆ͍ͯ͠Δ΢ΣΠτ࣌ؒ಺ʹॲཧʢΞχϝʔγϣϯʣ͕ ׬ྃͤͣϦϑΝϨϯεը૾ͱࠩ෼͕ग़ͯ͠·͏ — खݩͷϚγϯͰ͸ৗʹύε͢Δ —

    Bitrise͕ϚγϯϦιʔεΛΞοϓάϨʔυͨ͠ͷͰվળ Update on Mac infrastructure upgrades and queues @ March 14, 2019 - https://blog.bitrise.io/update-mac-infrastructure-
  7. UIViewController ը໘Πϝʔδൺֱ assertSnapshot(matching: vc, as: .image) assertSnapshot(matching: vc, as: .image(on:

    .iPhoneSe)) assertSnapshot(matching: vc, as: .image(on: .iPhoneSe(.landscape))) assertSnapshot(matching: vc, as: .image(on: .iPhoneX)) assertSnapshot(matching: vc, as: .image(on: .iPadMini(.portrait))) — ಺෦ͰWindowͱRootViewController͕࡞͘ΒΕaddChild — VCͷϥΠϑαΠΫϧ΋ίʔϧόοΫ͞ΕΔ — viewWillAppear, vieDidAppear
  8. UIViewController ViewͷϑϨʔϜͱώΤϥϧΩʔൺֱ assertSnapshot(matching: vc, as: .recursiveDescription) assertSnapshot(matching: vc, as: .recursiveDescription(on:

    .iPhoneSe)) assertSnapshot(matching: vc, as: .recursiveDescription(on: .iPhoneSe(.landscape))) assertSnapshot(matching: vc, as: .recursiveDescription(on: .iPhoneX)) assertSnapshot(matching: vc, as: .recursiveDescription(on: .iPadMini(.portrait))) // [ AF LU ] h=--- v=--- NSButton "Push Me" f=(0,0,77,32) b=(-) // [ A LU ] h=--- v=--- NSButtonBezelView f=(0,0,77,32) b=(-) // [ AF LU ] h=--- v=--- NSButtonTextField "Push Me" f=(10,6,57,16) b=(-) // A=autoresizesSubviews, C=canDrawConcurrently, D=needsDisplay, F=flipped, G=gstate,... assertSnapshot(matching: vc, as: .hierarchy) // <UITabBarController>, state: appeared, view: <UILayoutContainerView> // | <UINavigationController>, state: appeared, view: <UILayoutContainerView> // | | <UIPageViewController>, state: appeared, view: <_UIPageViewControllerContentView> // | | | <UIViewController>, state: appeared, view: <UIView> // | <UINavigationController>, state: disappeared, view: <UILayoutContainerView> not in the window // | | <UIViewController>, state: disappeared, view: (view not loaded)
  9. Referenceͷॻ͖ग़͠ import SnapshotTesting import XCTest class HogeeTests: XCTestCase { func

    testView() { record = true let vc = MyViewController() assertSnapshot(matching: vc, as: .image) } }
  10. JSON assertSnapshot(matching: user, as: .json) // { // "bio" :

    "Blobbed around the world.", // "id" : 1, // "name" : "Blobby" // }
  11. CaseIterable enum Direction: String, CaseIterable { case up, down, left,

    right var rotatedLeft: Direction { switch self { case .up: return .left case .down: return .right case .left: return .down case .right: return .up } } } assertSnapshot( matching: { $0.rotatedLeft }, as: Snapshotting<Direction, String>.func(into: .description) ) // "up","left" // "down","right" // "left","down" // "right","up"
  12. Any assertSnapshot(matching: user, as: .dump) // ▿ User // -

    bio: "Blobbed around the world." // - id: 1 // - name: "Blobby"
  13. Defining Custom Snapshot Strategies extension Snapshotting where Value == WKWebView,

    Format == UIImage { public static let image: Snapshotting = Snapshotting<UIImage, UIImage>.image .asyncPullback { webView in Async { callback in webView.takeSnapshot(with: nil) { image, error in callback(image!) } } } }
  14. ࣄྫ঺հ1 ImagePipeline by Katsumi Kishikawa — Image Pipeline is an

    image loading and caching framework — σίʔυͨ͠ը૾΍Ճ޻ॲཧΛͨ͠ը૾ͷൺֱςετͰར ༻ — https://github.com/folio-sec/ImagePipeline
  15. ࣄྫ঺հ2 SwiftRewriter by Yasuhiro Inami — Swift code formatter using

    SwiftSyntax. — ΧελϜετϥςδʔΛ࡞੒͠ϑΥʔϚοτޙͷSwingίʔ υͱϦϑΝϨϯεSwingίʔυΛൺֱ — https://github.com/inamiy/SwiftRewriter