Slide 1

Slide 1 text

FBSnapshotTestCase ʹॿ͚ΒΕͨ࿩ Yuki Hirai Kyobashi.swift x AKIBA.swift ߹ಉษڧձ 2017/02/08

Slide 2

Slide 2 text

๻͸ฏҪ༞थͩʔʂ • 31ࡀɻطࠗ • དྷि͙Β͍ʹ͕஀ੜ͢Δ • RMP • iOS Engineer

Slide 3

Slide 3 text

FBSnapshotTestCaseʹ ॿ͚ΒΕͨ࿩Λ͠·͢

Slide 4

Slide 4 text

൒೥͙Β͍લɾɾɾ Φοεʂ࣍ͷϓϩδΣΫτདྷͨͧʔ ແअؾˁ طࠗˁ

Slide 5

Slide 5 text

൒೥͙Β͍લɾɾɾ Φοεʂ࣍ͷϓϩδΣΫτདྷͨͧʔ 0,ͬ͢ʂͲ͏͍͏ํ਑Ͱ࡞͍͖ͬͯ·͢ʁ ແअؾˁ طࠗˁ

Slide 6

Slide 6 text

൒೥͙Β͍લɾɾɾ ͦ͏ͩͳɾɾɾ ແअؾˁ طࠗˁ

Slide 7

Slide 7 text

൒೥͙Β͍લɾɾɾ ࠷ۙ$MFBO4XJGUྑͦ͞͏ͩͱࢥͬͯ͊͞ɺͦΕ࠾ ༻ͭͭ͠#PMUTͱ͔͍͍ײ͡ʹ࢖ͬͯॻ͜͏ͱࢥͬ ͯͯɺ͋ͬɺ͋ͱΧόϨοδ΋શମͰ͸ͱΓͨ ͍ͱ͜ΖͩΑͶɻ͋ͱTXJGUMJOU΋ΨονΨνʹೖΕ ͯ͞ʂ͍͍ײ͡͡ΌͶʂʁ4XJOKFDU΋ಋೖ͠Α ͏ʂ׬ྃ৚݅ʹ΋௥Ճ͠Α͏ʂʂ ແअؾˁ طࠗˁ

Slide 8

Slide 8 text

൒೥͙Β͍લɾɾɾ ࠷ۙ$MFBO4XJGUྑͦ͞͏ͩͱࢥͬͯ͊͞ɺͦΕ࠾ ༻ͭͭ͠#PMUTͱ͔͍͍ײ͡ʹ࢖ͬͯॻ͜͏ͱࢥͬ ͯͯɺ͋ͬɺ͋ͱΧόϨοδ΋શମͰ͸ͱΓͨ ͍ͱ͜ΖͩΑͶɻ͋ͱTXJGUMJOU΋ΨονΨνʹೖΕ ͯ͞ʂ͍͍ײ͡͡ΌͶʂʁ4XJOKFDU΋ಋೖ͠Α ͏ʂ׬ྃ৚݅ʹ΋௥Ճ͠Α͏ʂʂ ͑ͬʁ3Y4XJGU͕ྑ͍ͳɾɾɾͰ΋࣮੷͋ ΔΈ͍ͨͩ͠ͳ͊ɾɾ͔͠΋ͳΜָ͔ͦ͠ ͏ʹͯ͠Δ͠ɾɾɾ·͍͔͊ͬ ͸͍ʂʂ ແअؾˁ طࠗˁ

Slide 9

Slide 9 text

࣮૷ணख طࠗˁ

Slide 10

Slide 10 text

࣮૷ணख طࠗˁ ྑ͍ײ͡

Slide 11

Slide 11 text

͠͹Βͯ͘͠ɾɾɾ ɾɾɾ ແअؾˁ طࠗˁ

Slide 12

Slide 12 text

͠͹Βͯ͘͠ɾɾɾ ΍ͬͺ3Y4XJGUͰ͍͜͏ʂX ແअؾˁ طࠗˁ

Slide 13

Slide 13 text

͠͹Βͯ͘͠ɾɾɾ ΍ͬͺ3Y4XJGUͰ͍͜͏ʂX ແअؾˁ طࠗˁ ͓͍X

Slide 14

Slide 14 text

͜͏ͯ͠ॻ͖׵͕͑ ͸͡·ͬͨ

Slide 15

Slide 15 text

͔͠͠৺഑ແ༻ʂ

Slide 16

Slide 16 text

͜Μͳ͜ͱ΋͋Ζ͏͔ͱ ൿࡦΛ४උ͍ͯͨ͠ͷͩʂ

Slide 17

Slide 17 text

FBSnapshotTestCase • https://github.com/facebook/ios-snapshot- test-case • UIView/CALayer ͷεφοϓγϣοτͱ͋Β͔ ͡Ί༻ҙͨ͠ը૾Λൺֱͯ͠ɺҰக͠ͳ͍৔ ߹͸ςετࣦഊʹͰ͖Δ • XCTestCaseͷαϒΫϥε

Slide 18

Slide 18 text

Nimble-Snapshots • https://github.com/ashfurrow/Nimble- Snapshots • FBSnapshotTestCaseΛ Nimble ͷ matcher ͱ ͯ͠ఆٛ

Slide 19

Slide 19 text

Nimble-Snapshots expect(view).to(haveValidSnapshot()) expect(view).to(haveValidSnapshot(named: "some custom name")) expect(view) == snapshot() expect(view) == snapshot("some custom name") (view) (view, "some custom name")

Slide 20

Slide 20 text

import UIKit import Quick import Nimble import Nimble_Snapshots @testable import ScreenShotExample final class ViewControllerSpec: QuickSpec { override func spec() { describe("view") { var subject: ViewController! var window: UIWindow! beforeEach { window = UIWindow() let bundle = Bundle.main let storyboard = UIStoryboard(name: "Main", bundle: bundle) subject = storyboard.instantiateViewController(withIdentifier: "ViewController") as! ViewController window.addSubview(subject.view) RunLoop.current.run(until: Date()) } afterEach { window = nil } it("displays correctly") { (subject) // expect(subject).toEventually(haveValidSnapshot()) } } } ʩ

Slide 21

Slide 21 text

import UIKit import Quick import Nimble import Nimble_Snapshots @testable import ScreenShotExample final class ViewControllerSpec: QuickSpec { override func spec() { describe("view") { var subject: ViewController! var window: UIWindow! beforeEach { window = UIWindow() let bundle = Bundle.main let storyboard = UIStoryboard(name: "Main", bundle: bundle) subject = storyboard.instantiateViewController(withIdentifier: "ViewController") as! ViewController window.addSubview(subject.view) RunLoop.current.run(until: Date()) } afterEach { window = nil } it("displays correctly") { (subject) // expect(subject).toEventually(haveValidSnapshot()) } } } ʩ ύγϟϦʂ

Slide 22

Slide 22 text

Nimble-Snapshots

Slide 23

Slide 23 text

import UIKit import Quick import Nimble import Nimble_Snapshots @testable import ScreenShotExample final class ViewControllerSpec: QuickSpec { override func spec() { describe("view") { var subject: ViewController! var window: UIWindow! beforeEach { window = UIWindow() let bundle = Bundle.main let storyboard = UIStoryboard(name: "Main", bundle: bundle) subject = storyboard.instantiateViewController(withIdentifier: "ViewController") as! ViewController window.addSubview(subject.view) RunLoop.current.run(until: Date()) } afterEach { window = nil } it("displays correctly") { // (subject) expect(subject).toEventually(haveValidSnapshot()) } } } ʩ

Slide 24

Slide 24 text

import UIKit import Quick import Nimble import Nimble_Snapshots @testable import ScreenShotExample final class ViewControllerSpec: QuickSpec { override func spec() { describe("view") { var subject: ViewController! var window: UIWindow! beforeEach { window = UIWindow() let bundle = Bundle.main let storyboard = UIStoryboard(name: "Main", bundle: bundle) subject = storyboard.instantiateViewController(withIdentifier: "ViewController") as! ViewController window.addSubview(subject.view) RunLoop.current.run(until: Date()) } afterEach { window = nil } it("displays correctly") { // (subject) expect(subject).toEventually(haveValidSnapshot()) } } } ʩ

Slide 25

Slide 25 text

DEMO https://github.com/yutu/ScreenShotExample

Slide 26

Slide 26 text

ແࣄ৐Γӽ͑ͨʂ

Slide 27

Slide 27 text

͔͠͠ຊ౰ʹා͍ͷ͸ɾɾɾ

Slide 28

Slide 28 text

࣮͸ɾɾɾ ແअؾˁ طࠗˁ

Slide 29

Slide 29 text

Ͳͬͪ΋๻Ͱͨ͠ ແअؾˁ طࠗˁ

Slide 30

Slide 30 text

͔ͯ͘͠ FBSnapshotTestCaseʹ ๻͸ॿ͚ΒΕ·ͨ͠

Slide 31

Slide 31 text

FBSnapshotTestCase ͷ͍͍ͱ͜Ζ • ΞʔΩςΫνϟ΍OSSʹґଘ͠ͳ͍ςετ͕ॻ͚Δ • අ༻ରޮՌ͕ߴ͍ • XcodeͰΧόϨοδΛܭଌͰ͖Δ • PRͰεφοϓγϣοτͷdiff͕ݟΕΔ • CircleCI Ͱ΋ී௨ʹಈ͘ • TDDͰ͖Δ

Slide 32

Slide 32 text

ؾΛ͚͍ͭͨ͜ͱ • ϢχοτςετΛॻ͔ͳͯ͘ྑ͍Θ͚Ͱ͸ͳ͍ • IT͢Δ৔߹͸࣮ߦ͢ΔσόΠεͱiOS verʹ஫ҙ • UIΛར༻ͨ͠ςετ ≠ E2Eςετ • ʮͳͥɾͲ͜·ͰςετΛॻ͔͘ʢॻ͔ͳ͍ ͔ʣʯΛৗʹҙࣝ͢Δ

Slide 33

Slide 33 text

ʴΞϧϑΝ • CircleCI + fastlane (scan/slather) ͰΧόϨο δܭଌ • swiftlintʢͱ͘ʹ cyclomatic_complexityʣ • Swinject

Slide 34

Slide 34 text

͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠