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
良いテストコードのために悪いテストコードを理解する - 不安定なテスト編: iOSアプリ開発...
Search
yimajo
June 24, 2024
Programming
6.8k
22
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
良いテストコードのために悪いテストコードを理解する - 不安定なテスト編: iOSアプリ開発ユニットテストの場合
「モバイルアプリ開発における良いテストコードの考え方」の発表資料です。
https://trident-qa.connpass.com/event/320151/
yimajo
June 24, 2024
More Decks by yimajo
See All by yimajo
OSSとなったswift-buildで Xcodeのビルドを差し替えられるため 自分でXcodeを直せる時代になっている ダイアモンド問題編
yimajo
3
900
いま中途半端なSwift 6対応をするより、Default ActorやApproachable Concurrencyを有効にしてからでいいんじゃない?
yimajo
2
710
11年かかって やっとVibe Codingに 時代が追いつきましたね
yimajo
1
390
iOSアプリ開発で 関数型プログラミングを実現する The Composable Architectureの紹介
yimajo
3
340
TCAの Shared Stateって どういう仕組みになってんの?
yimajo
0
2.7k
Swift 5.9 からの Observation はiOS17 未満 からも使えて struct の変更検知もできるんすかね?
yimajo
2
1.2k
不安定なテストは200種類あんねん
yimajo
3
1.2k
TCA v0.19.0からのSwitchStore/CaseLetが良い
yimajo
0
2.1k
TCAでViewStoreにKeyPath DynamicMemberLookupが使われてる件
yimajo
0
1.3k
Other Decks in Programming
See All in Programming
複数の Claude Code が"放置"されてしまう問題をCLI ダッシュボードを自作して解決した話
sumihiro3
1
690
自動化したのに回らない テスト運用の壁―AI時代の品質責任と生産性
mfunaki
0
220
これって Effect でできたのでは? / TSKaigi Mashup Kansai #2
susisu
0
240
jsmini JavaScript Engine を作ってみた話
yosuke_furukawa
PRO
0
320
プロポーザルを書いてもらう
pvcresin
0
540
実装をデザインガイドラインに追従させるための取り組み / 260731-dip-mosh-design-system
dachi023
0
480
FDEが実現するAI駆動経営の現在地
gonta
2
280
わからない話を追いかけたら、プログラミング言語を作る側にいた
ydah
3
510
【QA Test Talk Vol.8】AI-DLC による Whole Team Approach の加速
pkshadeck
PRO
0
190
freee が目指す データ マネジメント戦略 AI-Ready 時代を支える 攻めのガバナンスとは
freee
PRO
0
390
「人を評価する AI」の設計と実装
ryoyanara
0
210
AIが無かった頃の素敵な出会いの話
codmoninc
1
440
Featured
See All Featured
Raft: Consensus for Rubyists
vanstee
141
7.6k
Primal Persuasion: How to Engage the Brain for Learning That Lasts
tmiket
0
410
Leading Effective Engineering Teams in the AI Era
addyosmani
9
2.3k
[RailsConf 2023] Rails as a piece of cake
palkan
59
6.9k
Site-Speed That Sticks
csswizardry
13
1.4k
WCS-LA-2024
lcolladotor
0
800
Building Applications with DynamoDB
mza
96
7.2k
Fashionably flexible responsive web design (full day workshop)
malarkey
408
67k
Principles of Awesome APIs and How to Build Them.
keavy
128
18k
Become a Pro
speakerdeck
PRO
31
6.2k
Google's AI Overviews - The New Search
badams
0
1.1k
JAMstack: Web Apps at Ludicrous Speed - All Things Open 2022
reverentgeek
1
570
Transcript
ྑ͍ςετίʔυͷͨΊʹ ѱ͍ςετίʔυΛཧղ͢Δ 2024.6.24 גࣜձࣾΩϡϦΦγςΟιϑτΣΞ @yimajo #ྑ͍ςετίʔυ ෆ҆ఆͳςετฤJ04ΞϓϦ։ൃϢχοτςετͷ߹ 🐲 🤖 ϤϩγΫ
͓ئ͍͠·͢
͔ΜͨΜͳ ࣗݾհ
• ৬ྺ • ৽ଔ: ಋମϝʔΧʔͰLSIΛςετ͢ΔΤϯδχΞ • WebϞόΠϧΞϓϦ։ൃͷιϑτΣΞΤϯδχΞ • Now!: ϓϩαφʔ݉ιϑτΣΞΤϯδχΞ
ຊͷલʹ ࠓճ͢Ϣχοτςετʹ͍ͭͯͷఆٛ ྑ͍ςετͱԿ͔ʹ͍ͭͯ ͳͥςετΛॻ͘ͷ͔
Ҿ༻: 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ͷఆٛ 🐲
Ҿ༻: 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ͷఆٛ 🐲
Ҿ༻: 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 ʹ͍ͭͯͷςετͷͰ͢ʯ 🐲
ఆٛ: ྑ͍ςετͱʁ ιϑτΣΞͷ࣭Λ ʢ্͛Δͱ͍͏͔ʣམͱ͞ͳ͍ͨΊʹ ։ൃऀͷʹཱͭࣗಈԽ 🐲
ఆٛ: ྑ͍ςετͱʁ ιϑτΣΞͷ࣭Λ ʢ্͛Δͱ͍͏͔ʣམͱ͞ͳ͍ͨΊʹ ։ൃऀͷʹཱͭࣗಈԽ ʮͪΖΜؒతʹΞϓϦΛͬͯ͘ΕΔϢʔβʔͷʹཱͭͱ͍͏͜ͱͰ͢ʯ 🐲
ςετͷ۩ମతͳϝϦοτ Ҿ༻: ॻ੶ʮGoogle ͷιϑτΣΞΤϯδχΞϦϯάʯ • σόοά࣌ؒͷݮগ • มߋͷ৴པͷ૿େ • υΩϡϝϯςʔγϣϯͷվળ
• ϨϏϡʔͷ୯७Խ • ࢥྀʹΉઃܭ • ߴͰߴ࣭ͳϦϦʔε
ςετͷ۩ମతͳϝϦοτ Ҿ༻: ॻ੶ʮ୯ମςετͷߟ͑ํ/͍ํʯ • ςετΛॻ͘ͱઃܭ͕ྑ͘ͳΔ • ʢϓϩμΫτΛʣ࣋ଓՄೳͳͷʹ͢Δ
ςετͷ۩ମతͳϝϦοτ • ςετΛॻ͘ͱઃܭ͕ྑ͘ͳΔ • ʢϓϩμΫτΛʣ࣋ଓՄೳͳͷʹ͢Δ • σόοά࣌ؒͷݮগ • มߋͷ৴པͷ૿େ •
υΩϡϝϯςʔγϣϯͷվળ • ϨϏϡʔͷ୯७Խ • ࢥྀʹΉઃܭ • ߴͰߴ࣭ͳϦϦʔε 🤖
ςετͷ۩ମతͳϝϦοτ • ςετΛॻ͘ͱઃܭ͕ྑ͘ͳΔ • ʢϓϩμΫτΛʣ࣋ଓՄೳͳͷʹ͢Δ • σόοά࣌ؒͷݮগ • มߋͷ৴པͷ૿େ •
υΩϡϝϯςʔγϣϯͷվળ • ϨϏϡʔͷ୯७Խ • ࢥྀʹΉઃܭ • ߴͰߴ࣭ͳϦϦʔε ʮςετΛॻ࣮࣌ؒ͘ߦ࣌ؒͷσϝϦοτແࢹͳͷʁʯ 🤖
ςετ͋Γͳ͠ͷଛӹذ͕͋Δ ࢀߟ: ॻ੶ʮ୯ମςετͷߟ͑ํ/͍ํʯ ʮ࠷ॳςετΛॻ͕͔͔͍࣌ؒͬͯͯ͘ɺ ɹɹ࣍ୈʹͦΕ͕ଛͰͳ͘ʹඞਢʹʯ 🐲
ίετ͔͔Δ͕ςετΛॻ͘ཧ༝͕͋Δ
ͦͦiOSΞϓϦ֎෦࿈ܞ͔Γ ࣗͨͪͷαʔόʔ ·ͨ8FC"1*Λެ։ͯ͘͠ΕͯΔ8FCαʔϏε ·ͨ'JSFCBTFͳͲͷαʔόʔϨεͳͲ "QQMFͷϑϨʔϜϫʔΫ ࣗͨͪͷιʔείʔυ 044ͷϑϨʔϜϫʔΫ Ӭଓσʔλʔ ʢ'JMFͱ͔%#ʣ ֤छ
ηϯαʔ 🤖 🐲
ͦͦiOSΞϓϦ֎෦࿈ܞ͔Γ ࣗͨͪͷαʔόʔ ·ͨ8FC"1*Λެ։ͯ͘͠ΕͯΔ8FCαʔϏε ·ͨ'JSFCBTFͳͲͷαʔόʔϨεͳͲ "QQMFͷϑϨʔϜϫʔΫ ࣗͨͪͷιʔείʔυ 044ͷϑϨʔϜϫʔΫ ʮͤΊͯࣗͷॻ͍ͨίʔυݫີʹৼΔ͏Α͏ʹ͍ͨ͠ʯ Ӭଓσʔλʔ ʢ'JMFͱ͔%#ʣ
֤छ ηϯαʔ 🤖 🐲
ͦͦiOSΞϓϦ֎෦࿈ܞ͔Γ ࣗͨͪͷαʔόʔ ·ͨ8FC"1*Λެ։ͯ͘͠ΕͯΔ8FCαʔϏε ·ͨ'JSFCBTFͳͲͷαʔόʔϨεͳͲ "QQMFͷϑϨʔϜϫʔΫ ࣗͨͪͷιʔείʔυ 044ͷϑϨʔϜϫʔΫ ʮͤΊͯࣗͷॻ͍ͨίʔυݫີʹৼΔ͏Α͏ʹ͍ͨ͠ʯ Ӭଓσʔλʔ ʢ'JMFͱ͔%#ʣ
֤छ ηϯαʔ ʮUIͷૢ࡞͕લఏͳΒࢮ͵΄Ͳखಈςετͨ͠ΒΧόʔͰ͖Δͷʹ…ʯ 🤖 🐲
ͪΖΜ ͲΜͳςετͰॻ͚ྑ͍Θ͚Ͱͳ͘ ѱ͍ςετΛॻ͍͍͚ͯͳ͍
ྑ͍ςετͷରଆʹ͋Δѱ͍ςετ • ཧղͮ͠Β͍ςετίʔυ • ඞཁҎ্ʹ͍ςετίʔυ • յΕ͍͢ςετ • ςετ݁Ռ͕ෆ҆ఆͳςετ 🐲
ྑ͍ςετͷରଆʹ͋Δѱ͍ςετ • ཧղͮ͠Β͍ςετίʔυ • ඞཁҎ্ʹ͍ςετίʔυ • յΕ͍͢ςετ • ςετ݁Ռ͕ෆ҆ఆͳςετ ʮෆ҆ఆͳςετ݁ՌʹͳΔͱ։ൃऀͷʹཱͨͳ͍͕େ͖͍ʯ
🐲
ຊ ෆ҆ఆͳςετͷݪҼΛཧ͠ղܾ͢Δ
ࢲͷʮෆ҆ఆͳςετʯͷఆٛ 100%ޭ 100%ࣦഊ ಉҰͷςετίʔυͰ ޭͨ͠Γࣦഊͨ͠Γ ݁Ռ͕ෆ҆ఆ 🤖 🐲
ࢲͷʮෆ҆ఆͳςετʯͷఆٛ 100%ޭ 100%ࣦഊ ಉҰͷςετίʔυͰ ޭͨ͠Γࣦഊͨ͠Γ ݁Ռ͕ෆ҆ఆ ʮFlakyʢ৴པͰ͖ͳ͍ʣςετͬͯݴ͍͍͑ͷʹ…ʯ 🤖 🐲
ࢲͷʮෆ҆ఆͳςετʯͷఆٛ 100%ޭ 100%ࣦഊ ಉҰͷςετίʔυͰ ޭͨ͠Γࣦഊͨ͠Γ ݁Ռ͕ෆ҆ఆ ʮFlakyʢ৴པͰ͖ͳ͍ʣςετͬͯݴ͍͍͑ͷʹ…ʯ ʮ݁Ռ͕ෆ҆ఆͱ͍͏ํ͕తͰ͖ʯ 🤖 🐲
ࠓճ͢ෆ҆ఆͳςετͷݪҼྨ • ݪҼ • ݪҼ1:࣮ߦॱংʹґଘ͢Δςετ • ݪҼ2: ࣮ߦ࣌·Ͱ͕ܾఆ͠ͳ͍࠶ݱੑͷͳ͍ςετ • ݪҼ3:
ϑϨʔϜϫʔΫཧղෆ • ݪҼ͕ද໘Խ͍͢͠: • CIͳͲͷςετ࣮ߦϚγϯͷϦιʔεෆ Swift 5.10Ͱݕূ
ݪҼ1: ࣮ߦॱংʹґଘ͢Δςετ • ྫ • ςετίʔυؒͰڞ༗ͷ/ࢀরΛ࣋ͬͯ͠·͏
ෆ҆ఆͳݪҼ: ࣮ߦॱংʹґଘ͢Δςετ 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") } } 🐲 🤖
ෆ҆ఆͳݪҼ: ࣮ߦॱংʹґଘ͢Δςετ 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") } } ʮγϯάϧτϯͳΜͯ Θͳ͚Ε͍͍ͷʹ…ʯ 🐲 🤖
ෆ҆ఆͳݪҼ: ࣮ߦॱংʹґଘ͢Δςετ 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() ΛͬͯॳظԽʯ 🐲 🤖
ෆ҆ఆͳݪҼ: ࣮ߦॱংʹґଘ͢Δςετ 🐲 ʮͪͳΈʹɺXCTestCaseͷϓϩύςΟࣗಈͰॳظԽ͞Ε·͢ʯ 🤖 🐲
ෆ҆ఆͳݪҼ: ࣮ߦॱংʹґଘ͢Δςετ 🐲 ʮͪͳΈʹɺXCTestCaseͷϓϩύςΟࣗಈͰॳظԽ͞Ε·͢ʯ ʮclassͳͷͰ༧͔ͭͳ͍ ͷͰ͓͢͢ΊͰ͖ͳ͍…ʯ 🤖 🐲
ෆ҆ఆͳݪҼ: ࣮ߦॱংʹґଘ͢Δςετ 🐲 ʮͪͳΈʹɺXCTestCaseͷϓϩύςΟࣗಈͰॳظԽ͞Ε·͢ʯ ʮclassͳͷͰ༧͔ͭͳ͍ ͷͰ͓͢͢ΊͰ͖ͳ͍…ʯ 🤖 ʮ࣍ੈͷSwift Testing Ͱ
ͨΓલʹͳΓ·͢Αʯ 🐲
ݪҼ2: ࣮ߦ࣌ʹ͕ܾఆ͢Δಈతͳॲཧͷςετ • ྫ • Date() / Date.init() / Date.now
• ݺͼग़͞Εͨࡍͷ࣌ؒΛऔಘ͢Δίʔυ
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 // ڐ༰͢Δޡࠩൣғ ) } } ෆ҆ఆͳݪҼ: ࣮ߦ࣌ʹ͕ܾఆ͢Δಈతͳॲཧͷςετ 🤖
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ͷϚγϯύϫʔʹ ɹґଘ͢Δͷʹ…ʯ 🤖
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 // ڐ༰͢Δޡࠩൣғ ) } } ෆ҆ఆͳݪҼ: ࣮ߦ࣌ʹ͕ܾఆ͢Δಈతͳॲཧͷςετ
ิ: ࣮ߦ࣌ʹಈతʹ͕ܾఆ͢ΔͭΒ • Foundation.Date / Calendar / Locale / UUID
• ͳͲͷཚੜ https://github.com/pointfreeco/swift-dependencies/tree/main/ Sources/Dependencies/DependencyValues ࢀߟ: pointfreeco/swift-dependencies ෆ҆ఆͳݪҼ: ࣮ߦ࣌ʹ͕ܾఆ͢Δಈతͳॲཧͷςετ
ݪҼ3: OSSϑϨʔϜϫʔΫཧղෆ • ྫ • ҙਤͤͣRealm͕εϨουؒΛӽ͑ͯ͠·͏ • ผεϨουͰRealmͷ݁ՌΛrefreshͤͣݕূͯ͠͠·͏
ෆ҆ఆͳݪҼ:OSSϑϨʔϜϫʔΫཧղෆ https://developer.apple.com/documentation/xctest/asynchronous_tests_and_expectations جૅ: XCTestͷglobalActorࢦఆ༗ແͷڍಈͷҧ͍ 🤖 🐲 🐲
ෆ҆ఆͳݪҼ:OSSϑϨʔϜϫʔΫཧղෆ https://developer.apple.com/documentation/xctest/asynchronous_tests_and_expectations ʮMainActorࢦఆ ɹϝΠϯεϨουʯ جૅ: XCTestͷglobalActorࢦఆ༗ແͷڍಈͷҧ͍ 🤖 🐲 🐲
ෆ҆ఆͳݪҼ:OSSϑϨʔϜϫʔΫཧղෆ https://developer.apple.com/documentation/xctest/asynchronous_tests_and_expectations ʮMainActorࢦఆ ɹϝΠϯεϨουʯ ʮແࢦఆϝΠϯ ɹεϨουͰͳ͍ʯ جૅ: XCTestͷglobalActorࢦఆ༗ແͷڍಈͷҧ͍ 🤖 🐲
🐲
ෆ҆ఆͳݪҼ:OSSϑϨʔϜϫʔΫཧղෆ https://developer.apple.com/documentation/xctest/asynchronous_tests_and_expectations ʮMainActorࢦఆ ɹϝΠϯεϨουʯ ʮແࢦఆϝΠϯ ɹεϨουͰͳ͍ʯ جૅ: XCTestͷglobalActorࢦఆ༗ແͷڍಈͷҧ͍ ʮެࣜυΩϡϝϯτʹॻ͔ΕͯΔͷʹ…ʯ 🤖
🐲 🐲
@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ݺͼग़͢ͱ࣮ߦ εϨουঢ়گʹࠨӈ͞ΕΔʢεϨουϓʔϧ͔ΒબΕΔʣ 🐲
@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ݺͼग़͢ͱ࣮ߦ εϨουঢ়گʹࠨӈ͞ΕΔʢεϨουϓʔϧ͔ΒબΕΔʣ 🐲
@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ݺͼग़͢ͱ࣮ߦ εϨουঢ়گʹࠨӈ͞ΕΔʢεϨουϓʔϧ͔ΒબΕΔʣ 🐲
ෆ҆ఆͳݪҼ: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") ) } } 🐲
ෆ҆ఆͳݪҼ: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") ) } } 🐲
ෆ҆ఆͳݪҼ: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") ) } } 🐲
ෆ҆ఆͳݪҼ: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") ) } } 🐲
ෆ҆ఆͳݪҼ: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") ) } } 🐲
ෆ҆ఆͳݪҼ: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͕ผεϨουͰར༻͞ΕΔ 🚫 ࣦഊ 🐲
ෆ҆ఆͳݪҼ:OSSϑϨʔϜϫʔΫཧղෆ ྫ: ผεϨουͷRealmͷॻ͖ࠐΈΛ͚ͭͩͰ ·ͩಉظ͍ྃͯ͠ͳ͍ ಉظλΠϛϯά खಈSFGSFTI ·ͨ࣌ؒܦա ·ͨ BVUPSFGSFTIʢϝΠϯεϨουͷσϑΥϧτʣ ·ͨ
͓ͦΒͦ͘ͷଞ͍Ζ͍ΖͳλΠϛϯά ͦΕͱผ εϨουͷ Realm ผεϨουͷ Realm 🐲
ෆ҆ఆͳݪҼ:OSSϑϨʔϜϫʔΫཧղෆ ྫ: ผεϨουͷRealmͷॻ͖ࠐΈΛ͚ͭͩͰ ·ͩಉظ͍ྃͯ͠ͳ͍ ಉظλΠϛϯά खಈSFGSFTI ·ͨ࣌ؒܦա ·ͨ BVUPSFGSFTIʢϝΠϯεϨουͷσϑΥϧτʣ ·ͨ
͓ͦΒͦ͘ͷଞ͍Ζ͍ΖͳλΠϛϯά ͦΕͱผ εϨουͷ Realm ผεϨουͷ Realm Realmͷॻ͖ࠐΈΛݕূ͢Δ݁Ռ͕ ෆ҆ఆͳͱ͖ ಈ࡞͍ͯ͠ΔεϨουΛௐࠪ͠ɺ RealmͷυΩϡϝϯτͰ realm.refresh()ΛௐͯΈ͍ͨɻ 🐲
·ͱΊ
ෆ҆ఆͳ݁Ռͷ࣌ʹؾ͕͖͍ͭͨ͜ͱ • ςετέʔεؒͰ/ࢀরΛ࠶ར༻ͯ͠ͳ͍͔ • ಈతʹܾ·ΔΛϩδοΫςετίʔυʹຒΊࠐΜͰ͍ͳ͍͔ • ࣮ߦεϨου͕ҙਤͤͣมΘͬͯ͠·͍ͬͯͳ͍͔
͓ΘΓʹ
ͦͷ΄͔ͷෆ҆ఆͷݪҼͱղܾͷ • ࠓճઆ໌লུʢผͷࢿྉ: ෆ҆ఆͳςετ200छྨ͋ΜͶΜʣ • https://speakerdeck.com/yimajo/bu-an-ding-natesutoha200zhong- lei-annen
͜ͷൃදͰ ؒҧ͍͕͋ͬͨΓ͝ҙݟ͋Εɺ ͳΔ͘ Θ͔Γ͘͢ࢦఠ࣭͚ͯ͠Δͱ ༗ҙٛͳΓͱΓ͕Ͱ͖ΔͣͰ͢ɻ ʢࢲͷࢥ͍ࠐΈͰղܾͨͭ͠Γʹͳ͍ͬͯΔ͜ͱ͋Γ·͢ʣ
͓ΘΓ 🐲 🤖 ͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠