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
ashdik
October 23, 2021
12
5.2k
テストの可読性を支える技術
テストの可読性は、とても大事です。
そんな可読性をあげてくれるパッケージや考え方などを解説したので
ぜひご覧いただければ幸いです。
ashdik
October 23, 2021
Tweet
Share
More Decks by ashdik
See All by ashdik
4年間変わらなかった YOUTRUSTのアーキテクチャ
daiki1003
3
1.6k
DevTools Extension 3分クッキング
daiki1003
0
1k
barrel_file_for_flutter.pdf
daiki1003
1
1.2k
YOUTRUSTアプリの構造、暴露してみた
daiki1003
7
3.4k
Featured
See All Featured
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
129
19k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
366
25k
GitHub's CSS Performance
jonrohan
1030
460k
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
100
18k
VelocityConf: Rendering Performance Case Studies
addyosmani
328
24k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
33
2.8k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
29
1k
The MySQL Ecosystem @ GitHub 2015
samlambert
250
12k
Making Projects Easy
brettharned
116
6k
Documentation Writing (for coders)
carmenintech
68
4.6k
Scaling GitHub
holman
459
140k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
PRO
13
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 հ͖͠Εͳ͔ͬͨύοέʔδͨͪ
ਖ਼ࣾһ Λݟਾ͑ͨۀҕୗͷํ ืूதʂ
͝ਗ਼ௌɺ͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ