-
[iOS] SnapshotTesting with SwiftUI (v1.11.0)iOS 2023. 4. 5. 08:48
SnapshotTesting
SnapshotTesting์ ํตํด UI์ ์ ํจ์ฑ์ ๊ฒ์ฌํ๊ณ , ์ฝ๋ ๋ณ๊ฒฝ์ด ๋น๋ํ UI์ ์ํฅ์ ๋ฏธ์น์ง ์์๋์ง ํ์ธํ๋ค.
SnapshotTesting์ด๋?
Snapshot Test๋ฅผ ์ฌ์ฉํ๋ฉด ํ ์คํธ ์์ ์ UI์ ์ค๋ ์ท์ ๊ธฐ์ค์ (baseline)์ด๋ผ๊ณ ํ๋ ์ ํจํ UI ์ค๋ ์ท๊ณผ ๋น๊ตํ์ฌ UI๋ฅผ ๊ฒ์ฆํ ์ ์์ต๋๋ค.
ํ ์คํธ ๋ฌ๋๋ ํ์ฌ ์ค๋ ์ท์ ๊ธฐ์ค ์ค๋ ์ท๊ณผ ๋น๊ตํ๊ณ , ์ค๋ ์ท ๊ฐ์ ์ฐจ์ด๊ฐ ์์ ๊ฒฝ์ฐ, UI๋ ๋ณ๊ฒฝ๋ ๊ฒ์ด๋ฏ๋ก ํ ์คํธ๊ฐ ์คํจํฉ๋๋ค.
GitHub - pointfreeco/swift-snapshot-testing: ๐ธ Delightful Swift snapshot testing.
๐ธ Delightful Swift snapshot testing. Contribute to pointfreeco/swift-snapshot-testing development by creating an account on GitHub.
github.com

์ถ์ฒ : Raywenderlich Snapshot Testing Strategies
SnapshotTesting ํ๋ ์์ํฌ๋ ์ด๋ฏธ์ง ์ ๋ต (image strategy) ๋๋ ์ฌ๊ท ์ค๋ช ์ ๋ต (recursive description strategy)์ ์ฌ์ฉํ์ฌ UIView, UIViewController๋ค์ ํ ์คํธ๋ฅผ ์ง์ํฉ๋๋ค.
์ด๋ฏธ์ง ์ ๋ต (Image Strategy)์ UI์ ์ค์ ์ค๋ ์ท์ ์ด๋ฏธ์ง ํ์ผ๋ก ๊ฐ์ ธ์จ ๋ค์, ์ด๋ฏธ์ง๋ฅผ ํฝ์ ๋จ์๋ก ๋น๊ตํฉ๋๋ค. ์ด๋ฏธ์ง๊ฐ ๋ค๋ฅด๋ฉด UI๊ฐ ๋ณ๊ฒฝ๋ ๊ฒ์ด๊ณ , ํ ์คํธ๊ฐ ์คํจํฉ๋๋ค.
์ฌ๊ท์ ์ค๋ช ์ ๋ต (Recursive description Strategy)์ ๋ทฐ ๊ณ์ธต ๊ตฌ์กฐ์ ๋ฌธ์์ด description์ ๋น๊ตํฉ๋๋ค. description์ด ๋ณ๊ฒฝ๋ ๊ฒฝ์ฐ, UI๊ฐ ๋ฌ๋ผ์ง๊ณ , ํ ์คํธ๊ฐ ์คํจํฉ๋๋ค.
๋ง์ง๋ง์ผ๋ก, SnapshotTesting์ ๋ทฐ์ปจํธ๋กค๋ฌ ๊ณ์ธต์ ์ผ๋ฐ ํ ์คํธ๋ก ์์ฑํ๋ ๊ณ์ธต ์ ๋ต (hierarchy strategy)์ ์ ๊ณตํฉ๋๋ค. ๋ทฐ์ปจํธ๋กค๋ฌ ๊ณ์ธต ๊ตฌ์กฐ๋ฅผ ๋ณ๊ฒฝํ๋ฉด ํ ์คํธ๊ฐ ์คํจํ๊ณ , ์ด๋ UIViewController์์๋ง ์๋ํฉ๋๋ค.
Using the Image Strategy
UIViewController๋ฅผ ํ ์คํธํ๋ฉด ์ ๊ณตํ๋ ๋ค์ํ ์ต์
- drawHierarchyInKeyWindow: Bool = false
- ์๋ฎฌ๋ ์ดํฐ์ Key Window์์ ๋ทฐ๋ฅผ ๋ ๋๋งํ๊ณ , appearance, visual effects ์ฌ์ฉ
- on: ViewImageConfig
- ViewImageConfig ์ต์ ์ผ๋ก ๋ค์ํ ์ฅ์น ์ ํ ๊ฐ๋ฅ
- precision: Float = 1
- ํ ์คํธ ํต๊ณผ๋ฅผ ์ํด ์ผ์นํด์ผ ํ๋ ํฝ์ ์ ํผ์ผํธ๋ก, ๊ธฐ๋ณธ์ ์ผ๋ก 1์ด๊ณ , ์ด๋ ์ฆ ํฝ์ ์ 100%๊ฐ ์ผ์นํด์ผํจ์ ์๋ฏธ
- size: CGSize = nil
- ํ ์คํธ ๋ทฐ์ ์ค๋ ์ท์ ์์ฑํ๊ธฐ ์ํ ๋ทฐ ํฌ๊ธฐ
- traits: UITraitCollection = .init()
- ํ ์คํธํ ๋ง์ ํน์ฑ๋ค ์ง์ ๊ฐ๋ฅ
๊ธฐ๋ณธ ์ค๋ ์ท ์์ฑํ๊ธฐ
์ฒ์์ผ๋ก ํ ์คํธ ์ผ์ด์ค๋ฅผ ์์ฑํ๋ฉด, ์์ง ๊ธฐ์ค ์ค๋ ์ท (baseline snapshot)์ด ์๊ธฐ ๋๋ฌธ์ ํ ์คํธ๊ฐ ์คํจํฉ๋๋ค.
์ฒ์ ์คํํ๋ฉด, SnapshotTesting์ ๊ธฐ์ค ์ค๋ ์ท์ ํ๋ก์ ํธ ๋๋ ํ ๋ฆฌ์ ์ ์ฅํฉ๋๋ค.

์์ ๊ฐ์ด Snapshots ํด๋๊ฐ ์์ฑ๋๊ณ , ๊ทธ ์์ ์๋์ผ๋ก ์ด๋ฏธ์ง๊ฐ ์ ์ฅ๋๋ค! ์ด๋ ๊ฒ ๊ธฐ์ค ์ค๋ ์ท์ด ์์ฑ๋๊ณ ๋๋ฉด, ์ดํ์ ์งํ๋๋ ํ ์คํธ ์คํ์ ์ ์ค๋ ์ท์ ๊ฐ์ ธ์์ ๊ธฐ์ค ์ค๋ ์ท๊ณผ ๋น๊ตํฉ๋๋ค.

๊ธฐ์ค ์ค๋ ์ท์ด ์์ฑ๋์์ ๋ ๋์ค๋ ํ ์คํธ ๊ฒฐ๊ณผ 
์ค๋ฅ ๋ฉ์์ง ์ฒ์ ํ ์คํธ ์คํ ์, ํ ์คํธ ๊ฒฐ๊ณผ์ ๋น๊ตํ ์ฐธ์กฐ ์ค๋ ์ท์ด ์๊ธฐ ๋๋ฌธ์ ํ ์คํธ๊ฐ ์คํจํฉ๋๋ค.
ํ ์คํธ๊ฐ ์ฒ์ ์คํ๋๋ฉด, ๊ทธ๋์์ผ SnapshotTesting ํ๋ ์์ํฌ๊ฐ ๊ธฐ์ค ์ค๋ ์ท์ ์์ฑํฉ๋๋ค.
์ฌ๊ธฐ์ image strategy๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด ํ ์คํธํ๋ ๊ฒ์ ์ค์ UI ๋ทฐ์ ๊ทธ๋ํฝ ํํ์ ๊ณ ์ ์ฝํ ์ธ ํฌ๊ธฐ (intrinsic content size)๋ก ์ ์ฅํ๋ค๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค.
๊ธฐ๋ณธ ์ค๋ ์ท์ ์์ฑํ๊ณ ๋ค์ ํ ์คํธ๋ฅผ ์คํํ๋ฉด ์ด์ ํ ์คํธ์ ์ฑ๊ณตํฉ๋๋ค!

UI ๋ณ๊ฒฝ ์ ๊ธฐ๋ณธ ์ค๋ ์ท ์ ๋ฐ์ดํธํ๊ธฐ
๋ทฐ์ ์ฝ๋๊ฐ ๋ณ๊ฒฝ๋์ง ์๋ ํ, ํ ์คํธ๋ ์ฑ๊ณตํฉ๋๋ค.
๋ง์ฝ ๋ทฐ๊ฐ ๋ณ๊ฒฝ๋๋ค๋ฉด, SnapshotTesting์ ์ด๋ฐ ๋ฉ์์ง๋ฅผ ํ์ํฉ๋๋ค.
Newly-taken snapshot does not match reference.
์ฆ, ๊ธฐ์ค ์ค๋ ์ท๊ณผ ์๋ก์ด ์ค๋ ์ท์ด ์ผ์นํ์ง ์๋ค๋ ๊ฒ์ธ๋ฐ, ์ค๋ฅ ๋ฉ์์ง์๋ ๊ธฐ์ค ์ค๋ ์ท๊ณผ ์ ์ค๋ ์ท์ ๊ฒฝ๋ก๊ฐ ํฌํจ๋์ด์์ต๋๋ค!
Finder์์ Command + Shift + G๋ฅผ ๋๋ฌ์ ๊ฒฝ๋ก๋ก ์ด๋ํ๊ธฐ!
UI๊ฐ ๋ณ๊ฒฝ๋์๊ธฐ ๋๋ฌธ์, ํ ์คํธ๊ฐ ์ฑ๊ณตํ๋ ค๋ฉด ๊ธฐ์ค ์ค๋ ์ท์ ์ ๋ฐ์ดํธํด์ผ ํฉ๋๋ค.
assertSnapShot(matching:as:) ์์ ๋ค์ ์ฝ๋๋ฅผ ์ถ๊ฐํฉ๋๋ค.
isRecording = trueisRecording์ true๋ก ์ค์ ํ๋ฉด, SnapshotTesting ํ๋ ์์ํฌ์ ์ ๊ธฐ์ค ์ค๋ ์ท์ ์์ฑํ ๊ฒ์ ์๋ฆฌ๋ ๊ฒ์ ๋๋ค.

์ฝ๋๋ฅผ ์คํํ๋ฉด, ํ ์คํธ๋ ์คํจํ์ง๋ง SnapshotTesting์ ํ ์คํธ๋ฅผ ์ฒ์ ์คํํ์ ๋์ ๋ง์ฐฌ๊ฐ์ง๋ก ์๋ก์ด ๊ธฐ์ค ์ค๋ ์ท์ ์ ์ฅํฉ๋๋ค.
์ค๋ฅ ๋ฉ์์ง๋ฅผ ๋ณด๋ฉด Record mode is on ์ด๋ผ๋ ๋ฉ์์ง๋ฅผ ๋ณผ ์ ์์ต๋๋ค.

isRecording = true๋ฅผ ์ค์ ํ๋ฉด, ํ ์คํธ๋ ์คํจํ๊ณ ์๋ก์ด ๊ธฐ์ค ์ค๋ ์ท์ด ์์ฑ๋๋ค. 
์์ธ ์ค๋ฅ ๋ฉ์์ง ์ด์ ์๋ก์ด ๊ธฐ์ค ์ค๋ ์ท์ด ์์ฑ๋์์ผ๋ฏ๋ก, isRecording = true๋ฅผ ์ ๊ฑฐํฉ๋๋ค.
๊ทธ๋ฆฌ๊ณ ๋ค์ ํ ์คํธ๋ฅผ ์คํํ๋ฉด, ํ ์คํธ๊ฐ ์ฑ๊ณตํฉ๋๋ค.

ํ ์คํธ ์ฑ๊ณต ์์ธ ๋ทฐ ํ ์คํธํ๊ธฐ
๋ทฐ์ปจํธ๋กค๋ฌ๋ฅผ ์ฌ์ฉํ๋ฉด, ๋ค์ํ ์์ดํฐ ๋ฒ์ , ํ๋ฉด ๋ฐฉํ, ๊ธฐ๊ธฐ ํ์ ์ ์ฌ์ฉํ๋๋ก ํ ์คํธ๋ฅผ ์ค์ ํ ์ ์๊ณ , ๋คํฌ ๋ชจ๋์์ ์ฑ์ด ์ด๋ป๊ฒ ์๋ํ๋์ง ํ์ธํ ์ ์์ต๋๋ค.
setUpWithError ๋ด์์ ์ธ์คํด์ค๋ฅผ ์์ฑํ๋ฉด ํ ์คํธ ์ผ์ด์ค์์ ๋งค๋ฒ ํ ์คํธ ์ค์ธ ์์คํ ์ ๋ง๋ค ํ์๊ฐ ์์ต๋๋ค.
ํน์ ์์ดํฐ ๋ฒ์ ํ ์คํธ
/// Testing for Specific iPhone Versions func testBookDetailViewOniPhone() throws { assertSnapshot( matching: viewController, as: .image(on: .iPhoneX) // SnapshotTesting์๊ฒ ๋ทฐ์ปจํธ๋กค๋ฌ๋ฅผ iPhoneX ์คํฌ๋ฆฐ ์ฌ์ด์ฆ๋ก ๋ ๋๋งํ๋๋ก ์ง์ ) }ํน์ ๊ธฐ๊ธฐ ๋ฐฉํฅ ํ ์คํธ
/// Testing for Device Orientation func testBookDetailViewOniPhoneLandscape() throws { assertSnapshot( matching: viewController, as: .image(on: .iPhoneX(.landscape)) // ๊ฐ๋ก ๋ฐฉํฅ์ผ๋ก UI ํ ์คํธ ) } /// Testing for Device Orientation func testBookDetailViewOniPadPortrait() throws { assertSnapshot( matching: viewController, as: .image(on: .iPadPro11(.portrait)) ) }๋คํฌ ๋ชจ๋ ํ ์คํธ
/// Testing for Dark Mode func testBookDetailViewOniPhoneDarkMode() throws { let traitDarkMode = UITraitCollection(userInterfaceStyle: .dark) assertSnapshot( matching: viewController, as: .image(on: .iPhoneX, traits: traitDarkMode) ) }ํ ์คํธ๊ฐ ๋ชจ๋ ์ฑ๊ณตํ๊ณ , ์ค๋ ์ท์ด ์ ์ฅ๋ ๊ฒ ํ์ธ ๊ฐ๋ฅ!
Snapshot Testing Tutorial for SwiftUI: Getting Started
Learn how to test your SwiftUI iOS views in a simple and fast way using snapshot testing.
www.kodeco.com
'iOS' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
- drawHierarchyInKeyWindow: Bool = false