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

Snapshot Testing in iOS

Snapshot Testing in iOS

Avatar for yohei sugigami

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