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

Xcode PreviewsからSnapshotテストを自動生成する

D2f212ce418f3daa29c23914c9b6892b?s=47 kenmaz
December 18, 2020

Xcode PreviewsからSnapshotテストを自動生成する

2020/12/18に開催された「merpay Tech Talk ~for iOS Engineers~」というイベントでのスライドです。

https://mercari.connpass.com/event/197804/?utm_campaign=event_message_to_selected_participant&utm_source=notifications&utm_medium=email&utm_content=title_link

本発表の内容の詳細については下記のブログ記事を参照してください。
https://engineering.mercari.com/blog/entry/20201204-1f94b9dca2/

D2f212ce418f3daa29c23914c9b6892b?s=128

kenmaz

December 18, 2020
Tweet

Transcript

  1. 9DPEF1SFWJFXT͔Β 4OBQTIPUςετΛࣗಈੜ੒͢Δ LFONB[ !NFSQBZ .FSQBZ5FDI5BMLGPSJ04&OHJOFFST  NFSQBZ@UFDIUBML

  2. ࠓ೔͓࿩͢͠Δ಺༰ w 9DPEF1SFWJFXTΛ࢖ͬͨ7JFXͷ։ൃͷ֓ཁ w 4OBQTIPUςετͷ֓ཁͱ՝୊ w 9DPEF1SFWJFXT͔Β4OBQTIPUςετΛࣗಈੜ੒͢Δ w σϞ

  3. 9DPEF1SFWJFXTΛ࢖ͬͨ7JFXͷ։ൃ

  4. 9DPEF1SFWJFXTͰ6*,JUϕʔεͷΞϓϦ։ൃΛޮ཰Խ͢ΔJ04%$+BQBO

  5. final class ConfirmationStateView: UIView, .. { struct Input { let

    screenName: String } private lazy var screenNameLabel: UILabel = { let label = UILabel(...) .... return label }() func apply(input: Input) { screenNameLabel.text = input.screenName ... } 7JFXίʔυ 6*7JFX
  6. struct Wrapper: UIViewRepresentable { let input: ConfirmationStateView.Input init(input: ConfirmationStateView.Input) {

    self.input = input } func makeUIView(...) -> ConfirmationStateView { let view = ConfirmationStateView() view.apply(input: input) return view } func updateUIView(...) { } } 7JFXίʔυΛ4XJGU6*ͷ7JFXͱͯ͠ϥοϓ
  7. struct ConfirmationStateViewPreview: PreviewProvider { static var previews: some View {

    Group { Wrapper( input: .init(screenName: "୹໊͍લ") ) Wrapper( input: .init(screenName: "গ͠௕໊͍લগ͠௕໊͍લ") ) Wrapper( input: .init(screenName: "௕໊͍લ௕໊͍લ௕໊͍લ௕໊͍લ௕໊͍લ") ) 1SFWJFXίʔυ 1SFWJFX1SPWJEFS
  8. None
  9. 4OBQTIPUςετ IUUQTHJUIVCDPNQPJOUGSFFDPTXJGUTOBQTIPUUFTUJOH

  10. import XCTest import SnapshotTesting class ConfirmationStateViewTests: XCTestCase { func testShortName()

    { let view = ConfirmationStateView() let input = ConfirmationStateView.Input( screenName: "୹໊͍લ" ) view.apply(input: input) assertSnapshot(matching: view, as: .image) } func testMiddleName() { ... screenName: "গ͠௕໊͍લগ͠௕໊͍લ" ... assertSnapshot(matching: view, as: .image) } func testLongName() { ... screenName: "௕໊͍લ௕໊͍લ௕໊͍લ௕໊͍લ௕໊͍લ" ... assertSnapshot(matching: view, as: .image) } 4OBQTIPUςετίʔυ 7JFXΛΩϟϓνϟͯ͠ ࢀরը૾ϑΝΠϧͱͯ͠ อଘ ൺֱ ࢀরը૾
  11. 4OBQTIPUςετͷ՝୊ w 4OBQTIPUςετʹݶΒͣɺςετίʔυͷهड़ʹ͸ίετ͕͔͔Δ

  12. import XCTest import SnapshotTesting class ConfirmationStateViewTests: XCTestCase { func testShortName()

    { let view = ConfirmationStateView() let input = ConfirmationStateView.Input( screenName: "୹໊͍લ" ) view.apply(input: input) assertSnapshot(matching: view, as: .image) } func testMiddleName() { ... screenName: "গ͠௕໊͍લগ͠௕໊͍લ" ... assertSnapshot(matching: view, as: .image) } func testLongName() { ... screenName: "௕໊͍લ௕໊͍લ௕໊͍લ௕໊͍લ௕໊͍લ" ... assertSnapshot(matching: view, as: .image) } 1SFWJFXίʔυ 4OBQTIPUςετίʔυ ಉ͡Α͏ͳ͜ͱΛ΍͍ͬͯΔ
  13. 1SFWJFXίʔυ 4OBQTIPUςετίʔυ 1SFWJFXର৅ͷ7JFXΛɺͦͷ·· 4OBQTIPUςετͷର৅ͱͯ͠ѻ͏

  14. 1SFWJFXίʔυʹର͢Δ4OBQTIPUςετ class PreviewSnapshotTests: XCTestCase { func testConfirmationStateViewPreview() { for preview

    in ConfirmationStateViewPreview._allPreviews { assertSnapshot(matching: preview.content, as: .image) } } } FYUFOTJPO@1SFWJFX1SPWJEFS\ QVCMJDTUBUJDWBS@BMM1SFWJFXT<4XJGU6*@1SFWJFX>\HFU^ QVCMJDTUSVDU@1SFWJFX\ QVCMJDWBSDPOUFOU4XJGU6*"OZ7JFX\HFU^ 1SFWJFXίʔυதʹఆٛ͞ΕͨશͯͷϓϨϏϡʔ ֤ϓϨϏϡʔͷදࣔ಺༰Λࣔ͢4XJGU6*ͷ7JFX
  15. class PreviewSnapshotTests: XCTestCase { func testConfirmationStateViewPreview() { for preview in

    ConfirmationStateViewPreview._allPreviews { assertSnapshot(matching: preview.content, as: .image) } } }
  16. 4PVSDFSZʹΑΔ4OBQTIPUςετίʔυͷࣗಈੜ੒ class PreviewSnapshotTests: XCTestCase { {% for type in types.based.PreviewProvider

    %} func test{{type.name}}() { for preview in T._allPreviews { assertSnapshot(matching: preview.content, as: .image) } } {% endfor %} } TUFODJMςϯϓϨʔτ IUUQTHJUIVCDPNLS[ZT[UPG[BCMPDLJ4PVSDFSZ UZQFTCBTFE1SFWJFX1SPWJEFS 1SFWJFX1SPWJEFSϓϩτίϧʹద߹ͨ͠શͯͷܕΛऔಘ
  17. 9DPEF1SFWJFXT͔Β4OBQTIPUςετΛࣗಈੜ੒͢Δ

  18. σϞ

  19. None
  20. $*΁ͷ૊ΈࠐΈ 1VTI࣌ʹ$*্Ͱ4OBQTIPUςετΛࣗಈ࣮ߦ

  21. $*΁ͷ૊ΈࠐΈ (JUIVCϥϕϧΛ࢖ͬͨ࠶Ωϟϓνϟ (JUIVCϥϕϧΛઃఆ $*্Ͱࢀরը૾Λ࠶౓Ωϟϓνϟ ࠩ෼͕͋Ε͹1VMM3FRVFTU࡞੒ ։ൃऀ͕֬ೝͯ͠໰୊ͳ͚Ε͹Ϛʔδ

  22. ·ͱΊ w 1SFWJFXίʔυΛ΋ͱʹ4OBQTIPUςετΛࣗಈੜ੒͢Δ࢓૊ΈΛߏங w 1SFWJFXίʔυΛ͖ͪΜͱॻ͚͹ॻ͘΄Ͳɺςετέʔε͕ॆ࣮͢Δ w Ұੴೋௗͳ࢓૊Έ w 6*,JU4XJGU6*͍ͣΕʹ΋ద༻Ͱ͖Δ w

    .FSQBZ"EWFOU$BMFOEBSͷهࣄ΋͝ࢀর͍ͩ͘͞