Upgrade to Pro — share decks privately, control downloads, hide ads and more …

テストの可読性を支える技術

ashdik
October 23, 2021
4.7k

 テストの可読性を支える技術

テストの可読性は、とても大事です。
そんな可読性をあげてくれるパッケージや考え方などを解説したので
ぜひご覧いただければ幸いです。

ashdik

October 23, 2021
Tweet

Transcript

  1. ே೔େथ
    ςετͷՄಡੑ
    Λࢧ͑Δٕज़

    View Slide

  2. ே೔େथ
    5XJUUFSɿ!EBJLJ
    'MVUUFSྺɿ೥
    ϒϩάɿIUUQTCMPHEBMUNF

    View Slide

  3. ໨࣍
    w ςετ΋Մಡੑ͕େࣄ
    w ςετͷՄಡੑ
    ฐࣾͷϏϑΥʔΞϑλʔ
    w ςετ݁ՌͷՄಡੑ

    View Slide

  4. ࠓ೔ͷ໨ඪ
    ͎͘ʙɺςετͷՄಡੑ͋͛ͯ͐ʙ

    View Slide

  5. ຊ೔ͷ಺༰
    IUUQTHJUIVCDPNEBJLJ
    fl
    [email protected]@[email protected]

    View Slide

  6. ຊ೔ͷ಺༰

    View Slide

  7. ຊ೔ͷྲྀΕ

    View Slide

  8. Կ΋ҙࣝͤͣॻ͍ͨ
    ςετ͕͋Δ

    View Slide

  9. View Slide

  10. ͜ΕΛ
    ͭͷύοέʔδͳͲΛ
    ࢖ͬͯՄಡੑΛ্͛ͯΈΑ͏ʂ

    View Slide

  11. ͱɺͦͷલʹʂ

    View Slide

  12. ͦ΋ͦ΋
    ςετʹՄಡੑ͸ඞཁ͔ʁ

    View Slide

  13. :&4ʂ

    View Slide

  14. ςετʹՄಡੑ͸ඞཁ͔ʁ
    w ςετ͸ίʔυͷઆ໌ॻ
    w ΋ͪΖΜਖ਼͘͠ಈ͔͘Ͳ͏͔Λ୲อ͢Δͱ͍͏ଆ໘΋͋Δ
    ͦͷҰํͰɺ
    w ࢲ͸͜͏͍͏෩ʹಈ͘ίʔυΛॻ͍ͨΑ
    w ͜͏͍͏ೖྗ஋Λ૝ఆ͍ͯ͠ΔΑ
    w ͜͏͍͏࣌͸ΤϥʔΛग़͢Α
    ͱ͍͏આ໌ॻɺҙࢥද໌ʹ΋ͳ͍ͬͯΔ

    View Slide

  15. ςετͷՄಡੑ͕௿͍
    ʹ
    Կ͕ݴ͍͍ͨͷ͔෼͔Βͳ͍
    ςετʹՄಡੑ͸ඞཁ͔ʁ

    View Slide

  16. ͔ͩΒɺςετ͸
    ಡΈ΍͘͢ॻ͜͏ͳʁ
    ݁࿦

    View Slide

  17. ςετͷՄಡੑ
    w HSPVQ
    w [email protected]@UIFO
    w
    fl
    [email protected]@VJ

    View Slide

  18. HSPVQ

    View Slide

  19. ςετͷՄಡੑ
    w ͝ଘ஌ͷํ΋ଟ͍͸ͣ
    w
    fl
    [email protected]ʹಉࠝ͞Ε͍ͯΔϝιου
    w ͦͷ໊ͷ௨Γɺෳ਺ͷςετΛάϧʔϓԽग़དྷΔ
    HSPVQ

    View Slide

  20. View Slide

  21. JODSFNFOUCVUUPO
    EFDSFNFOUCVUUPO

    View Slide

  22. ςετͷՄಡੑ

    View Slide

  23. ςετͷՄಡੑ
    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 {




    });


    });


    });


    View Slide

  24. ςετͷՄಡੑ
    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 {




    });


    });


    });


    View Slide

  25. HSPVQ
    ݸॏͳͬͱΔΜ΍͚Ͳʁ

    View Slide

  26. ςετͷՄಡੑ
    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 {




    });


    });


    });


    View Slide

  27. ςετͷՄಡੑ
    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 {




    });


    });


    });


    View Slide

  28. ςετͷՄಡੑ
    w 34QFDͰ࠾༻͞Ε͍ͯΔه๏
    w EFTDSJCF
    ɹςετର৅
    w DPOUFYU
    ɹςετΛߦ͏લఏ৚݅
    w JU
    ɹͲ͏ͳ͍ͬͯΔ΂͖͔ JUTIPVMECFͷҙຯͷJU

    EFTDSJCF DPOUFYU JUFYBNQMF

    View Slide

  29. ςετͷՄಡੑ
    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 {




    });


    });


    });


    View Slide

  30. ςετͷՄಡੑ
    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 {




    });


    });


    });


    View Slide

  31. ςετͷՄಡੑ
    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 {




    });


    });


    });


    View Slide

  32. ςετͷՄಡੑ
    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 {




    });


    });


    });


    View Slide

  33. View Slide

  34. ςετͷՄಡੑ
    ςετͬͯ݁ہͷͱ͜Ζ͜Μͳߏ੒Ͱ੒Γཱ͍ͬͯΔ
    [email protected]@UIFO

    View Slide

  35. ςετͷՄಡੑ
    ͋Δ৚݅Ͱɺ͋Δಈ࡞Λͨ͠Β͜͏ͳͬͯ΄͍͠
    [email protected]@UIFO

    View Slide

  36. ςετͷՄಡੑ
    ͋Δ৚݅Ͱɺ͋Δಈ࡞Λͨ͠Β͜͏ͳͬͯ΄͍͠
    [email protected]@UIFO
    HJWFO

    View Slide

  37. ςετͷՄಡੑ
    ͋Δ৚݅Ͱɺ͋Δಈ࡞Λͨ͠Β͜͏ͳͬͯ΄͍͠
    [email protected]@UIFO
    XIFO
    HJWFO

    View Slide

  38. ςετͷՄಡੑ
    ͋Δ৚݅Ͱɺ͋Δಈ࡞Λͨ͠Β͜͏ͳͬͯ΄͍͠
    [email protected]@UIFO
    UIFO
    XIFO
    HJWFO

    View Slide

  39. ྫ͑͹

    View Slide

  40. ςετͷՄಡੑ
    ͕දࣔ͞Ε͍ͯΔ࣌ʹ
    ϓϥεϘλϯΛԡͨ͠Β
    ͕දࣔ͞Ε͍ͯͯཉ͍͠
    [email protected]@UIFO
    HJWFO

    View Slide

  41. ςετͷՄಡੑ
    ͕දࣔ͞Ε͍ͯΔ࣌ʹ
    ϓϥεϘλϯΛԡͨ͠Β
    ͕දࣔ͞Ε͍ͯͯཉ͍͠
    [email protected]@UIFO
    HJWFO
    XIFO

    View Slide

  42. ςετͷՄಡੑ
    ͕දࣔ͞Ε͍ͯΔ࣌ʹ
    ϓϥεϘλϯΛԡͨ͠Β
    ͕දࣔ͞Ε͍ͯͯཉ͍͠
    [email protected]@UIFO
    HJWFO
    XIFO
    UIFO

    View Slide

  43. ςετͷՄಡੑ
    [email protected]@UIFO
    testWidgets('label shows "1"', harness((given, when, then) async {


    {


    await given.pumpMyApp();


    given.canFindZero();


    given.cannotFindOne();


    }


    {


    await when.userTapsIncrementButton();


    }


    {


    then.cannotFindZero();


    then.canFindOne();


    }


    }),


    );


    View Slide

  44. ςετͷՄಡੑ
    [email protected]@UIFO
    testWidgets('label shows "1"', harness((given, when, then) async {


    {


    await given.pumpMyApp();


    given.canFindZero();


    given.cannotFindOne();


    }


    {


    await when.userTapsIncrementButton();


    }


    {


    then.cannotFindZero();


    then.canFindOne();


    }


    }),


    );


    ͕දࣔ͞Ε͍ͯΔ࣌ʹ

    View Slide

  45. ςετͷՄಡੑ
    [email protected]@UIFO
    testWidgets('label shows "1"', harness((given, when, then) async {


    {


    await given.pumpMyApp();


    given.canFindZero();


    given.cannotFindOne();


    }


    {


    await when.userTapsIncrementButton();


    }


    {


    then.cannotFindZero();


    then.canFindOne();


    }


    }),


    );


    ϓϥεϘλϯ͕ԡ͞ΕͨΒ
    ͕දࣔ͞Ε͍ͯΔ࣌ʹ

    View Slide

  46. ςετͷՄಡੑ
    [email protected]@UIFO
    testWidgets('label shows "1"', harness((given, when, then) async {


    {


    await given.pumpMyApp();


    given.canFindZero();


    given.cannotFindOne();


    }


    {


    await when.userTapsIncrementButton();


    }


    {


    then.cannotFindZero();


    then.canFindOne();


    }


    }),


    );


    ϓϥεϘλϯ͕ԡ͞ΕͨΒ
    ͕දࣔ͞Ε͍ͯΔ࣌ʹ
    ͕දࣔ͞Ε͍ͯͯཉ͍͠

    View Slide

  47. ςετͷՄಡੑ
    ղઆ
    [email protected]@UIFO

    View Slide

  48. ςετͷՄಡੑ
    [email protected]@UIFO
    testWidgets('label shows "1"', harness((given, when, then) async {


    {


    await given.pumpMyApp();


    given.canFindZero();


    given.cannotFindOne();


    }


    {


    await when.userTapsIncrementButton();


    }


    {


    then.cannotFindZero();


    then.canFindOne();


    }


    }),


    );


    View Slide

  49. ςετͷՄಡੑ
    [email protected]@UIFO

    View Slide

  50. ςετͷՄಡੑ
    [email protected]@UIFO
    extension 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);


    }}


    View Slide

  51. ςετͷՄಡੑ
    [email protected]@UIFO
    extension 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();


    }


    }


    View Slide

  52. ςετͷՄಡੑ
    [email protected]@UIFO
    extension 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);


    }


    }


    View Slide

  53. ςετͷՄಡੑ
    [email protected]@UIFO
    testWidgets('label shows "1"', harness((given, when, then) async {


    {


    await given.pumpMyApp();


    given.canFindZero();


    given.cannotFindOne();


    }


    {


    await when.userTapsIncrementButton();


    }


    {


    then.cannotFindZero();


    then.canFindOne();


    }


    }),


    );


    View Slide

  54. ςετͷՄಡੑ
    [email protected]@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();


    }


    }));


    View Slide

  55. 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

    View Slide

  56. ςετͷՄಡੑ
    [email protected]@UIFO
    ͪΐͬͱେม͡ΌͶʁ

    View Slide

  57. ςετͷՄಡੑ
    [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);


    }


    });


    View Slide

  58. View Slide

  59. ςετͷՄಡੑ
    [email protected]@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();


    }


    }));


    }


    View Slide

  60. ςετͷՄಡੑ
    [email protected]@VJ
    w testUI, setupUI, tearDownUIͳͲɺ6*ςετ༻ͷϝιουΛఏڙ

    View Slide

  61. ςετͷՄಡੑ
    [email protected]@VJ
    testWidgets('label shows "1"', harness((given, when, then) async {


    {


    await given.pumpMyApp();


    given.canFindZero();


    given.cannotFindOne();


    }


    {


    await when.userTapsIncrementButton();


    }


    {


    then.cannotFindZero();


    then.canFindOne();


    }


    }),


    );


    View Slide

  62. ςετͷՄಡੑ
    [email protected]@UIFO
    testWidgets('label shows "1"', harness((given, when, then) async {


    {


    await given.pumpMyApp();


    given.canFindZero();


    given.cannotFindOne();


    }


    {


    await when.userTapsIncrementButton();


    }


    {


    then.cannotFindZero();


    then.canFindOne();


    }


    }),


    );


    View Slide

  63. ςετͷՄಡੑ
    [email protected]@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();


    }


    }));


    }


    View Slide

  64. ςετͷՄಡੑ
    [email protected]@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();


    }


    }));


    }


    View Slide

  65. ςετͷՄಡੑ
    [email protected]@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();


    }


    }));


    }


    View Slide

  66. 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

    View Slide

  67. ฐࣾͰͷϏϑΥʔΞϑλʔ

    View Slide

  68. ฐࣾͰͷϏϑΥʔΞϑλʔ

    "3FRVFTU
    #3FRVFTU
    $3FRVFTU
    "QJ$MJFOU
    "3FTQPOTF
    #3FTQPOTF
    $3FTQPOTF
    $PNNBOEύλʔϯ
    "QJ
    3FRV
    FTU

    View Slide


  69. "3FRVFTU
    #3FRVFTU
    $3FRVFTU
    "QJ$MJFOU
    "3FTQPOTF
    #3FTQPOTF
    $3FTQPOTF
    $PNNBOEύλʔϯ
    "QJ
    3FRV
    FTU
    ฐࣾͰͷϏϑΥʔΞϑλʔ

    View Slide


  70. "3FRVFTU
    #3FRVFTU
    $3FRVFTU
    "QJ$MJFOU
    "3FTQPOTF
    #3FTQPOTF
    $3FTQPOTF
    $PNNBOEύλʔϯ
    "QJ
    3FRV
    FTU
    ฐࣾͰͷϏϑΥʔΞϑλʔ

    View Slide


  71. "3FRVFTU
    #3FRVFTU
    $3FRVFTU
    "QJ$MJFOU
    "3FTQPOTF
    #3FTQPOTF
    $3FTQPOTF
    $PNNBOEύλʔϯ
    "QJ
    3FRV
    FTU
    ฐࣾͰͷϏϑΥʔΞϑλʔ

    View Slide


  72. ฐࣾͰͷϏϑΥʔΞϑλʔ

    View Slide


  73. ฐࣾͰͷϏϑΥʔΞϑλʔ

    View Slide

  74. ฐࣾͰͷϏϑΥʔΞϑλʔ

    View Slide

  75. ฐࣾͰͷϏϑΥʔΞϑλʔ

    View Slide

  76. ฐࣾͰͷϏϑΥʔΞϑλʔ
    ςετΛͭ௥Ճ͢ΔͨΊʹ༷ʑͳϑΝΠϧΛ௥Ճมߋ͢Δඞཁ͕͋Δ
    w UFTUSFTPVSDFT഑Լ
    w KTPOͷ௥Ճ
    w 'BLF"QJ$MJFOU
    w ϞοΫύεͷઃఆ
    w ๨ΕΔͱImplementationError


    w xxxTest
    σϝϦοτ

    View Slide

  77. ฐࣾͰͷϏϑΥʔΞϑλʔ
    ͪΐͬͱ͚ͩҧ͏ϨεϙϯεΛ࡞Δͷʹ৽ͨͳKTPO͕ඞཁ
    ɾ༑ୡͷ༗ແ
    ɾϑΥϩʔঢ়ଶ
    ɾϒϩοΫঢ়ଶ
    σϝϦοτ

    View Slide

  78. ฐࣾͰͷϏϑΥʔΞϑλʔ
    σϝϦοτ
    มߋʹରͯ͠ίϯύΠϧηʔϑͰͳ͍
    ɾ6TFS͔ΒϓϩύςΟΛҰͭݮΒͨ͠
    ɹɹKTPO΋ม͑ͳ͖Ό͍͚ͳ͍͜ͱͳΜͯ୭΋ؾ෇͔ͳ͍ΑͶস

    View Slide

  79. Ξϑλʔ

    View Slide

  80. ฐࣾͰͷϏϑΥʔΞϑλʔ
    Ξϑλʔ
    typedef FakeApiResponder = dynamic Function(ApiRequest);


    View Slide

  81. ฐࣾͰͷϏϑΥʔΞϑλʔ
    Ξϑλʔ
    FakeApiClientImpl appending({


    required FakeApiResponder responder,


    }) {


    return FakeApiClientImpl._(


    responders: [..._responders, responder],


    );


    }


    View Slide

  82. )PXUPVTFʁ

    View Slide

  83. ฐࣾͰͷϏϑΥʔΞϑλʔ
    Ξϑλʔ
    void main() {


    final baseApiClient = FakeApiClientImpl.create();


    setUpAll(TestWidgetsFlutterBinding.ensureInitialized);


    // Test cases...


    }


    View Slide

  84. ฐࣾͰͷϏϑΥʔΞϑλʔ
    Ξϑλʔ
    final apiClient = baseApiClient.appending(


    responder: (request) {


    if (request is UsersRequest) {


    return _Fixtures.createUser(


    id: 'some_user_id',


    );


    }


    },


    );


    View Slide

  85. ฐࣾͰͷϏϑΥʔΞϑλʔ
    Ξϑλʔ
    class _Fixtures {


    static User createUser() {


    return User(id: 'hoge', name: 'ashdik');


    }


    }


    View Slide

  86. ฐࣾͰͷϏϑΥʔΞϑλʔ
    ςετΛͭ௥Ճ͢ΔͨΊʹ༷ʑͳϑΝΠϧΛ௥Ճมߋ͢Δඞཁ͕͋Δ
    ɹˠͦΕͧΕͷςετͷΈʂ
    ͪΐͬͱ͚ͩҧ͏ϨεϙϯεΛ࡞Δͷʹ৽ͨͳKTPO͕ඞཁ
    ɹˠ@'JYUVSFͷҾ਺ʹม͍͑ͨ஋ΛऔΔ͚ͩ
    มߋʹରͯ͠ίϯύΠϧηʔϑͰͳ͍
    ɹˠ6TFSΫϥεΛ௚઀ΠϯελϯεԽ͍ͯ͠Δ
    σϝϦοτ

    View Slide

  87. ςετ݁ՌͷՄಡੑ

    View Slide

  88. View Slide

  89. ςετ݁ՌͷՄಡੑ
    w σϑΥϧτͰ͸ ओʹࣦഊ࣌
    ಡΈͮΒ͍ʜ
    GMVUUFSUFTU

    View Slide

  90. ςετ݁ՌͷՄಡੑ
    w ಡΈ΍͍͢🎉

    View Slide

  91. ςετ݁ՌͷՄಡੑ
    w [email protected]ΠϯεύΠΞ
    w ςετ݁ՌΛݟ΍ͯ͘͘͢͠ΕΔ
    w ʮʯ
    ɹ੒ޭ
    w ʮ9ʯ
    ɹࣦഊ
    w ʮʯ
    ɹεΩοϓ
    [email protected]@SFQPSUFS

    View Slide

  92. ςετ݁ՌͷՄಡੑ
    ͩΜͩΜಡΈ΍ͯ͘͢͠ΈΑ͏
    GMVUUFSUFTUNBDIJOF

    View Slide

  93. w ҎԼ2εςοϓ
    ςετ݁ՌͷՄಡੑ
    w pubspec.yamlʹdart_dot_reporterΛ௥Ճ
    [email protected]@SFQPSUFSɹʙԼ४උʙ
    [email protected]@SFQPSUFS

    View Slide

  94. ςετ݁ՌͷՄಡੑ
    ͩΜͩΜಡΈ΍ͯ͘͢͠ΈΑ͏
    GMVUUFSUFTUNBDIJOFNBDIJOFMPH
    [email protected]@SFQPSUFSNBDIJOFMPH

    View Slide

  95. ςετ݁ՌͷՄಡੑ
    ͩΜͩΜಡΈ΍ͯ͘͢͠ΈΑ͏

    View Slide

  96. ͦͷଞ
    w SJWFSQPE
    w
    fl
    [email protected]
    w NPDLJOHKBZ
    w NPDLJUP
    w [email protected]@UFTU
    w PHVSFUT
    ঺հ͖͠Εͳ͔ͬͨύοέʔδͨͪ

    View Slide


  97. ਖ਼ࣾһ Λݟਾ͑ͨۀ຿ҕୗͷํ
    ืूதʂ

    View Slide


  98. ͝ਗ਼ௌɺ͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ

    View Slide