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
テストの可読性を支える技術
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
ashdik
October 23, 2021
12
5.4k
テストの可読性を支える技術
テストの可読性は、とても大事です。
そんな可読性をあげてくれるパッケージや考え方などを解説したので
ぜひご覧いただければ幸いです。
ashdik
October 23, 2021
Tweet
Share
More Decks by ashdik
See All by ashdik
4年間変わらなかった YOUTRUSTのアーキテクチャ
daiki1003
3
2k
DevTools Extension 3分クッキング
daiki1003
0
1.1k
barrel_file_for_flutter.pdf
daiki1003
1
1.6k
YOUTRUSTアプリの構造、暴露してみた
daiki1003
7
3.8k
Featured
See All Featured
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
659
61k
Claude Code どこまでも/ Claude Code Everywhere
nwiizo
61
52k
Typedesign – Prime Four
hannesfritz
42
3k
What’s in a name? Adding method to the madness
productmarketing
PRO
24
3.9k
The Director’s Chair: Orchestrating AI for Truly Effective Learning
tmiket
1
100
WCS-LA-2024
lcolladotor
0
450
Docker and Python
trallard
47
3.7k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
128
55k
Visualization
eitanlees
150
17k
Why Your Marketing Sucks and What You Can Do About It - Sophie Logan
marketingsoph
0
77
How to Get Subject Matter Experts Bought In and Actively Contributing to SEO & PR Initiatives.
livdayseo
0
67
Testing 201, or: Great Expectations
jmmastey
46
8.1k
Transcript
ேେथ ςετͷՄಡੑ Λࢧ͑Δٕज़
ேେथ 5XJUUFSɿ!EBJLJ 'MVUUFSྺɿ ϒϩάɿIUUQTCMPHEBMUNF
࣍ w ςετՄಡੑ͕େࣄ w ςετͷՄಡੑ ฐࣾͷϏϑΥʔΞϑλʔ w ςετ݁ՌͷՄಡੑ
ࠓͷඪ ͎͘ʙɺςετͷՄಡੑ͋͛ͯ͐ʙ
ຊͷ༰ IUUQTHJUIVCDPNEBJLJ fl VUUFS@UFTU@XJUI@SFBEBCJMJUZ
ຊͷ༰
ຊͷྲྀΕ
Կҙࣝͤͣॻ͍ͨ ςετ͕͋Δ
None
͜ΕΛ ͭͷύοέʔδͳͲΛ ͬͯՄಡੑΛ্͛ͯΈΑ͏ʂ
ͱɺͦͷલʹʂ
ͦͦ ςετʹՄಡੑඞཁ͔ʁ
:&4ʂ
ςετʹՄಡੑඞཁ͔ʁ w ςετίʔυͷઆ໌ॻ w ͪΖΜਖ਼͘͠ಈ͔͘Ͳ͏͔Λ୲อ͢Δͱ͍͏ଆ໘͋Δ ͦͷҰํͰɺ w ࢲ͜͏͍͏෩ʹಈ͘ίʔυΛॻ͍ͨΑ w ͜͏͍͏ೖྗΛఆ͍ͯ͠ΔΑ
w ͜͏͍͏࣌ΤϥʔΛग़͢Α ͱ͍͏આ໌ॻɺҙࢥද໌ʹͳ͍ͬͯΔ
ςετͷՄಡੑ͕͍ ʹ Կ͕ݴ͍͍ͨͷ͔͔Βͳ͍ ςετʹՄಡੑඞཁ͔ʁ
͔ͩΒɺςετ ಡΈ͘͢ॻ͜͏ͳʁ ݁
ςετͷՄಡੑ w HSPVQ w HJWFO@XIFO@UIFO w fl VUUFS@UFTU@VJ
HSPVQ
ςετͷՄಡੑ w ͝ଘͷํଟ͍ͣ w fl VUUFS@UFTUʹಉࠝ͞Ε͍ͯΔϝιου w ͦͷ໊ͷ௨ΓɺෳͷςετΛάϧʔϓԽग़དྷΔ HSPVQ
None
JODSFNFOUCVUUPO EFDSFNFOUCVUUPO
ςετͷՄಡੑ
ςετͷՄಡੑ group('[Increment Button]', () { group('label shows "0"', () {
testWidgets('label shows "1"', (WidgetTester tester) async { }); }); group('label shows "1"', () { testWidgets('label shows "2"', (WidgetTester tester) async { }); }); });
ςετͷՄಡੑ group('[Decrement Button]', () { group('label shows "1"', () {
testWidgets('label shows "0"', (WidgetTester tester) async { }); }); group('label shows "2"', () { testWidgets('label shows "1"', (WidgetTester tester) async { }); }); });
HSPVQ ݸॏͳͬͱΔΜ͚Ͳʁ
ςετͷՄಡੑ group('[Increment Button]', () { group('label shows "0"', () {
testWidgets('label shows "1"', (WidgetTester tester) async { }); }); group('label shows "1"', () { testWidgets('label shows "2"', (WidgetTester tester) async { }); }); });
ςετͷՄಡੑ group('[Increment Button]', () { ← describe group('label shows "0"',
() { ← context testWidgets('label shows "1"' ← it, (WidgetTester tester) async { }); }); group('label shows "1"', () { testWidgets('label shows "2"', (WidgetTester tester) async { }); }); });
ςετͷՄಡੑ w 34QFDͰ࠾༻͞Ε͍ͯΔه๏ w EFTDSJCF ɹςετର w DPOUFYU ɹςετΛߦ͏લఏ݅ w
JU ɹͲ͏ͳ͍ͬͯΔ͖͔ JUTIPVMECFͷҙຯͷJU EFTDSJCF DPOUFYU JUFYBNQMF
ςετͷՄಡੑ group('[Increment Button]', () { ← describe group('label shows "0"',
() { ← context testWidgets('label shows "1"' ← it, (WidgetTester tester) async { }); }); group('label shows "1"', () { testWidgets('label shows "2"', (WidgetTester tester) async { }); }); });
ςετͷՄಡੑ group('[Increment Button]', () { ← describe group('label shows "0"',
() { ← context testWidgets('label shows "1"' ← it, (WidgetTester tester) async { }); }); group('label shows "1"', () { testWidgets('label shows "2"', (WidgetTester tester) async { }); }); });
ςετͷՄಡੑ group('[Increment Button]', () { ← describe group('label shows "0"',
() { ← context testWidgets('label shows "1"' ← it, (WidgetTester tester) async { }); }); group('label shows "1"', () { testWidgets('label shows "2"', (WidgetTester tester) async { }); }); });
ςετͷՄಡੑ group('[Increment Button]', () { group('label shows "0"', () {
testWidgets('label shows "1"', (WidgetTester tester) async { }); }); group('label shows "1"', () { testWidgets('label shows "2"', (WidgetTester tester) async { }); }); });
HJWFO@XIFO@UIFO
ςετͷՄಡੑ ςετͬͯ݁ہͷͱ͜Ζ͜ΜͳߏͰΓཱ͍ͬͯΔ HJWFO@XIFO@UIFO
ςετͷՄಡੑ ͋Δ݅Ͱɺ͋Δಈ࡞Λͨ͠Β͜͏ͳͬͯ΄͍͠ HJWFO@XIFO@UIFO
ςετͷՄಡੑ ͋Δ݅Ͱɺ͋Δಈ࡞Λͨ͠Β͜͏ͳͬͯ΄͍͠ HJWFO@XIFO@UIFO HJWFO
ςετͷՄಡੑ ͋Δ݅Ͱɺ͋Δಈ࡞Λͨ͠Β͜͏ͳͬͯ΄͍͠ HJWFO@XIFO@UIFO XIFO HJWFO
ςετͷՄಡੑ ͋Δ݅Ͱɺ͋Δಈ࡞Λͨ͠Β͜͏ͳͬͯ΄͍͠ HJWFO@XIFO@UIFO UIFO XIFO HJWFO
ྫ͑
ςετͷՄಡੑ ͕දࣔ͞Ε͍ͯΔ࣌ʹ ϓϥεϘλϯΛԡͨ͠Β ͕දࣔ͞Ε͍ͯͯཉ͍͠ HJWFO@XIFO@UIFO HJWFO
ςετͷՄಡੑ ͕දࣔ͞Ε͍ͯΔ࣌ʹ ϓϥεϘλϯΛԡͨ͠Β ͕දࣔ͞Ε͍ͯͯཉ͍͠ HJWFO@XIFO@UIFO HJWFO XIFO
ςετͷՄಡੑ ͕දࣔ͞Ε͍ͯΔ࣌ʹ ϓϥεϘλϯΛԡͨ͠Β ͕දࣔ͞Ε͍ͯͯཉ͍͠ HJWFO@XIFO@UIFO HJWFO XIFO UIFO
ςετͷՄಡੑ HJWFO@XIFO@UIFO testWidgets('label shows "1"', harness((given, when, then) async {
{ await given.pumpMyApp(); given.canFindZero(); given.cannotFindOne(); } { await when.userTapsIncrementButton(); } { then.cannotFindZero(); then.canFindOne(); } }), );
ςετͷՄಡੑ HJWFO@XIFO@UIFO testWidgets('label shows "1"', harness((given, when, then) async {
{ await given.pumpMyApp(); given.canFindZero(); given.cannotFindOne(); } { await when.userTapsIncrementButton(); } { then.cannotFindZero(); then.canFindOne(); } }), ); ͕දࣔ͞Ε͍ͯΔ࣌ʹ
ςετͷՄಡੑ HJWFO@XIFO@UIFO testWidgets('label shows "1"', harness((given, when, then) async {
{ await given.pumpMyApp(); given.canFindZero(); given.cannotFindOne(); } { await when.userTapsIncrementButton(); } { then.cannotFindZero(); then.canFindOne(); } }), ); ϓϥεϘλϯ͕ԡ͞ΕͨΒ ͕දࣔ͞Ε͍ͯΔ࣌ʹ
ςετͷՄಡੑ HJWFO@XIFO@UIFO testWidgets('label shows "1"', harness((given, when, then) async {
{ await given.pumpMyApp(); given.canFindZero(); given.cannotFindOne(); } { await when.userTapsIncrementButton(); } { then.cannotFindZero(); then.canFindOne(); } }), ); ϓϥεϘλϯ͕ԡ͞ΕͨΒ ͕දࣔ͞Ε͍ͯΔ࣌ʹ ͕දࣔ͞Ε͍ͯͯཉ͍͠
ςετͷՄಡੑ ղઆ HJWFO@XIFO@UIFO
ςετͷՄಡੑ HJWFO@XIFO@UIFO testWidgets('label shows "1"', harness((given, when, then) async {
{ await given.pumpMyApp(); given.canFindZero(); given.cannotFindOne(); } { await when.userTapsIncrementButton(); } { then.cannotFindZero(); then.canFindOne(); } }), );
ςετͷՄಡੑ HJWFO@XIFO@UIFO
ςετͷՄಡੑ HJWFO@XIFO@UIFO extension MyAppGiven on WidgetTestGiven<_MyAppTestHarness> { Future<void> pumpMyApp() async
{ await tester.pumpWidget(const MyApp()); } Future<void> increment() async { await tester.tap(find.byKey(incrementKey)); } Future<void> pump() async { await tester.pump(); } void canFindZero() { expect(find.text('0'), findsOneWidget); } void cannotFindZero() { expect(find.text('0'), findsNothing); }}
ςετͷՄಡੑ HJWFO@XIFO@UIFO extension MyAppWhen on WidgetTestWhen<_MyAppTestHarness> { Future<void> userTapsIncrementButton() async
{ await tester.tap(find.byKey(incrementKey)); await tester.pump(); } Future<void> userTapsDecrementButton() async { await tester.tap(find.byKey(decrementKey)); await tester.pump(); } }
ςετͷՄಡੑ HJWFO@XIFO@UIFO extension MyAppThen on WidgetTestThen<_MyAppTestHarness> { void canFindZero() {
expect(find.text('0'), findsOneWidget); } void cannotFindZero() { expect(find.text('0'), findsNothing); } void canFindOne() { expect(find.text('1'), findsOneWidget); } void cannotFindOne() { expect(find.text('1'), findsNothing); } void canFindTwo() { expect(find.text('2'), findsOneWidget); } void cannotFindTwo() { expect(find.text('2'), findsNothing); } }
ςετͷՄಡੑ HJWFO@XIFO@UIFO testWidgets('label shows "1"', harness((given, when, then) async {
{ await given.pumpMyApp(); given.canFindZero(); given.cannotFindOne(); } { await when.userTapsIncrementButton(); } { then.cannotFindZero(); then.canFindOne(); } }), );
ςετͷՄಡੑ HJWFO@XIFO@UIFO testWidgets('label shows "2"', harness((given, when, then) async {
{ await given.pumpMyApp(); await given.increment(); await given.pump(); given.canFindOne(); given.cannotFindTwo(); } { await when.userTapsIncrementButton(); } { then.cannotFindOne(); then.canFindTwo(); } }));
testWidgets('label shows "1"', harness((given, when, then) async { { await
given.pumpMyApp(); await given.increment(); await given.increment(); await given.pump(); given.canFindTwo(); given.cannotFindOne(); } { await when.userTapsDecrementButton(); } { then.cannotFindTwo(); then.canFindOne(); } })); ςετͷՄಡੑ HJWFO@XIFO@UIFO
ςετͷՄಡੑ HJWFO@XIFO@UIFO ͪΐͬͱେม͡ΌͶʁ
ςετͷՄಡੑ HJWFO@XIFO@UIFOͷ؆қ൛ testWidgets('label shows "1"', (WidgetTester tester) async { //
given { await tester.pumpWidget(const MyApp()); expect(find.text('0'), findsOneWidget); expect(find.text('1'), findsNothing); } // when { await tester.tap(find.byKey(incrementKey)); await tester.pump(); } // then { expect(find.text('0'), findsNothing); expect(find.text('1'), findsOneWidget); } });
fl VUUFS@UFTU@VJ
ςετͷՄಡੑ GMVUUFS@UFTU@VJ void main() { setUpUI((tester) async { await tester.pumpWidget(const
MyApp()); }); testUI('label shows "1"', harness((given, when, then) async { { given.canFindZero(); given.cannotFindOne(); } { await when.userTapsIncrementButton(); } { then.cannotFindZero(); then.canFindOne(); } })); }
ςετͷՄಡੑ GMVUUFS@UFTU@VJ w testUI, setupUI, tearDownUIͳͲɺ6*ςετ༻ͷϝιουΛఏڙ
ςετͷՄಡੑ GMVUUFS@UFTU@VJ testWidgets('label shows "1"', harness((given, when, then) async {
{ await given.pumpMyApp(); given.canFindZero(); given.cannotFindOne(); } { await when.userTapsIncrementButton(); } { then.cannotFindZero(); then.canFindOne(); } }), );
ςετͷՄಡੑ HJWFO@XIFO@UIFO testWidgets('label shows "1"', harness((given, when, then) async {
{ await given.pumpMyApp(); given.canFindZero(); given.cannotFindOne(); } { await when.userTapsIncrementButton(); } { then.cannotFindZero(); then.canFindOne(); } }), );
ςετͷՄಡੑ GMVUUFS@UFTU@VJ void main() { setUpUI((tester) async { await tester.pumpWidget(const
MyApp()); }); testUI('label shows "1"', harness((given, when, then) async { { given.canFindZero(); given.cannotFindOne(); } { await when.userTapsIncrementButton(); } { then.cannotFindZero(); then.canFindOne(); } })); }
ςετͷՄಡੑ GMVUUFS@UFTU@VJ void main() { setUpUI((tester) async { await tester.pumpWidget(const
MyApp()); }); testUI('label shows "1"', harness((given, when, then) async { { given.canFindZero(); given.cannotFindOne(); } { await when.userTapsIncrementButton(); } { then.cannotFindZero(); then.canFindOne(); } })); }
ςετͷՄಡੑ GMVUUFS@UFTU@VJ void main() { setUpUI((tester) async { await tester.pumpWidget(const
MyApp()); }); testUI('label shows "1"', harness((given, when, then) async { { given.canFindZero(); given.cannotFindOne(); } { await when.userTapsIncrementButton(); } { then.cannotFindZero(); then.canFindOne(); } })); }
group('label shows "2"', () { setUpUI((tester) async { await tester.tap(find.byKey(incrementKey));
await tester.tap(find.byKey(incrementKey)); await tester.pump(); }); testUI('label shows "1"', harness((given, when, then) async { { given.canFindTwo(); given.cannotFindOne(); } { await when.userTapsDecrementButton(); } { then.cannotFindTwo(); then.canFindOne(); } })); }); ςετͷՄಡੑ GMVUUFS@UFTU@VJ
ฐࣾͰͷϏϑΥʔΞϑλʔ
ฐࣾͰͷϏϑΥʔΞϑλʔ "3FRVFTU #3FRVFTU $3FRVFTU "QJ$MJFOU "3FTQPOTF #3FTQPOTF $3FTQPOTF $PNNBOEύλʔϯ
"QJ 3FRV FTU
"3FRVFTU #3FRVFTU $3FRVFTU "QJ$MJFOU "3FTQPOTF #3FTQPOTF $3FTQPOTF $PNNBOEύλʔϯ "QJ
3FRV FTU ฐࣾͰͷϏϑΥʔΞϑλʔ
"3FRVFTU #3FRVFTU $3FRVFTU "QJ$MJFOU "3FTQPOTF #3FTQPOTF $3FTQPOTF $PNNBOEύλʔϯ "QJ
3FRV FTU ฐࣾͰͷϏϑΥʔΞϑλʔ
"3FRVFTU #3FRVFTU $3FRVFTU "QJ$MJFOU "3FTQPOTF #3FTQPOTF $3FTQPOTF $PNNBOEύλʔϯ "QJ
3FRV FTU ฐࣾͰͷϏϑΥʔΞϑλʔ
ฐࣾͰͷϏϑΥʔΞϑλʔ
ฐࣾͰͷϏϑΥʔΞϑλʔ
ฐࣾͰͷϏϑΥʔΞϑλʔ
ฐࣾͰͷϏϑΥʔΞϑλʔ
ฐࣾͰͷϏϑΥʔΞϑλʔ ςετΛͭՃ͢ΔͨΊʹ༷ʑͳϑΝΠϧΛՃมߋ͢Δඞཁ͕͋Δ w UFTUSFTPVSDFTԼ w KTPOͷՃ w 'BLF"QJ$MJFOU w ϞοΫύεͷઃఆ
w ΕΔͱImplementationError w xxxTest σϝϦοτ
ฐࣾͰͷϏϑΥʔΞϑλʔ ͪΐͬͱ͚ͩҧ͏ϨεϙϯεΛ࡞Δͷʹ৽ͨͳKTPO͕ඞཁ ɾ༑ୡͷ༗ແ ɾϑΥϩʔঢ়ଶ ɾϒϩοΫঢ়ଶ σϝϦοτ
ฐࣾͰͷϏϑΥʔΞϑλʔ σϝϦοτ มߋʹରͯ͠ίϯύΠϧηʔϑͰͳ͍ ɾ6TFS͔ΒϓϩύςΟΛҰͭݮΒͨ͠ ɹɹKTPOม͑ͳ͖Ό͍͚ͳ͍͜ͱͳΜͯ୭ؾ͔ͳ͍ΑͶস
Ξϑλʔ
ฐࣾͰͷϏϑΥʔΞϑλʔ Ξϑλʔ typedef FakeApiResponder = dynamic Function(ApiRequest<dynamic>);
ฐࣾͰͷϏϑΥʔΞϑλʔ Ξϑλʔ FakeApiClientImpl appending({ required FakeApiResponder responder, }) { return
FakeApiClientImpl._( responders: [..._responders, responder], ); }
)PXUPVTFʁ
ฐࣾͰͷϏϑΥʔΞϑλʔ Ξϑλʔ void main() { final baseApiClient = FakeApiClientImpl.create(); setUpAll(TestWidgetsFlutterBinding.ensureInitialized);
// Test cases... }
ฐࣾͰͷϏϑΥʔΞϑλʔ Ξϑλʔ final apiClient = baseApiClient.appending( responder: (request) { if
(request is UsersRequest) { return _Fixtures.createUser( id: 'some_user_id', ); } }, );
ฐࣾͰͷϏϑΥʔΞϑλʔ Ξϑλʔ class _Fixtures { static User createUser() { return
User(id: 'hoge', name: 'ashdik'); } }
ฐࣾͰͷϏϑΥʔΞϑλʔ ςετΛͭՃ͢ΔͨΊʹ༷ʑͳϑΝΠϧΛՃมߋ͢Δඞཁ͕͋Δ ɹˠͦΕͧΕͷςετͷΈʂ ͪΐͬͱ͚ͩҧ͏ϨεϙϯεΛ࡞Δͷʹ৽ͨͳKTPO͕ඞཁ ɹˠ@'JYUVSFͷҾʹม͍͑ͨΛऔΔ͚ͩ มߋʹରͯ͠ίϯύΠϧηʔϑͰͳ͍ ɹˠ6TFSΫϥεΛΠϯελϯεԽ͍ͯ͠Δ σϝϦοτ
ςετ݁ՌͷՄಡੑ
EBSU@EPU@SFQPSUFS
ςετ݁ՌͷՄಡੑ w σϑΥϧτͰ ओʹࣦഊ࣌ ಡΈͮΒ͍ʜ GMVUUFSUFTU
ςετ݁ՌͷՄಡੑ w ಡΈ͍͢🎉
ςετ݁ՌͷՄಡੑ w EPU@SFQPSUFSΠϯεύΠΞ w ςετ݁ՌΛݟͯ͘͘͢͠ΕΔ w ʮʯ ɹޭ w ʮ9ʯ
ɹࣦഊ w ʮʯ ɹεΩοϓ EBSU@EPU@SFQPSUFS
ςετ݁ՌͷՄಡੑ ͩΜͩΜಡΈͯ͘͢͠ΈΑ͏ GMVUUFSUFTUNBDIJOF
w ҎԼ2εςοϓ ςετ݁ՌͷՄಡੑ w pubspec.yamlʹdart_dot_reporterΛՃ EBSU@EPU@SFQPSUFSɹʙԼ४උʙ GMVUUFSQVCHMPCBMBDUJWBUFEBSU@EPU@SFQPSUFS
ςετ݁ՌͷՄಡੑ ͩΜͩΜಡΈͯ͘͢͠ΈΑ͏ GMVUUFSUFTUNBDIJOFNBDIJOFMPH GMVUUFSQVCHMPCBMSVOEBSU@EPU@SFQPSUFSNBDIJOFMPH
ςετ݁ՌͷՄಡੑ ͩΜͩΜಡΈͯ͘͢͠ΈΑ͏
ͦͷଞ w SJWFSQPE w fl VUUFS@HIFSLJO w NPDLJOHKBZ w NPDLJUP
w CEE@XJEHFU@UFTU w PHVSFUT հ͖͠Εͳ͔ͬͨύοέʔδͨͪ
ਖ਼ࣾһ Λݟਾ͑ͨۀҕୗͷํ ืूதʂ
͝ਗ਼ௌɺ͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ