Slide 1

Slide 1 text

ྑ͍ςετίʔυͷͨΊʹ ѱ͍ςετίʔυΛཧղ͢Δ 2024.6.24 גࣜձࣾΩϡϦΦγςΟιϑτ΢ΣΞ @yimajo #ྑ͍ςετίʔυ ෆ҆ఆͳςετฤJ04ΞϓϦ։ൃϢχοτςετͷ৔߹ 🐲 🤖 ϤϩγΫ ͓ئ͍͠·͢

Slide 2

Slide 2 text

͔ΜͨΜͳ ࣗݾ঺հ

Slide 3

Slide 3 text

• ৬ྺ • ৽ଔ: ൒ಋମϝʔΧʔͰLSIΛςετ͢ΔΤϯδχΞ • Web΍ϞόΠϧΞϓϦ։ൃͷιϑτ΢ΣΞΤϯδχΞ • Now!: ϓϩα΢φʔ݉ιϑτ΢ΣΞΤϯδχΞ

Slide 4

Slide 4 text

ຊ୊ͷલʹ ࠓճ࿩͢Ϣχοτςετʹ͍ͭͯͷఆٛ ྑ͍ςετͱ͸Կ͔ʹ͍ͭͯ ͳͥςετΛॻ͘ͷ͔

Slide 5

Slide 5 text

Ҿ༻: Google Testing Blog / Test Size https://testing.googleblog.com/2010/12/test-sizes.html ಛ௃ Small Medium Large ωοτϫʔΫΞΫηε ͍͍͑ ϩʔΧϧϗετͷΈ ͸͍ σʔλϕʔε ͍͍͑ ͸͍ ͸͍ ϑΝΠϧγεςϜ΁ͷΞΫηε ͍͍͑ ͸͍ ͸͍ ֎෦γεςϜΛ࢖༻͢Δ ͍͍͑ ਪ঑͞Εͳ͍ ͸͍ ෳ਺ͷεϨου ͍͍͑ ͸͍ ͸͍ Sleep໋ྩ ͍͍͑ ͸͍ ͸͍ γεςϜϓϩύςΟ ͍͍͑ ͸͍ ͸͍ ੍ݶ࣌ؒ(ඵ) 60 300 900 ఆٛ: Small, Medium, Large Testͷఆٛ 🐲

Slide 6

Slide 6 text

Ҿ༻: Google Testing Blog / Test Size https://testing.googleblog.com/2010/12/test-sizes.html ಛ௃ Small Medium Large ωοτϫʔΫΞΫηε ͍͍͑ ϩʔΧϧϗετͷΈ ͸͍ σʔλϕʔε ͍͍͑ ͸͍ ͸͍ ϑΝΠϧγεςϜ΁ͷΞΫηε ͍͍͑ ͸͍ ͸͍ ֎෦γεςϜΛ࢖༻͢Δ ͍͍͑ ਪ঑͞Εͳ͍ ͸͍ ෳ਺ͷεϨου ͍͍͑ ͸͍ ͸͍ Sleep໋ྩ ͍͍͑ ͸͍ ͸͍ γεςϜϓϩύςΟ ͍͍͑ ͸͍ ͸͍ ੍ݶ࣌ؒ(ඵ) 60 300 900 ఆٛ: Small, Medium, Large Testͷఆٛ 🐲

Slide 7

Slide 7 text

Ҿ༻: Google Testing Blog / Test Size https://testing.googleblog.com/2010/12/test-sizes.html ಛ௃ Small Medium Large ωοτϫʔΫΞΫηε ͍͍͑ ϩʔΧϧϗετͷΈ ͸͍ σʔλϕʔε ͍͍͑ ͸͍ ͸͍ ϑΝΠϧγεςϜ΁ͷΞΫηε ͍͍͑ ͸͍ ͸͍ ֎෦γεςϜΛ࢖༻͢Δ ͍͍͑ ਪ঑͞Εͳ͍ ͸͍ ෳ਺ͷεϨου ͍͍͑ ͸͍ ͸͍ Sleep໋ྩ ͍͍͑ ͸͍ ͸͍ γεςϜϓϩύςΟ ͍͍͑ ͸͍ ͸͍ ੍ݶ࣌ؒ(ඵ) 60 300 900 ఆٛ: Small, Medium, Large Testͷఆٛ ʮ͜ͷࢿྉͷ࿩͸ Small/Meidum ʹ͍ͭͯͷςετͷ࿩Ͱ͢ʯ 🐲

Slide 8

Slide 8 text

ఆٛ: ྑ͍ςετͱ͸ʁ ιϑτ΢ΣΞͷ࣭Λ ʢ্͛Δͱ͍͏͔ʣམͱ͞ͳ͍ͨΊʹ ։ൃऀͷ໾ʹཱͭࣗಈԽ 🐲

Slide 9

Slide 9 text

ఆٛ: ྑ͍ςετͱ͸ʁ ιϑτ΢ΣΞͷ࣭Λ ʢ্͛Δͱ͍͏͔ʣམͱ͞ͳ͍ͨΊʹ ։ൃऀͷ໾ʹཱͭࣗಈԽ ʮ΋ͪΖΜؒ઀తʹΞϓϦΛ࢖ͬͯ͘ΕΔϢʔβʔͷ໾ʹཱͭͱ͍͏͜ͱͰ͢ʯ 🐲

Slide 10

Slide 10 text

ςετͷ۩ମతͳϝϦοτ Ҿ༻: ॻ੶ʮGoogle ͷιϑτ΢ΣΞΤϯδχΞϦϯάʯ • σόοά࣌ؒͷݮগ • มߋ΁ͷ৴པͷ૿େ • υΩϡϝϯςʔγϣϯͷվળ • ϨϏϡʔͷ୯७Խ • ࢥྀʹ෋Ήઃܭ • ߴ଎Ͱߴ඼࣭ͳϦϦʔε

Slide 11

Slide 11 text

ςετͷ۩ମతͳϝϦοτ Ҿ༻: ॻ੶ʮ୯ମςετͷߟ͑ํ/࢖͍ํʯ • ςετΛॻ͘ͱઃܭ͕ྑ͘ͳΔ • ʢϓϩμΫτΛʣ࣋ଓՄೳͳ΋ͷʹ͢Δ

Slide 12

Slide 12 text

ςετͷ۩ମతͳϝϦοτ • ςετΛॻ͘ͱઃܭ͕ྑ͘ͳΔ • ʢϓϩμΫτΛʣ࣋ଓՄೳͳ΋ͷʹ͢Δ • σόοά࣌ؒͷݮগ • มߋ΁ͷ৴པͷ૿େ • υΩϡϝϯςʔγϣϯͷվળ • ϨϏϡʔͷ୯७Խ • ࢥྀʹ෋Ήઃܭ • ߴ଎Ͱߴ඼࣭ͳϦϦʔε 🤖

Slide 13

Slide 13 text

ςετͷ۩ମతͳϝϦοτ • ςετΛॻ͘ͱઃܭ͕ྑ͘ͳΔ • ʢϓϩμΫτΛʣ࣋ଓՄೳͳ΋ͷʹ͢Δ • σόοά࣌ؒͷݮগ • มߋ΁ͷ৴པͷ૿େ • υΩϡϝϯςʔγϣϯͷվળ • ϨϏϡʔͷ୯७Խ • ࢥྀʹ෋Ήઃܭ • ߴ଎Ͱߴ඼࣭ͳϦϦʔε ʮςετΛॻ࣌ؒ͘΍࣮ߦ࣌ؒͷσϝϦοτ͸ແࢹͳͷʁʯ 🤖

Slide 14

Slide 14 text

ςετ͋Γͳ͠ͷଛӹ෼ذ఺͕͋Δ ࢀߟ: ॻ੶ʮ୯ମςετͷߟ͑ํ/࢖͍ํʯ ʮ࠷ॳςετΛॻ͕͔͔͍࣌ؒͬͯͯ͘΋ɺ ɹɹ࣍ୈʹͦΕ͕ଛͰ͸ͳ͘੒௕ʹඞਢʹʯ 🐲

Slide 15

Slide 15 text

ίετ͸͔͔Δ͕ςετΛॻ͘ཧ༝͕͋Δ

Slide 16

Slide 16 text

ͦ΋ͦ΋iOSΞϓϦ͸֎෦࿈ܞ͹͔Γ ࣗ෼ͨͪͷαʔόʔ ·ͨ͸8FC"1*Λެ։ͯ͘͠ΕͯΔ8FCαʔϏε ·ͨ͸'JSFCBTFͳͲͷαʔόʔϨεͳͲ "QQMFͷϑϨʔϜϫʔΫ ࣗ෼ͨͪͷιʔείʔυ 044ͷϑϨʔϜϫʔΫ Ӭଓσʔλʔ ʢ'JMFͱ͔%#ʣ ֤छ ηϯαʔ 🤖 🐲

Slide 17

Slide 17 text

ͦ΋ͦ΋iOSΞϓϦ͸֎෦࿈ܞ͹͔Γ ࣗ෼ͨͪͷαʔόʔ ·ͨ͸8FC"1*Λެ։ͯ͘͠ΕͯΔ8FCαʔϏε ·ͨ͸'JSFCBTFͳͲͷαʔόʔϨεͳͲ "QQMFͷϑϨʔϜϫʔΫ ࣗ෼ͨͪͷιʔείʔυ 044ͷϑϨʔϜϫʔΫ ʮͤΊͯࣗ෼ͷॻ͍ͨίʔυ͸ݫີʹৼΔ෣͏Α͏ʹ͍ͨ͠ʯ Ӭଓσʔλʔ ʢ'JMFͱ͔%#ʣ ֤छ ηϯαʔ 🤖 🐲

Slide 18

Slide 18 text

ͦ΋ͦ΋iOSΞϓϦ͸֎෦࿈ܞ͹͔Γ ࣗ෼ͨͪͷαʔόʔ ·ͨ͸8FC"1*Λެ։ͯ͘͠ΕͯΔ8FCαʔϏε ·ͨ͸'JSFCBTFͳͲͷαʔόʔϨεͳͲ "QQMFͷϑϨʔϜϫʔΫ ࣗ෼ͨͪͷιʔείʔυ 044ͷϑϨʔϜϫʔΫ ʮͤΊͯࣗ෼ͷॻ͍ͨίʔυ͸ݫີʹৼΔ෣͏Α͏ʹ͍ͨ͠ʯ Ӭଓσʔλʔ ʢ'JMFͱ͔%#ʣ ֤छ ηϯαʔ ʮUIͷૢ࡞͕લఏͳΒࢮ͵΄Ͳखಈςετͨ͠ΒΧόʔͰ͖Δͷʹ…ʯ 🤖 🐲

Slide 19

Slide 19 text

΋ͪΖΜ ͲΜͳςετͰ΋ॻ͚͹ྑ͍Θ͚Ͱ͸ͳ͘ ѱ͍ςετΛॻ͍ͯ͸͍͚ͳ͍

Slide 20

Slide 20 text

ྑ͍ςετͷ൓ରଆʹ͋Δѱ͍ςετ • ཧղͮ͠Β͍ςετίʔυ • ඞཁҎ্ʹ஗͍ςετίʔυ • յΕ΍͍͢ςετ • ςετ݁Ռ͕ෆ҆ఆͳςετ 🐲

Slide 21

Slide 21 text

ྑ͍ςετͷ൓ରଆʹ͋Δѱ͍ςετ • ཧղͮ͠Β͍ςετίʔυ • ඞཁҎ্ʹ஗͍ςετίʔυ • յΕ΍͍͢ςετ • ςετ݁Ռ͕ෆ҆ఆͳςετ ʮෆ҆ఆͳςετ݁ՌʹͳΔͱ։ൃऀͷ໾ʹཱͨͳ͍౓͕େ͖͍ʯ 🐲

Slide 22

Slide 22 text

ຊ୊ ෆ҆ఆͳςετͷݪҼΛ੔ཧ͠ղܾ͢Δ

Slide 23

Slide 23 text

ࢲͷʮෆ҆ఆͳςετʯͷఆٛ 100%੒ޭ 100%ࣦഊ ಉҰͷςετίʔυͰ ੒ޭͨ͠Γࣦഊͨ͠Γ ݁Ռ͕ෆ҆ఆ 🤖 🐲

Slide 24

Slide 24 text

ࢲͷʮෆ҆ఆͳςετʯͷఆٛ 100%੒ޭ 100%ࣦഊ ಉҰͷςετίʔυͰ ੒ޭͨ͠Γࣦഊͨ͠Γ ݁Ռ͕ෆ҆ఆ ʮFlakyʢ৴པͰ͖ͳ͍ʣςετͬͯݴ͑͹͍͍ͷʹ…ʯ 🤖 🐲

Slide 25

Slide 25 text

ࢲͷʮෆ҆ఆͳςετʯͷఆٛ 100%੒ޭ 100%ࣦഊ ಉҰͷςετίʔυͰ ੒ޭͨ͠Γࣦഊͨ͠Γ ݁Ռ͕ෆ҆ఆ ʮFlakyʢ৴པͰ͖ͳ͍ʣςετͬͯݴ͑͹͍͍ͷʹ…ʯ ʮ݁Ռ͕ෆ҆ఆͱ͍͏ํ͕௚઀తͰ޷͖ʯ 🤖 🐲

Slide 26

Slide 26 text

ࠓճ࿩͢ෆ҆ఆͳςετͷݪҼ෼ྨ • ݪҼ • ݪҼ1:࣮ߦॱংʹґଘ͢Δςετ • ݪҼ2: ࣮ߦ࣌·Ͱ஋͕ܾఆ͠ͳ͍࠶ݱੑͷͳ͍ςετ • ݪҼ3: ϑϨʔϜϫʔΫཧղෆ଍ • ݪҼ͕ද໘Խ͠΍͍͢: • CIͳͲͷςετ࣮ߦϚγϯͷϦιʔεෆ଍ Swift 5.10Ͱݕূ

Slide 27

Slide 27 text

ݪҼ1: ࣮ߦॱংʹґଘ͢Δςετ • ྫ • ςετίʔυؒͰڞ༗ͷ஋/ࢀরΛ࣋ͬͯ͠·͏

Slide 28

Slide 28 text

ෆ҆ఆͳݪҼ: ࣮ߦॱংʹґଘ͢Δςετ class Singleton { static let shared = Singleton() private init() {} var value: Int = 0 var unit: String = "g" } ———————————————————————-———————————————————————- final class SampleTests: XCTestCase { func test_value͕1000Ͱunit͕g͸_1000gͱදࣔ͞ΕΔ() { Singleton.shared.value = 1000 ɹɹɹɹ // ͜͜Ͱ୅ೖ͠Θ͢Ε͍ͯΔ͕ॳظ஋͕Χόʔͯ͠͠·ͬͯΔ XCTAssertEqual(Singleton.shared.value, 1000) XCTAssertEqual(Singleton.shared.unit, “g") // ԼͷςετΑΓઌʹ࣮ߦ͞ΕΔͱॳظ஋͕࢖ΘΕςετ͸੒ޭ } func test_value1Ͱunit͕kg͸_1kgͱදࣔ͞ΕΔ() { Singleton.shared.value = 1 Singleton.shared.unit = "kg" XCTAssertEqual(Singleton.shared.value, 1) XCTAssertEqual(Singleton.shared.unit, "kg") } } 🐲 🤖

Slide 29

Slide 29 text

ෆ҆ఆͳݪҼ: ࣮ߦॱংʹґଘ͢Δςετ class Singleton { static let shared = Singleton() private init() {} var value: Int = 0 var unit: String = "g" } ———————————————————————-———————————————————————- final class SampleTests: XCTestCase { func test_value͕1000Ͱunit͕g͸_1000gͱදࣔ͞ΕΔ() { Singleton.shared.value = 1000 ɹɹɹɹ // ͜͜Ͱ୅ೖ͠Θ͢Ε͍ͯΔ͕ॳظ஋͕Χόʔͯ͠͠·ͬͯΔ XCTAssertEqual(Singleton.shared.value, 1000) XCTAssertEqual(Singleton.shared.unit, “g") // ԼͷςετΑΓઌʹ࣮ߦ͞ΕΔͱॳظ஋͕࢖ΘΕςετ͸੒ޭ } func test_value1Ͱunit͕kg͸_1kgͱදࣔ͞ΕΔ() { Singleton.shared.value = 1 Singleton.shared.unit = "kg" XCTAssertEqual(Singleton.shared.value, 1) XCTAssertEqual(Singleton.shared.unit, "kg") } } ʮγϯάϧτϯͳΜͯ࢖ Θͳ͚Ε͹͍͍ͷʹ…ʯ 🐲 🤖

Slide 30

Slide 30 text

ෆ҆ఆͳݪҼ: ࣮ߦॱংʹґଘ͢Δςετ class Singleton { static let shared = Singleton() private init() {} var value: Int = 0 var unit: String = "g" } ———————————————————————-———————————————————————- final class SampleTests: XCTestCase { func test_value͕1000Ͱunit͕g͸_1000gͱදࣔ͞ΕΔ() { Singleton.shared.value = 1000 ɹɹɹɹ // ͜͜Ͱ୅ೖ͠Θ͢Ε͍ͯΔ͕ॳظ஋͕Χόʔͯ͠͠·ͬͯΔ XCTAssertEqual(Singleton.shared.value, 1000) XCTAssertEqual(Singleton.shared.unit, “g") // ԼͷςετΑΓઌʹ࣮ߦ͞ΕΔͱॳظ஋͕࢖ΘΕςετ͸੒ޭ } func test_value1Ͱunit͕kg͸_1kgͱදࣔ͞ΕΔ() { Singleton.shared.value = 1 Singleton.shared.unit = "kg" XCTAssertEqual(Singleton.shared.value, 1) XCTAssertEqual(Singleton.shared.unit, "kg") } } ʮγϯάϧτϯͳΜͯ࢖ Θͳ͚Ε͹͍͍ͷʹ…ʯ ʮ͜͏͍͏৔߹͸setUp() Λ࢖ͬͯॳظԽʯ 🐲 🤖

Slide 31

Slide 31 text

ෆ҆ఆͳݪҼ: ࣮ߦॱংʹґଘ͢Δςετ 🐲 ʮͪͳΈʹɺXCTestCaseͷϓϩύςΟ͸ࣗಈͰॳظԽ͞Ε·͢ʯ 🤖 🐲

Slide 32

Slide 32 text

ෆ҆ఆͳݪҼ: ࣮ߦॱংʹґଘ͢Δςετ 🐲 ʮͪͳΈʹɺXCTestCaseͷϓϩύςΟ͸ࣗಈͰॳظԽ͞Ε·͢ʯ ʮclassͳͷͰ༧૝΋͔ͭͳ͍ ͷͰ͓͢͢Ί͸Ͱ͖ͳ͍…ʯ 🤖 🐲

Slide 33

Slide 33 text

ෆ҆ఆͳݪҼ: ࣮ߦॱংʹґଘ͢Δςετ 🐲 ʮͪͳΈʹɺXCTestCaseͷϓϩύςΟ͸ࣗಈͰॳظԽ͞Ε·͢ʯ ʮclassͳͷͰ༧૝΋͔ͭͳ͍ ͷͰ͓͢͢Ί͸Ͱ͖ͳ͍…ʯ 🤖 ʮ࣍ੈ୅ͷSwift Testing Ͱ͸ ౰ͨΓલʹͳΓ·͢Αʯ 🐲

Slide 34

Slide 34 text

ݪҼ2: ࣮ߦ࣌ʹ஋͕ܾఆ͢Δಈతͳॲཧͷςετ • ྫ • Date() / Date.init() / Date.now • ݺͼग़͞Εͨࡍͷ࣌ؒΛऔಘ͢Δίʔυ

Slide 35

Slide 35 text

class TimeUtils { // ࠓճͷςετର৅ // ݱࡏ࣌ࠁ͔ΒҾ਺ͷDateͱͷࠩΛTimeintervalͰฦ͢ϝιου func timeElapsedSince(_ date: Date) -> TimeInterval { Date().timeIntervalSince(date) } } ———————————————————————-———————————————————————————————————————- final class TimeUtilsTests: XCTestCase { let timeUtils = TimeUtils() func test_TimeElapsedSince() { let date = Date(timeIntervalSinceNow: -6) // 6ඵલͷDate // Action …….. ͜͜Βล͕ΊͪΌͪ͘Ό͔͔࣌ؒͬͨΒ૝ఆ͔Β͸ͣΕΔ let elapsed = timeUtils.timeElapsedSince(date) // Assertion XCTAssertEqual( elapsed, 6, accuracy: 0.1 // ڐ༰͢Δޡࠩൣғ ) } } ෆ҆ఆͳݪҼ: ࣮ߦ࣌ʹ஋͕ܾఆ͢Δಈతͳॲཧͷςετ 🤖

Slide 36

Slide 36 text

class TimeUtils { // ࠓճͷςετର৅ // ݱࡏ࣌ࠁ͔ΒҾ਺ͷDateͱͷࠩΛTimeintervalͰฦ͢ϝιου func timeElapsedSince(_ date: Date) -> TimeInterval { Date().timeIntervalSince(date) } } ———————————————————————-———————————————————————————————————————- final class TimeUtilsTests: XCTestCase { let timeUtils = TimeUtils() func test_TimeElapsedSince() { let date = Date(timeIntervalSinceNow: -6) // 6ඵલͷDate // Action …….. ͜͜Βล͕ΊͪΌͪ͘Ό͔͔࣌ؒͬͨΒ૝ఆ͔Β͸ͣΕΔ let elapsed = timeUtils.timeElapsedSince(date) // Assertion XCTAssertEqual( elapsed, 6, accuracy: 0.1 // ڐ༰͢Δޡࠩൣғ ) } } ෆ҆ఆͳݪҼ: ࣮ߦ࣌ʹ஋͕ܾఆ͢Δಈతͳॲཧͷςετ ʮݱࡏ࣌ࠁʹཔΔͱςετ ɹ͢ΔCIͷϚγϯύϫʔʹ΋ ɹґଘ͢Δͷʹ…ʯ 🤖

Slide 37

Slide 37 text

class TimeUtils { func timeElapsedSince(date: Date, currentDate: Date = Date()) -> TimeInterval { // ςετ͢Δର৅ͷॲཧ͸࣮ߦ࣌ʹܾ·Βͳ͍Α͏ʹ͠࠶ݱੑΛߴΊΔ currentDate.timeIntervalSince(date) } } ———————————————————————-———————————————————————-———————————————————————- final class TimeUtilsTests: XCTestCase { let timeUtils = TimeUtils() // ࠓճͷςετର৅ func test_TimeElapsedSince2() { let currentDate = Date(timeIntervalSince1970: 0) // 1970/1/1ͷ0࣌ let date = Date(timeIntervalSince1970: -6) // 1970/1/1ͷ0͔࣌Β6ඵલ // Action let elapsed = timeUtils.timeElapsedSince( date: date, currentDate: currentDate ) // Assertion ɹ XCTAssertEqual( elapsed, 6, accuracy: 0.1 // ڐ༰͢Δޡࠩൣғ ) } } ෆ҆ఆͳݪҼ: ࣮ߦ࣌ʹ஋͕ܾఆ͢Δಈతͳॲཧͷςετ

Slide 38

Slide 38 text

ิ଍: ࣮ߦ࣌ʹಈతʹ஋͕ܾఆ͢Δ΍ͭΒ • Foundation.Date / Calendar / Locale / UUID • ੔਺ͳͲͷཚ਺ੜ੒ https://github.com/pointfreeco/swift-dependencies/tree/main/ Sources/Dependencies/DependencyValues ࢀߟ: pointfreeco/swift-dependencies ෆ҆ఆͳݪҼ: ࣮ߦ࣌ʹ஋͕ܾఆ͢Δಈతͳॲཧͷςετ

Slide 39

Slide 39 text

ݪҼ3: OSSϑϨʔϜϫʔΫཧղෆ଍ • ྫ • ҙਤͤͣRealm͕εϨουؒΛӽ͑ͯ͠·͏ • ผεϨουͰRealmͷ݁ՌΛrefreshͤͣݕূͯ͠͠·͏

Slide 40

Slide 40 text

ෆ҆ఆͳݪҼ:OSSϑϨʔϜϫʔΫཧղෆ଍ https://developer.apple.com/documentation/xctest/asynchronous_tests_and_expectations جૅ: XCTestͷglobalActorࢦఆ༗ແͷڍಈͷҧ͍ 🤖 🐲 🐲

Slide 41

Slide 41 text

ෆ҆ఆͳݪҼ:OSSϑϨʔϜϫʔΫཧղෆ଍ https://developer.apple.com/documentation/xctest/asynchronous_tests_and_expectations ʮMainActorࢦఆ͸ ɹϝΠϯεϨουʯ جૅ: XCTestͷglobalActorࢦఆ༗ແͷڍಈͷҧ͍ 🤖 🐲 🐲

Slide 42

Slide 42 text

ෆ҆ఆͳݪҼ:OSSϑϨʔϜϫʔΫཧղෆ଍ https://developer.apple.com/documentation/xctest/asynchronous_tests_and_expectations ʮMainActorࢦఆ͸ ɹϝΠϯεϨουʯ ʮແࢦఆ͸ϝΠϯ ɹεϨουͰͳ͍ʯ جૅ: XCTestͷglobalActorࢦఆ༗ແͷڍಈͷҧ͍ 🤖 🐲 🐲

Slide 43

Slide 43 text

ෆ҆ఆͳݪҼ:OSSϑϨʔϜϫʔΫཧղෆ଍ https://developer.apple.com/documentation/xctest/asynchronous_tests_and_expectations ʮMainActorࢦఆ͸ ɹϝΠϯεϨουʯ ʮແࢦఆ͸ϝΠϯ ɹεϨουͰͳ͍ʯ جૅ: XCTestͷglobalActorࢦఆ༗ແͷڍಈͷҧ͍ ʮެࣜυΩϡϝϯτʹॻ͔ΕͯΔͷʹ…ʯ 🤖 🐲 🐲

Slide 44

Slide 44 text

@globalActor actor DBActor { static let shared = DBActor() } class ThreadTests2: XCTestCase { // MainActorʢglobalActorࢦఆʣແ͠ func test_MainActorͰͳ͘async() async { print("A:", Thread.current) let task = Task { @DBActor in try! await Task.sleep(nanoseconds: 1_000_000_000 * 5) print("B:", Thread.current) } await task.value print("C:", Thread.current) } } ෆ҆ఆͳݪҼ:OSSϑϨʔϜϫʔΫཧղෆ଍ جૅ: globalActorࢦఆແ͠ͰActorݺͼग़͢ͱ࣮ߦ εϨου͸ঢ়گʹࠨӈ͞ΕΔʢεϨουϓʔϧ͔Βબ͹ΕΔʣ 🐲

Slide 45

Slide 45 text

@globalActor actor DBActor { static let shared = DBActor() } class ThreadTests2: XCTestCase { // MainActorʢglobalActorࢦఆʣແ͠ func test_MainActorͰͳ͘async() async { print("A:", Thread.current) let task = Task { @DBActor in try! await Task.sleep(nanoseconds: 1_000_000_000 * 5) print("B:", Thread.current) } await task.value print("C:", Thread.current) } } ෆ҆ఆͳݪҼ:OSSϑϨʔϜϫʔΫཧղෆ଍ جૅ: globalActorࢦఆແ͠ͰActorݺͼग़͢ͱ࣮ߦ εϨου͸ঢ়گʹࠨӈ͞ΕΔʢεϨουϓʔϧ͔Βબ͹ΕΔʣ 🐲

Slide 46

Slide 46 text

@globalActor actor DBActor { static let shared = DBActor() } class ThreadTests2: XCTestCase { // MainActorʢglobalActorࢦఆʣແ͠ func test_MainActorͰͳ͘async() async { print("A:", Thread.current) let task = Task { @DBActor in try! await Task.sleep(nanoseconds: 1_000_000_000 * 5) print("B:", Thread.current) } await task.value print("C:", Thread.current) } } ෆ҆ఆͳݪҼ:OSSϑϨʔϜϫʔΫཧղෆ଍ εϨουA,B,CͷՄೳੑ͸ A == B, B== C || A == B, B!= C || A != B, B== C || A == C, C !== B || جૅ: globalActorࢦఆແ͠ͰActorݺͼग़͢ͱ࣮ߦ εϨου͸ঢ়گʹࠨӈ͞ΕΔʢεϨουϓʔϧ͔Βબ͹ΕΔʣ 🐲

Slide 47

Slide 47 text

ෆ҆ఆͳݪҼ:OSSϑϨʔϜϫʔΫཧղෆ଍ ྫ: ҙਤͤͣRealm͕εϨουؒΛӽ͑ͯ͠·͏ 🐲 @globalActor actor DBActor { static let shared = DBActor() } class ThreadTests2: XCTestCase { // MainActorʢglobalActorࢦఆʣແ͠ func test_MainActorͰͳ͘async() async { print("A:", Thread.current) let otherRealm = RLMRealm.default() let task = Task { @DBActor in try! await Task.sleep(nanoseconds: 1_000_000_000 * 5) print("B:", Thread.current) } await task.value print("C:", Thread.current) XCTAssertNotNil( RealmObject.object(in: otherRealm, forPrimaryKey: "1") ) } } 🐲

Slide 48

Slide 48 text

ෆ҆ఆͳݪҼ:OSSϑϨʔϜϫʔΫཧղෆ଍ ྫ: ҙਤͤͣRealm͕εϨουؒΛӽ͑ͯ͠·͏ 🐲 @globalActor actor DBActor { static let shared = DBActor() } class ThreadTests2: XCTestCase { // MainActorʢglobalActorࢦఆʣແ͠ func test_MainActorͰͳ͘async() async { print("A:", Thread.current) let otherRealm = RLMRealm.default() let task = Task { @DBActor in try! await Task.sleep(nanoseconds: 1_000_000_000 * 5) print("B:", Thread.current) } await task.value print("C:", Thread.current) XCTAssertNotNil( RealmObject.object(in: otherRealm, forPrimaryKey: "1") ) } } 🐲

Slide 49

Slide 49 text

ෆ҆ఆͳݪҼ:OSSϑϨʔϜϫʔΫཧղෆ଍ ྫ: ҙਤͤͣRealm͕εϨουؒΛӽ͑ͯ͠·͏ 🐲 @globalActor actor DBActor { static let shared = DBActor() } class ThreadTests2: XCTestCase { // MainActorʢglobalActorࢦఆʣແ͠ func test_MainActorͰͳ͘async() async { print("A:", Thread.current) let otherRealm = RLMRealm.default() let task = Task { @DBActor in try! await Task.sleep(nanoseconds: 1_000_000_000 * 5) print("B:", Thread.current) } await task.value print("C:", Thread.current) XCTAssertNotNil( RealmObject.object(in: otherRealm, forPrimaryKey: "1") ) } } 🐲

Slide 50

Slide 50 text

ෆ҆ఆͳݪҼ:OSSϑϨʔϜϫʔΫཧղෆ଍ ྫ: ҙਤͤͣRealm͕εϨουؒΛӽ͑ͯ͠·͏ 🐲 @globalActor actor DBActor { static let shared = DBActor() } class ThreadTests2: XCTestCase { // MainActorʢglobalActorࢦఆʣແ͠ func test_MainActorͰͳ͘async() async { print("A:", Thread.current) let otherRealm = RLMRealm.default() let task = Task { @DBActor in try! await Task.sleep(nanoseconds: 1_000_000_000 * 5) print("B:", Thread.current) } await task.value print("C:", Thread.current) XCTAssertNotNil( RealmObject.object(in: otherRealm, forPrimaryKey: "1") ) } } 🐲

Slide 51

Slide 51 text

ෆ҆ఆͳݪҼ:OSSϑϨʔϜϫʔΫཧղෆ଍ εϨουA==Cͷ৔߹ Realm͸ಉҰεϨουͰར༻ ✅ ੒ޭ ྫ: ҙਤͤͣRealm͕εϨουؒΛӽ͑ͯ͠·͏ 🐲 @globalActor actor DBActor { static let shared = DBActor() } class ThreadTests2: XCTestCase { // MainActorʢglobalActorࢦఆʣແ͠ func test_MainActorͰͳ͘async() async { print("A:", Thread.current) let otherRealm = RLMRealm.default() let task = Task { @DBActor in try! await Task.sleep(nanoseconds: 1_000_000_000 * 5) print("B:", Thread.current) } await task.value print("C:", Thread.current) XCTAssertNotNil( RealmObject.object(in: otherRealm, forPrimaryKey: "1") ) } } 🐲

Slide 52

Slide 52 text

ෆ҆ఆͳݪҼ:OSSϑϨʔϜϫʔΫཧղෆ଍ εϨουA==Cͷ৔߹ Realm͸ಉҰεϨουͰར༻ ✅ ੒ޭ ྫ: ҙਤͤͣRealm͕εϨουؒΛӽ͑ͯ͠·͏ 🐲 @globalActor actor DBActor { static let shared = DBActor() } class ThreadTests2: XCTestCase { // MainActorʢglobalActorࢦఆʣແ͠ func test_MainActorͰͳ͘async() async { print("A:", Thread.current) let otherRealm = RLMRealm.default() let task = Task { @DBActor in try! await Task.sleep(nanoseconds: 1_000_000_000 * 5) print("B:", Thread.current) } await task.value print("C:", Thread.current) XCTAssertNotNil( RealmObject.object(in: otherRealm, forPrimaryKey: "1") ) } } εϨουA != Cͷ৔߹ Realm͕ผεϨουͰར༻͞ΕΔ 🚫 ࣦഊ 🐲

Slide 53

Slide 53 text

ෆ҆ఆͳݪҼ:OSSϑϨʔϜϫʔΫཧղෆ଍ ྫ: ผεϨουͷRealmͷॻ͖ࠐΈΛ଴͚ͭͩͰ͸ ·ͩಉظ͸׬͍ྃͯ͠ͳ͍ ಉظ͸λΠϛϯά͸ खಈSFGSFTI ·ͨ͸࣌ؒܦա ·ͨ͸ BVUPSFGSFTIʢϝΠϯεϨουͷσϑΥϧτʣ ·ͨ͸ ͓ͦΒͦ͘ͷଞ͍Ζ͍ΖͳλΠϛϯά ͦΕͱ͸ผ εϨουͷ Realm ผεϨουͷ Realm 🐲

Slide 54

Slide 54 text

ෆ҆ఆͳݪҼ:OSSϑϨʔϜϫʔΫཧղෆ଍ ྫ: ผεϨουͷRealmͷॻ͖ࠐΈΛ଴͚ͭͩͰ͸ ·ͩಉظ͸׬͍ྃͯ͠ͳ͍ ಉظ͸λΠϛϯά͸ खಈSFGSFTI ·ͨ͸࣌ؒܦա ·ͨ͸ BVUPSFGSFTIʢϝΠϯεϨουͷσϑΥϧτʣ ·ͨ͸ ͓ͦΒͦ͘ͷଞ͍Ζ͍ΖͳλΠϛϯά ͦΕͱ͸ผ εϨουͷ Realm ผεϨουͷ Realm Realmͷॻ͖ࠐΈΛݕূ͢Δ݁Ռ͕ ෆ҆ఆͳͱ͖͸ ಈ࡞͍ͯ͠ΔεϨουΛௐࠪ͠ɺ RealmͷυΩϡϝϯτͰ realm.refresh()Λௐ΂ͯΈ͍ͨɻ 🐲

Slide 55

Slide 55 text

·ͱΊ

Slide 56

Slide 56 text

ෆ҆ఆͳ݁Ռͷ࣌ʹؾ͕͖͍ͭͨ͜ͱ • ςετέʔεؒͰ஋/ࢀরΛ࠶ར༻ͯ͠ͳ͍͔ • ಈతʹܾ·Δ஋ΛϩδοΫ΍ςετίʔυʹຒΊࠐΜͰ͍ͳ͍͔ • ࣮ߦεϨου͕ҙਤͤͣมΘͬͯ͠·͍ͬͯͳ͍͔

Slide 57

Slide 57 text

͓ΘΓʹ

Slide 58

Slide 58 text

ͦͷ΄͔ͷෆ҆ఆͷݪҼͱղܾͷ࿩ • ࠓճ͸આ໌লུʢผͷࢿྉ: ෆ҆ఆͳςετ͸200छྨ͋ΜͶΜʣ • https://speakerdeck.com/yimajo/bu-an-ding-natesutoha200zhong- lei-annen

Slide 59

Slide 59 text

͜ͷൃදͰ ؒҧ͍͕͋ͬͨΓ͝ҙݟ͋Ε͹ɺ ͳΔ΂͘ Θ͔Γ΍͘͢ࢦఠ΍࣭໰ͯ͠௖͚Δͱ ༗ҙٛͳ΍ΓͱΓ͕Ͱ͖Δ͸ͣͰ͢ɻ ʢࢲͷࢥ͍ࠐΈͰղܾͨͭ͠΋Γʹͳ͍ͬͯΔ͜ͱ΋͋Γ·͢ʣ

Slide 60

Slide 60 text

͓ΘΓ 🐲 🤖 ͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠