$30 off During Our Annual Pro Sale. View Details »

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

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/

kenmaz

December 18, 2020
Tweet

More Decks by kenmaz

Other Decks in Programming

Transcript

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

    NFSQBZ@UFDIUBML

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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ͱͯ͠ϥοϓ

    View Slide

  7. struct ConfirmationStateViewPreview: PreviewProvider {
    static var previews: some View {
    Group {
    Wrapper(
    input: .init(screenName: "୹໊͍લ")
    )
    Wrapper(
    input: .init(screenName: "গ͠௕໊͍લগ͠௕໊͍લ")
    )
    Wrapper(
    input: .init(screenName: "௕໊͍લ௕໊͍લ௕໊͍લ௕໊͍લ௕໊͍લ")
    )
    1SFWJFXίʔυ 1SFWJFX1SPWJEFS

    View Slide

  8. View Slide

  9. 4OBQTIPUςετ
    IUUQTHJUIVCDPNQPJOUGSFFDPTXJGUTOBQTIPUUFTUJOH

    View Slide

  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ΛΩϟϓνϟͯ͠
    ࢀরը૾ϑΝΠϧͱͯ͠
    อଘ
    ൺֱ
    ࢀরը૾

    View Slide

  11. 4OBQTIPUςετͷ՝୊
    w 4OBQTIPUςετʹݶΒͣɺςετίʔυͷهड़ʹ͸ίετ͕͔͔Δ

    View Slide

  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ςετίʔυ
    ಉ͡Α͏ͳ͜ͱΛ΍͍ͬͯΔ

    View Slide

  13. 1SFWJFXίʔυ 4OBQTIPUςετίʔυ
    1SFWJFXର৅ͷ7JFXΛɺͦͷ··
    4OBQTIPUςετͷର৅ͱͯ͠ѻ͏

    View Slide

  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

    View Slide

  15. class PreviewSnapshotTests: XCTestCase {
    func testConfirmationStateViewPreview() {
    for preview in ConfirmationStateViewPreview._allPreviews {
    assertSnapshot(matching: preview.content, as: .image)
    }
    }
    }

    View Slide

  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ϓϩτίϧʹద߹ͨ͠શͯͷܕΛऔಘ

    View Slide

  17. 9DPEF1SFWJFXT͔Β4OBQTIPUςετΛࣗಈੜ੒͢Δ

    View Slide

  18. σϞ

    View Slide

  19. View Slide

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

    View Slide

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

    View Slide

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

    View Slide