Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
良いテストコードのために悪いテストコードを理解する - 不安定なテスト編: iOSアプリ開発...
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
yimajo
June 24, 2024
Programming
6.9k
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
片田舎のおっさん、 Swift Buildのダイアモンド問題解決の不具合修正PRを出すが、解決方法がキャッシュをしないようにすることであり、ビルド時間が伸びると言われてマージされないので高速化もする/swiftbuild
yimajo
0
390
OSSとなったswift-buildで Xcodeのビルドを差し替えられるため 自分でXcodeを直せる時代になっている ダイアモンド問題編
yimajo
3
930
いま中途半端なSwift 6対応をするより、Default ActorやApproachable Concurrencyを有効にしてからでいいんじゃない?
yimajo
2
750
11年かかって やっとVibe Codingに 時代が追いつきましたね
yimajo
1
410
iOSアプリ開発で 関数型プログラミングを実現する The Composable Architectureの紹介
yimajo
3
360
TCAの Shared Stateって どういう仕組みになってんの?
yimajo
0
2.8k
Swift 5.9 からの Observation はiOS17 未満 からも使えて struct の変更検知もできるんすかね?
yimajo
2
1.2k
不安定なテストは200種類あんねん
yimajo
3
1.3k
TCA v0.19.0からのSwitchStore/CaseLetが良い
yimajo
0
2.1k
Other Decks in Programming
See All in Programming
AI Agent時代のリアーキテクチャ戦略と実践
hokaccha
9
5.4k
Atomic Design, Enforced: Scaling Mobile Design Systems (next.app devCon / droidCon Berlin 2026)
steliosf
PRO
0
120
Simple Storage Service(S3) is not simple
iwatsukayura
0
190
Findy - エンジニア向け会社紹介/Findy Company Deck
findyinc
6
400k
UPDATE をやめる — EF Core でマスタをバージョン管理する
panda728
PRO
0
1.1k
半永久的に提供し続けられるプライベートクラウドを目指して ― 利用者の認知負荷を抑えるAPI抽象化とハードウェア世代交代の基盤設計
tomokon
0
440
Everything will be SERVERLESS — 信じて運用した10年の経験値 / Everything Will be Serverless — Lessons Learned from 10 Years of Operational Experience
seike460
PRO
1
740
JRuby: Past, Present, and Future
headius
0
220
Workers Cache を知る
syumai
0
330
AI時代のコードレビューは人に向けるな、仕組みに向けろ
texmeijin
5
3.5k
Webの地図
yosuke_furukawa
PRO
6
5k
Unity6 URPによる海底をテーマにしたVJシステムの解説 | Unity Shader 完全に理解した 勉強会
gam0022
0
320
Featured
See All Featured
The innovator’s Mindset - Leading Through an Era of Exponential Change - McGill University 2025
jdejongh
PRO
1
360
Done Done
chrislema
187
17k
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
16
2.2k
GraphQLの誤解/rethinking-graphql
sonatard
75
12k
The MySQL Ecosystem @ GitHub 2015
samlambert
251
13k
Leo the Paperboy
mayatellez
10
2.3k
Are puppies a ranking factor?
jonoalderson
2
4k
Chasing Engaging Ingredients in Design
codingconduct
0
340
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
141
35k
Build The Right Thing And Hit Your Dates
maggiecrowley
39
3.5k
XXLCSS - How to scale CSS and keep your sanity
sugarenia
250
1.3M
How to Think Like a Performance Engineer
csswizardry
28
2.9k
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
͜ͷൃදͰ ؒҧ͍͕͋ͬͨΓ͝ҙݟ͋Εɺ ͳΔ͘ Θ͔Γ͘͢ࢦఠ࣭͚ͯ͠Δͱ ༗ҙٛͳΓͱΓ͕Ͱ͖ΔͣͰ͢ɻ ʢࢲͷࢥ͍ࠐΈͰղܾͨͭ͠Γʹͳ͍ͬͯΔ͜ͱ͋Γ·͢ʣ
͓ΘΓ 🐲 🤖 ͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠