Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
FBSnapshotTestCaseに助けられた話
Search
yutu
February 09, 2017
Programming
1
2.7k
FBSnapshotTestCaseに助けられた話
Kyobashi.swift x AKIBA.swift 合同勉強会
2017/02/08
yutu
February 09, 2017
Tweet
Share
More Decks by yutu
See All by yutu
コスパの良いiOS開発を求めて
yutu
1
2k
Other Decks in Programming
See All in Programming
Kiroの仕様駆動開発から見えてきたAIコーディングとの正しい付き合い方
clshinji
1
200
AIコーディングAgentとの向き合い方
eycjur
0
260
ProxyによるWindow間RPC機構の構築
syumai
3
1.1k
TanStack DB ~状態管理の新しい考え方~
bmthd
2
490
基礎から学ぶ大画面対応(Learning Large-Screen Support from the Ground Up)
tomoya0x00
0
250
さようなら Date。 ようこそTemporal! 3年間先行利用して得られた知見の共有
8beeeaaat
2
1.4k
CSC305 Summer Lecture 12
javiergs
PRO
0
140
プロポーザル駆動学習 / Proposal-Driven Learning
mackey0225
2
940
複雑なドメインに挑む.pdf
yukisakai1225
5
990
【第4回】関東Kaggler会「Kaggleは執筆に役立つ」
mipypf
0
1.1k
「手軽で便利」に潜む罠。 Popover API を WCAG 2.2の視点で安全に使うには
taitotnk
0
770
testingを眺める
matumoto
1
130
Featured
See All Featured
A Tale of Four Properties
chriscoyier
160
23k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
51
5.6k
The Pragmatic Product Professional
lauravandoore
36
6.9k
Optimizing for Happiness
mojombo
379
70k
4 Signs Your Business is Dying
shpigford
184
22k
Scaling GitHub
holman
463
140k
Designing for Performance
lara
610
69k
Designing Dashboards & Data Visualisations in Web Apps
destraynor
231
53k
Documentation Writing (for coders)
carmenintech
74
5k
Fireside Chat
paigeccino
39
3.6k
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
31
2.5k
Art, The Web, and Tiny UX
lynnandtonic
302
21k
Transcript
FBSnapshotTestCase ʹॿ͚ΒΕͨ Yuki Hirai Kyobashi.swift x AKIBA.swift ߹ಉษڧձ 2017/02/08
ฏҪ༞थͩʔʂ • 31ࡀɻطࠗ • དྷि͙Β͍ʹ͕ੜ͢Δ • RMP • iOS Engineer
FBSnapshotTestCaseʹ ॿ͚ΒΕͨΛ͠·͢
͙Β͍લɾɾɾ Φοεʂ࣍ͷϓϩδΣΫτདྷͨͧʔ ແअؾˁ طࠗˁ
͙Β͍લɾɾɾ Φοεʂ࣍ͷϓϩδΣΫτདྷͨͧʔ 0,ͬ͢ʂͲ͏͍͏ํͰ࡞͍͖ͬͯ·͢ʁ ແअؾˁ طࠗˁ
͙Β͍લɾɾɾ ͦ͏ͩͳɾɾɾ ແअؾˁ طࠗˁ
͙Β͍લɾɾɾ ࠷ۙ$MFBO4XJGUྑͦ͞͏ͩͱࢥͬͯ͊͞ɺͦΕ࠾ ༻ͭͭ͠#PMUTͱ͔͍͍ײ͡ʹͬͯॻ͜͏ͱࢥͬ ͯͯɺ͋ͬɺ͋ͱΧόϨοδશମͰͱΓͨ ͍ͱ͜ΖͩΑͶɻ͋ͱTXJGUMJOUΨονΨνʹೖΕ ͯ͞ʂ͍͍ײ͡͡ΌͶʂʁ4XJOKFDUಋೖ͠Α ͏ʂྃ݅ʹՃ͠Α͏ʂʂ ແअؾˁ طࠗˁ
͙Β͍લɾɾɾ ࠷ۙ$MFBO4XJGUྑͦ͞͏ͩͱࢥͬͯ͊͞ɺͦΕ࠾ ༻ͭͭ͠#PMUTͱ͔͍͍ײ͡ʹͬͯॻ͜͏ͱࢥͬ ͯͯɺ͋ͬɺ͋ͱΧόϨοδશମͰͱΓͨ ͍ͱ͜ΖͩΑͶɻ͋ͱTXJGUMJOUΨονΨνʹೖΕ ͯ͞ʂ͍͍ײ͡͡ΌͶʂʁ4XJOKFDUಋೖ͠Α ͏ʂྃ݅ʹՃ͠Α͏ʂʂ ͑ͬʁ3Y4XJGU͕ྑ͍ͳɾɾɾͰ࣮͋ ΔΈ͍ͨͩ͠ͳ͊ɾɾ͔͠ͳΜָ͔ͦ͠ ͏ʹͯ͠Δ͠ɾɾɾ·͍͔͊ͬ
͍ʂʂ ແअؾˁ طࠗˁ
࣮ணख طࠗˁ
࣮ணख طࠗˁ ྑ͍ײ͡
͠Βͯ͘͠ɾɾɾ ɾɾɾ ແअؾˁ طࠗˁ
͠Βͯ͘͠ɾɾɾ ͬͺ3Y4XJGUͰ͍͜͏ʂX ແअؾˁ طࠗˁ
͠Βͯ͘͠ɾɾɾ ͬͺ3Y4XJGUͰ͍͜͏ʂX ແअؾˁ طࠗˁ ͓͍X
͜͏ͯ͠ॻ͖͕͑ ͡·ͬͨ
͔͠͠৺ແ༻ʂ
͜Μͳ͜ͱ͋Ζ͏͔ͱ ൿࡦΛ४උ͍ͯͨ͠ͷͩʂ
FBSnapshotTestCase • https://github.com/facebook/ios-snapshot- test-case • UIView/CALayer ͷεφοϓγϣοτͱ͋Β͔ ͡Ί༻ҙͨ͠ը૾Λൺֱͯ͠ɺҰக͠ͳ͍ ߹ςετࣦഊʹͰ͖Δ •
XCTestCaseͷαϒΫϥε
Nimble-Snapshots • https://github.com/ashfurrow/Nimble- Snapshots • FBSnapshotTestCaseΛ Nimble ͷ matcher ͱ
ͯ͠ఆٛ
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")
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()) } } } ʩ
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()) } } } ʩ ύγϟϦʂ
Nimble-Snapshots
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()) } } } ʩ
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()) } } } ʩ
DEMO https://github.com/yutu/ScreenShotExample
ແࣄΓӽ͑ͨʂ
͔͠͠ຊʹා͍ͷɾɾɾ
࣮ɾɾɾ ແअؾˁ طࠗˁ
ͲͬͪͰͨ͠ ແअؾˁ طࠗˁ
͔ͯ͘͠ FBSnapshotTestCaseʹ ॿ͚ΒΕ·ͨ͠
FBSnapshotTestCase ͷ͍͍ͱ͜Ζ • ΞʔΩςΫνϟOSSʹґଘ͠ͳ͍ςετ͕ॻ͚Δ • අ༻ରޮՌ͕ߴ͍ • XcodeͰΧόϨοδΛܭଌͰ͖Δ • PRͰεφοϓγϣοτͷdiff͕ݟΕΔ
• CircleCI Ͱී௨ʹಈ͘ • TDDͰ͖Δ
ؾΛ͚͍ͭͨ͜ͱ • ϢχοτςετΛॻ͔ͳͯ͘ྑ͍Θ͚Ͱͳ͍ • IT͢Δ߹࣮ߦ͢ΔσόΠεͱiOS verʹҙ • UIΛར༻ͨ͠ςετ ≠ E2Eςετ
• ʮͳͥɾͲ͜·ͰςετΛॻ͔͘ʢॻ͔ͳ͍ ͔ʣʯΛৗʹҙࣝ͢Δ
ʴΞϧϑΝ • CircleCI + fastlane (scan/slather) ͰΧόϨο δܭଌ • swiftlintʢͱ͘ʹ
cyclomatic_complexityʣ • Swinject
͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠