テストの可読性は、とても大事です。 そんな可読性をあげてくれるパッケージや考え方などを解説したので ぜひご覧いただければ幸いです。
ேେथςετͷՄಡੑΛࢧ͑Δٕज़
View Slide
ேେथ5XJUUFSɿ!EBJLJ'MVUUFSྺɿϒϩάɿIUUQTCMPHEBMUNF
࣍w ςετՄಡੑ͕େࣄw ςετͷՄಡੑฐࣾͷϏϑΥʔΞϑλʔw ςετ݁ՌͷՄಡੑ
ࠓͷඪ͎͘ʙɺςετͷՄಡੑ͋͛ͯ͐ʙ
ຊͷ༰IUUQTHJUIVCDPNEBJLJfl[email protected]@[email protected]
ຊͷ༰
ຊͷྲྀΕ
Կҙࣝͤͣॻ͍ͨςετ͕͋Δ
͜ΕΛͭͷύοέʔδͳͲΛͬͯՄಡੑΛ্͛ͯΈΑ͏ʂ
ͱɺͦͷલʹʂ
ͦͦςετʹՄಡੑඞཁ͔ʁ
:&4ʂ
ςετʹՄಡੑඞཁ͔ʁw ςετίʔυͷઆ໌ॻw ͪΖΜਖ਼͘͠ಈ͔͘Ͳ͏͔Λ୲อ͢Δͱ͍͏ଆ໘͋ΔͦͷҰํͰɺw ࢲ͜͏͍͏෩ʹಈ͘ίʔυΛॻ͍ͨΑw ͜͏͍͏ೖྗΛఆ͍ͯ͠ΔΑw ͜͏͍͏࣌ΤϥʔΛग़͢Αͱ͍͏આ໌ॻɺҙࢥද໌ʹͳ͍ͬͯΔ
ςετͷՄಡੑ͕͍ʹԿ͕ݴ͍͍ͨͷ͔͔Βͳ͍ςετʹՄಡੑඞཁ͔ʁ
͔ͩΒɺςετಡΈ͘͢ॻ͜͏ͳʁ݁
ςετͷՄಡੑw HSPVQw [email protected]@UIFOwfl[email protected]@VJ
HSPVQ
ςετͷՄಡੑw ͝ଘͷํଟ͍ͣwfl[email protected]ʹಉࠝ͞Ε͍ͯΔϝιουw ͦͷ໊ͷ௨ΓɺෳͷςετΛάϧʔϓԽग़དྷΔHSPVQ
JODSFNFOUCVUUPOEFDSFNFOUCVUUPO
ςετͷՄಡੑ
ςετͷՄಡੑ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]', () { ← describegroup('label shows "0"', () { ← contexttestWidgets('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ͷҙຯͷJUEFTDSJCF DPOUFYU JUFYBNQMF
[email protected]@UIFO
ςετͷՄಡੑςετͬͯ݁ہͷͱ͜Ζ͜ΜͳߏͰΓཱ͍ͬͯΔ[email protected]@UIFO
ςετͷՄಡੑ͋Δ݅Ͱɺ͋Δಈ࡞Λͨ͠Β͜͏ͳͬͯ΄͍͠[email protected]@UIFO
ςετͷՄಡੑ͋Δ݅Ͱɺ͋Δಈ࡞Λͨ͠Β͜͏ͳͬͯ΄͍͠[email protected]@UIFOHJWFO
ςετͷՄಡੑ͋Δ݅Ͱɺ͋Δಈ࡞Λͨ͠Β͜͏ͳͬͯ΄͍͠[email protected]@UIFOXIFOHJWFO
ςετͷՄಡੑ͋Δ݅Ͱɺ͋Δಈ࡞Λͨ͠Β͜͏ͳͬͯ΄͍͠[email protected]@UIFOUIFOXIFOHJWFO
ྫ͑
ςετͷՄಡੑ͕දࣔ͞Ε͍ͯΔ࣌ʹϓϥεϘλϯΛԡͨ͠Β͕දࣔ͞Ε͍ͯͯཉ͍͠[email protected]@UIFOHJWFO
ςετͷՄಡੑ͕දࣔ͞Ε͍ͯΔ࣌ʹϓϥεϘλϯΛԡͨ͠Β͕දࣔ͞Ε͍ͯͯཉ͍͠[email protected]@UIFOHJWFOXIFO
ςετͷՄಡੑ͕දࣔ͞Ε͍ͯΔ࣌ʹϓϥεϘλϯΛԡͨ͠Β͕දࣔ͞Ε͍ͯͯཉ͍͠[email protected]@UIFOHJWFOXIFOUIFO
ςετͷՄಡੑ[email protected]@UIFOtestWidgets('label shows "1"', harness((given, when, then) async {{await given.pumpMyApp();given.canFindZero();given.cannotFindOne();}{await when.userTapsIncrementButton();}{then.cannotFindZero();then.canFindOne();}}),);
ςετͷՄಡੑ[email protected]@UIFOtestWidgets('label shows "1"', harness((given, when, then) async {{await given.pumpMyApp();given.canFindZero();given.cannotFindOne();}{await when.userTapsIncrementButton();}{then.cannotFindZero();then.canFindOne();}}),);͕දࣔ͞Ε͍ͯΔ࣌ʹ
ςετͷՄಡੑ[email protected]@UIFOtestWidgets('label shows "1"', harness((given, when, then) async {{await given.pumpMyApp();given.canFindZero();given.cannotFindOne();}{await when.userTapsIncrementButton();}{then.cannotFindZero();then.canFindOne();}}),);ϓϥεϘλϯ͕ԡ͞ΕͨΒ͕දࣔ͞Ε͍ͯΔ࣌ʹ
ςετͷՄಡੑ[email protected]@UIFOtestWidgets('label shows "1"', harness((given, when, then) async {{await given.pumpMyApp();given.canFindZero();given.cannotFindOne();}{await when.userTapsIncrementButton();}{then.cannotFindZero();then.canFindOne();}}),);ϓϥεϘλϯ͕ԡ͞ΕͨΒ͕දࣔ͞Ε͍ͯΔ࣌ʹ͕දࣔ͞Ε͍ͯͯཉ͍͠
ςετͷՄಡੑղઆ[email protected]@UIFO
ςετͷՄಡੑ[email protected]@UIFO
ςετͷՄಡੑ[email protected]@UIFOextension MyAppGiven on WidgetTestGiven {Future pumpMyApp() async {await tester.pumpWidget(const MyApp());}Future increment() async {await tester.tap(find.byKey(incrementKey));}Future pump() async {await tester.pump();}void canFindZero() {expect(find.text('0'), findsOneWidget);}void cannotFindZero() {expect(find.text('0'), findsNothing);}}
ςετͷՄಡੑ[email protected]@UIFOextension MyAppWhen on WidgetTestWhen {Future userTapsIncrementButton() async {await tester.tap(find.byKey(incrementKey));await tester.pump();}Future userTapsDecrementButton() async {await tester.tap(find.byKey(decrementKey));await tester.pump();}}
ςετͷՄಡੑ[email protected]@UIFOextension MyAppThen on WidgetTestThen {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);}}
ςετͷՄಡੑ[email protected]@UIFOtestWidgets('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();}}));ςετͷՄಡੑ[email protected]@UIFO
ςετͷՄಡੑ[email protected]@UIFOͪΐͬͱେม͡ΌͶʁ
ςετͷՄಡੑ[email protected]@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[email protected]@VJ
ςετͷՄಡੑ[email protected]@VJvoid 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();}}));}
ςετͷՄಡੑ[email protected]@VJw testUI, setupUI, tearDownUIͳͲɺ6*ςετ༻ͷϝιουΛఏڙ
ςετͷՄಡੑ[email protected]@VJtestWidgets('label shows "1"', harness((given, when, then) async {{await given.pumpMyApp();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();}}));});ςετͷՄಡੑ[email protected]@VJ
ฐࣾͰͷϏϑΥʔΞϑλʔ
ฐࣾͰͷϏϑΥʔΞϑλʔ"3FRVFTU#3FRVFTU$3FRVFTU"QJ$MJFOU"3FTQPOTF#3FTQPOTF$3FTQPOTF$PNNBOEύλʔϯ"QJ3FRVFTU
"3FRVFTU#3FRVFTU$3FRVFTU"QJ$MJFOU"3FTQPOTF#3FTQPOTF$3FTQPOTF$PNNBOEύλʔϯ"QJ3FRVFTUฐࣾͰͷϏϑΥʔΞϑλʔ
ฐࣾͰͷϏϑΥʔΞϑλʔςετΛͭՃ͢ΔͨΊʹ༷ʑͳϑΝΠϧΛՃมߋ͢Δඞཁ͕͋Δw UFTUSFTPVSDFTԼw KTPOͷՃw 'BLF"QJ$MJFOUw ϞοΫύεͷઃఆw ΕΔͱImplementationErrorw xxxTestσϝϦοτ
ฐࣾͰͷϏϑΥʔΞϑλʔͪΐͬͱ͚ͩҧ͏ϨεϙϯεΛ࡞Δͷʹ৽ͨͳKTPO͕ඞཁɾ༑ୡͷ༗ແɾϑΥϩʔঢ়ଶɾϒϩοΫঢ়ଶσϝϦοτ
ฐࣾͰͷϏϑΥʔΞϑλʔσϝϦοτมߋʹରͯ͠ίϯύΠϧηʔϑͰͳ͍ɾ6TFS͔ΒϓϩύςΟΛҰͭݮΒͨ͠ɹɹKTPOม͑ͳ͖Ό͍͚ͳ͍͜ͱͳΜͯ୭ؾ͔ͳ͍ΑͶস
Ξϑλʔ
ฐࣾͰͷϏϑΥʔΞϑλʔΞϑλʔtypedef FakeApiResponder = dynamic Function(ApiRequest);
ฐࣾͰͷϏϑΥʔΞϑλʔΞϑλʔ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ΫϥεΛΠϯελϯεԽ͍ͯ͠ΔσϝϦοτ
ςετ݁ՌͷՄಡੑ
[email protected]@SFQPSUFS
ςετ݁ՌͷՄಡੑw σϑΥϧτͰ ओʹࣦഊ࣌ಡΈͮΒ͍ʜGMVUUFSUFTU
ςετ݁ՌͷՄಡੑw ಡΈ͍͢🎉
ςετ݁ՌͷՄಡੑw [email protected]ΠϯεύΠΞw ςετ݁ՌΛݟͯ͘͘͢͠ΕΔw ʮʯɹޭw ʮ9ʯɹࣦഊw ʮʯɹεΩοϓ[email protected]@SFQPSUFS
ςετ݁ՌͷՄಡੑͩΜͩΜಡΈͯ͘͢͠ΈΑ͏GMVUUFSUFTUNBDIJOF
w ҎԼ2εςοϓςετ݁ՌͷՄಡੑw pubspec.yamlʹdart_dot_reporterΛՃ[email protected]@SFQPSUFSɹʙԼ४උʙ[email protected]@SFQPSUFS
ςετ݁ՌͷՄಡੑͩΜͩΜಡΈͯ͘͢͠ΈΑ͏GMVUUFSUFTUNBDIJOFNBDIJOFMPH[email protected]@SFQPSUFSNBDIJOFMPH
ςετ݁ՌͷՄಡੑͩΜͩΜಡΈͯ͘͢͠ΈΑ͏
ͦͷଞw SJWFSQPEwfl[email protected]w NPDLJOHKBZw NPDLJUPw [email protected]@UFTUw PHVSFUTհ͖͠Εͳ͔ͬͨύοέʔδͨͪ
ਖ਼ࣾһ Λݟਾ͑ͨۀҕୗͷํืूதʂ
͝ਗ਼ௌɺ͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ