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

テストが辛いを解決するテスト駆動開発のアプローチ at PHPカンファレンス仙台2019

テストが辛いを解決するテスト駆動開発のアプローチ at PHPカンファレンス仙台2019

"テストが辛いを解決するテスト駆動開発のアプローチ" at PHPカンファレンス仙台2019

Kazuki Higashiguchi
PRO

January 24, 2019
Tweet

More Decks by Kazuki Higashiguchi

Other Decks in Technology

Transcript

  1. ςετ͕ਏ͍Λղܾ͢Δ
    ςετۦಈ։ൃ
    ͷΞϓϩʔν
    ,B[VLJ)JHBTIJHVDIJ !IHTHUL

    1)1ΧϯϑΝϨϯεઋ୆

    View Slide

  2. "CPVUUIJTUBML
    ࢿྉެ։͋Γ
    !IHTHULΑΓ5XJUUFS
    ʹͯγΣΞ

    View Slide

  3. 2VFTUJPO ϢχοτςετΛ
    ॻ͍͍ͯΔʁ

    View Slide

  4. 2VFTUJPO
    ͿͬͪΌ͚
    ςετΛॻ͘ͷ͕ਏ
    ͍ʁ

    View Slide

  5. 8IBUUIJTUBMLJT
    BCPVU
    w ʮϢχοτςετΛॻ͘ͷ͕
    ਏ͍ʯͱ͍͏՝୊ΛݴޠԽ͢
    ΔࢼΈ
    w ՝୊ʹର͢Δղ๏ͱͯ͠ʮς
    ετۦಈ։ൃʯΛ׆༻͢Δ

    View Slide

  6. 8IBUUIJTUBMLJTOPU
    BCPVU
    w ΤϯυπʔΤϯυςετ
    w 1)16OJUͷ࢖͍ํ
    w ςετ࣮ߦ؀ڥͷ੔උ

    View Slide

  7. [email protected] NF

    w ౦ޱ࿨ᏻ ,B[VLJ)JHBTIJHVDIJ

    w !IHTHUL (JU)VC 5XJUUFS

    w 4FSWFSTJEF&OHJOFFS (P 1)1 FUD

    w ॴଐɿ#"4&#"/,גࣜձࣾ
    w #"4&גࣜձࣾࢠձࣾ

    View Slide

  8. "HFOEB
    Ϣχοτςετ͕ਏ͍ཁҼ
    ςετۦಈ։ൃͱ͸
    ςετۦಈ։ൃΛ׆༻͢Δ
    ςετۦಈ։ൃ΁ͷো֐Λ
    औΓআ͘
    ·ͱΊ

    View Slide

  9. ̍Ϣχοτςετ͕
    ਏ͍ཁҼ

    View Slide

  10. Ϣχοτςετͱ͸
    w ୯ମςετͱ΋ݺ͹ΕΔ
    w ϓϩάϥϜΛߏ੒͢Δൺֱతখ͞ͳ୯ҐʢϢχο
    τʣ͕ݸʑͷػೳΛਖ਼͘͠Ռ͍ͨͯ͠Δ͔Ͳ
    ͏͔Λݕূ͢Δ
    w 1)16OJUͳͲͷςετϑϨʔϜϫʔΫΛ༻͍ͯ
    ࣮ߦ͢Δ

    View Slide

  11. Ϣχοτςετ͕
    ਏ͍༷ʑͳཁҼ
    w ςετͷॻ͖ํ͕Θ͔Βͳ͍
    w ৗʹҰࠁҰඵΛ૪͍ͬͯΔ
    w طଘίʔυʹςετ͕ͳ͍
    w ςετΛॻ͘ͷ͕ԯ߷
    w ςετΛॻ͘ͷ͕೉͍͠ίʔ
    υʹͳΔ
    w طଘςετ͕։ൃΛ๦͍͛ͯ
    Δ

    View Slide

  12. w ςετͷॻ͖ํ͕Θ͔Βͳ͍
    w ৗʹҰࠁҰඵΛ૪͍ͬͯΔ
    w طଘίʔυʹςετ͕ͳ͍
    w ςετΛॻ͘ͷ͕ԯ߷
    w ςετΛॻ͘ͷ͕೉͍͠ίʔ
    υʹͳΔ
    w طଘςετ͕։ൃΛ๦͍͛ͯ
    Δ
    w ςετͷॻ͖ํͷशಘ
    d$BVTFd d1PTTJCMZ4PMVUJPOd
    w ϓϩδΣΫτ؅ཧ
    w ϨΨγʔίʔυվળ
    w ςετͷॻ͖΍͍͢ઃܭ
    w Մಡੑɾϝϯςφϯεੑ
    ͷߴ͍Ϣχοτςετ
    w ࣮૷தͷςετͷҐஔͮ
    ͚ͷݟ௚͠

    View Slide

  13. w ςετͷॻ͖ํ͕Θ͔Βͳ͍
    w ৗʹҰࠁҰඵΛ૪͍ͬͯΔ
    w طଘίʔυʹςετ͕ͳ͍
    w ςετΛॻ͘ͷ͕ԯ߷
    w ςετΛॻ͘ͷ͕೉͍͠ίʔ
    υʹͳΔ
    w طଘςετ͕։ൃΛ๦͍͛ͯ
    Δ
    d$BVTFd d1PTTJCMZ4PMVUJPOd
    w ςετͷॻ͖ํͷशಘ
    w ϓϩδΣΫτ؅ཧ
    w ϨΨγʔίʔυվળ
    w ςετͷॻ͖΍͍͢ઃܭ
    w Մಡੑɾϝϯςφϯεੑ
    ͷߴ͍Ϣχοτςετ
    w ࣮૷தͷςετͷҐஔͮ
    ͚ͷݟ௚͠
    Focus points

    View Slide

  14. ςετΛॻ͘ͷ͕ԯ߷
    w ʮςετΛॻ͘ͷ͕ԯ߷ʯͱ͍͏੠Λฉ͘ࡍɺ
    ಈ࡞ίʔυΛॻ͍ͨޙʹςετΛॻ͘έʔε
    ͕ଟ͍
    w ಈ࡞ίʔυΛॻ͍͔ͯΒɺςετΛॻ͘ͷ͕
    ໘౗
    w ࣮૷ͷϦζϜͷதʹϢχοτςετ͕͍ͳ͍

    View Slide

  15. ςετΛॻ͘ͷ͕೉͍͠ίʔυʹͳΔ
    w ಈ࡞ίʔυ࣮૷ޙɺ͍͟ςετΛॻ͜͏ͱ͠
    ͨΒςετ͕ॻ͖ͮΒ͔ͬͨͱ͍͏έʔε͕
    ͋Δ
    w ςετΛॻ͍͍ͯͳ͍ίʔυ͸ɺςετ͕ॻ
    ͖ͮΒ͍έʔε͕ଟ͍

    View Slide

  16. 4VNNBSZ
    wϢχοτςετ͕ਏ͍ঢ়گ
    ʹ͸༷ʑͳཁҼ͕͋Δ
    wʮϢχοτςετͱ࣮૷ͷ
    ϦζϜͷ్੾Εʯ͕ɺςε
    τΛॻ͘ԯ߷͞ΛੜΉ
    wʮςετΛॻ͖ͮΒ͍ίʔ
    υʯ͕ɺϢχοτςετΛ
    ਏ͘͢Δ

    View Slide

  17. ςετۦಈ։ൃͱ͸

    View Slide

  18. ςετۦಈ։ൃͱ͸
    w ϓϩάϥϜ։ൃख๏ͷҰछ
    w ʮಈ࡞͢Δ͖Ε͍ͳίʔυʯΛΰʔϧͱ͢Δ
    w ϓϩάϥϛϯάதͷෆ҆Λίϯτϩʔϧ͢Δख

    w ࣗಈԽ͞ΕͨςετʹΑͬͯ։ൃΛਪ͠ਐΊ
    Δ

    View Slide

  19. 3FE(SFFO3FGBDUPS
    3FE
    Green
    Refactor
    'BJMFEUFTU
    1BTTFE
    3FGBDUPS

    View Slide

  20. ςετۦಈ։ൃͷαΠΫϧ
    w ·ͣ͸ςετΛҰͭॻ͘
    w ͢΂ͯͷςετΛ૸Βͤɺ৽͍͠ςετͷࣦഊ
    Λ֬ೝ͢Δ 3FE

    w খ͞ͳมߋΛߦ͏
    w ͢΂ͯͷςετΛ૸Βͤɺ͢΂ͯ੒ޭ͢Δ͜ͱ
    Λ֬ೝ͢Δ (SFFO

    w ϦϑΝΫλϦϯάΛߦͬͯॏෳΛআڈ͢Δ
    3FGBDUPS

    View Slide

  21. ςετۦಈ։ൃͷਐΊํ
    w 50%0ϦετΛ࡞੒͢Δ
    w ඞཁʹͳΓͦ͏ͳςετΛॻ͖ग़͍ͯ͘͠
    w ్தͰؾ͕͍ͭͨ՝୊Λ௥ه͍ͯ͘͠
    w ςετۦಈ։ൃͷαΠΫϧʢ3FE(SFFO
    3FGBDUPS
    Λճ͍ͯ͘͠
    w 3FE͔Β(SFFOʹ͢ΔͨΊͷzԾ࣮૷zΛ͠ɺ
    ૉૣ͘αΠΫϧΛճ͢

    View Slide

  22. %FNPOTUSBUJPO
    wྫɿ$.4ΞϓϦέʔ
    γϣϯ
    wهࣄৄࡉऔಘ"1*Λ࣮
    ૷͢Δ

    View Slide

  23. 50%0Ϧετ
    <>هࣄৄࡉऔಘʹͯ੒ޭϨεϙϯε͕ฦ٫͞
    ΕΔ
    <>هࣄৄࡉऔಘʹͯଘࡏ͢ΔϨίʔυͷ৔߹ɺ
    هࣄ৘ใ͕ฦ٫͞ΕΔ
    DEMO
    ʢࢿྉެ։༻ʣ

    View Slide

  24. ςετΛॻ͘
    /**
    * @test
    */
    public function هࣄৄࡉऔಘʹͯ੒ޭϨεϙϯε͕ฦ٫͞ΕΔ()
    {
    $this->configRequest([
    'headers' => [
    'Accept' => 'application/json',
    ],
    ]);
    $this->get('/api/articles/view/first');
    $this->assertResponseSuccess();
    }
    DEMO
    ʢࢿྉެ։༻ʣ

    View Slide

  25. 1PTTJCMZSFMBUFEUP
    $BLFa$POUSPMMFSa&YDFQUJPOa.JTTJOH"DUJPO&
    YDFQUJPO"DUJPO"SUJDMFT$POUSPMMFSWJFX

    DPVMEOPUCFGPVOE PSJTOPUBDDFTTJCMF
    'BJMFEBTTFSUJOHUIBUJTCFUXFFO
    BOE
    DEMO
    ʢࢿྉެ։༻ʣ

    View Slide

  26. খ͞ͳมߋΛߦ͏
    class ArticlesController extends AppController
    {
    public function view($slug = null)
    {
    }
    }
    DEMO
    ʢࢿྉެ։༻ʣ

    View Slide

  27. 1PTTJCMZSFMBUFEUP
    $BLFa7JFXa&YDFQUJPOa.JTTJOH5FNQMBUF&YDF
    QUJPO5FNQMBUFpMF"QJ"SUJDMFTKTPO
    WJFXDUQJTNJTTJOH
    'BJMFEBTTFSUJOHUIBUJTCFUXFFO
    BOE
    DEMO
    ʢࢿྉެ։༻ʣ

    View Slide

  28. খ͞ͳมߋΛߦ͏
    class ArticlesController extends AppController
    {
    public function initialize()
    {
    parent::initialize();
    $this->loadComponent('RequestHandler');
    }
    public function view($slug = null)
    {
    $this->set([
    '_serialize' => ['article'],
    'article' => [],
    ]);
    }
    }
    DEMO
    ʢࢿྉެ։༻ʣ

    View Slide

  29. 0, UFTUT BTTFSUJPOT

    DEMO
    ʢࢿྉެ։༻ʣ

    View Slide

  30. 50%0Ϧετ
    هࣄৄࡉऔಘʹͯ੒ޭϨεϙϯε͕ฦ٫͞
    ΕΔ
    <>هࣄৄࡉऔಘʹͯଘࡏ͢ΔϨίʔυͷ৔߹ɺ
    هࣄ৘ใ͕ฦ٫͞ΕΔ
    DEMO
    ʢࢿྉެ։༻ʣ

    View Slide

  31. ςετΛॻ͘
    /**
    * @test
    */
    public function هࣄৄࡉऔಘʹͯଘࡏ͢ΔϨίʔυͷ৔߹ɺهࣄ৘ใ͕ฦ٫͞ΕΔ()
    {
    $this->configRequest([
    'headers' => [
    'Accept' => 'application/json',
    ],
    ]);
    $this->get('/api/articles/view/first');
    $expected = [
    'article' => [
    'id' => 1,
    'user_id' => 1,
    'title' => 'First Article',
    'slug' => 'first',
    'body' => 'First Article Body',
    'published' => 1,
    'created' => '2018-01-07T15:47:01+00:00',
    'modified' => '2018-01-07T15:47:02+00:00',
    ],
    ];
    $expected = json_encode($expected, JSON_PRETTY_PRINT);
    $this->assertSame($expected, (string)$this->_response->getBody());
    }
    DEMO
    ʢࢿྉެ։༻ʣ

    View Slide

  32. DEMO
    ʢࢿྉެ։༻ʣ
    'BJMFEBTTFSUJOHUIBUUXPTUSJOHTBSFJEFOUJDBM

    View Slide

  33. Ծ࣮૷
    public function view($slug = null)
    {
    // TODO temporary implement
    $article = [
    'id' => 1,
    'user_id' => 1,
    'title' => 'First Article',
    'slug' => 'first',
    'body' => 'First Article Body',
    'published' => 1,
    'created' => '2018-01-07T15:47:01+00:00',
    'modified' => '2018-01-07T15:47:02+00:00',
    ];
    $this->set([
    '_serialize' => ['article'],
    'article' => $article,
    ]);
    }
    DEMO
    ʢࢿྉެ։༻ʣ

    View Slide

  34. 0, UFTUT BTTFSUJPOT

    DEMO
    ʢࢿྉެ։༻ʣ

    View Slide

  35. ϦϑΝΫλϦϯά
    public function view($slug = null)
    {
    $article = $this->Articles->findBySlug($slug)-
    >firstOrFail();
    $this->set([
    '_serialize' => ['article'],
    'article' => $article,
    ]);
    }
    DEMO
    ʢࢿྉެ։༻ʣ

    View Slide

  36. 0, UFTUT BTTFSUJPOT

    DEMO
    ʢࢿྉެ։༻ʣ

    View Slide

  37. 50%0Ϧετ
    هࣄৄࡉऔಘʹͯ੒ޭϨεϙϯε͕ฦ٫͞
    ΕΔ
    هࣄৄࡉऔಘʹͯଘࡏ͢ΔϨίʔυͷ৔߹ɺ
    هࣄ৘ใ͕ฦ٫͞ΕΔ
    DEMO
    ʢࢿྉެ։༻ʣ

    View Slide

  38. w ςετͷॻ͖ํ͕Θ͔Βͳ͍
    w ৗʹҰࠁҰඵΛ૪͍ͬͯΔ
    w طଘίʔυʹςετ͕ͳ͍
    w ςετΛॻ͘ͷ͕ԯ߷
    w ςετΛॻ͘ͷ͕೉͍͠ίʔ
    υʹͳΔ
    w طଘςετ͕։ൃΛ๦͍͛ͯ
    Δ
    d$BVTFd d1PTTJCMZ4PMVUJPOd
    w ςετͷॻ͖ํͷशಘ
    w ϓϩδΣΫτ؅ཧ
    w ϨΨγʔίʔυվળ
    w ςετͷॻ͖΍͍͢ઃܭ
    w Մಡੑɾϝϯςφϯεੑ
    ͷߴ͍Ϣχοτςετ
    w ࣮૷தͷςετͷҐஔͮ
    ͚ͷݟ௚͠
    Focus points
    ৼΓฦΓ

    View Slide

  39. ςετۦಈ։ൃ׆༻ͷํ਑
    w ςετۦಈ։ൃͷ͍͔ͭ͘ͷଆ໘Λ༗ޮ׆༻
    ͢Δ
    w ςετΛ࠷ॳʹॻ͘ʢςετϑΝʔετʣ͜
    ͱʹΑΓɺʮςετ͕ॻ͖ͮΒ͍ʯίʔυΛ
    ճආ͢Δ
    w ςετۦಈ։ൃͷϦζϜΛऔΓೖΕΔ͜ͱͰɺ
    ಈ࡞ίʔυͱςετΛҰͭͷϦζϜͰਐΊΔ

    View Slide

  40. 4VNNBSZ
    w3FE(SFFO3FGBDUPS
    ͷϦζϜΛ΋ͬͯਐΊ
    Δςετۦಈ։ൃ
    wςετۦಈ։ൃʹΑͬ
    ͯɺςετ͕ॻ͖ͮΒ
    ͍ɾԯ߷ͳঢ়گΛճ
    ආ͢Δ

    View Slide

  41. ςετۦಈ։ൃΛ
    ׆༻͢Δ

    View Slide

  42. ϢχοτςετΛҙຯ͚ͮΔ
    w ܽؕͷ͋Δίʔυͷࠞೖ཰ΛԼ͛Δ
    w ৺ཧతෆ҆ΛԼ͛Δ
    w lςετ͕௨͍ͬͯΔ͔Βյ͍ͯ͠ͳ͍͸ͣz
    w ༐׶ͳϦϑΝΫλϦϯά
    w ઃܭͷิॿಓ۩

    View Slide

  43. ઃܭͷิॿಓ۩ͱͯ͠ͷςετ
    w Ϣχοτςετ͔Βɺಈ࡞ίʔυͷઃܭʹͭ
    ͍ͯɺϑΟʔυόοΫΛಘΔ
    w ςετΛઌʹॻ͘͜ͱͰɺʮςετͷ͠΍͢
    ͍ίʔυʯΛॻ͘ΪϒεΛ෇͚Δ
    w ʮςετͷॻ͖ʹ͘͞ʯ͔Βઃܭʹର͢Δҧ
    ࿨ײΛټΈऔΔ

    View Slide

  44. $BTF4UVEZ
    ઃܭͷ
    ϑΟʔυόοΫΛ
    ςετέʔε͔Β
    ಘΔ

    View Slide

  45. $BTF4UVEZ
    w ໰͍߹ΘͤอଘػೳΛ࣮૷͢Δ
    w ʮλΠτϧʯɾʮຊจʯɾʮϝʔϧΞυϨεʯɾ
    ʮΫϥΠΞϯτͷ*1ΞυϨεʯΛอଘ͓ͯ͘͠

    View Slide

  46. 50%0Ϧετ
    ໰͍߹ΘͤϖʔδʹΞΫηεͰ͖Δ
    ໰͍߹Θͤอଘ੒ޭ࣌Ұཡϖʔδ΁ϦμΠ
    ϨΫτ͢Δ
    <>໰͍߹Θͤͨ͠಺༰͕JORVJSJFTςʔϒϧʹ
    อଘ͞ΕΔ

    View Slide

  47. ݱ࣌఺ͷςετ
    /**
    * @test
    */
    public function ໰͍߹ΘͤϖʔδʹΞΫηεͰ͖Δ()
    {
    $this->get('/inquiries/add');
    $this->assertResponseOk();
    }
    /**
    * @test
    */
    public function ໰͍߹Θͤอଘ੒ޭ࣌Ұཡϖʔδ΁ϦμΠϨΫτ͢Δ()
    {
    $this->enableCsrfToken();
    $this->enableSecurityToken();
    $data = [
    'title' => 'sample',
    'body' => 'sample',
    'email' => '[email protected]',
    ];
    $this->post('/inquiries/add', $data);
    $this->assertResponseSuccess();
    $this->assertRedirect('/inquiries');
    }

    View Slide

  48. ݱ࣌఺ͷ࣮૷
    public function add()
    {
    $inquiry = $this->Inquiries->newEntity();
    if ($this->request->is('post')) {
    // TODO save inquiry data
    return $this->redirect(['action' => 'index']);
    }
    $this->set(compact('inquiry'));
    }

    View Slide

  49. 50%0Ϧετ
    ໰͍߹ΘͤϖʔδʹΞΫηεͰ͖Δ
    ໰͍߹Θͤอଘ੒ޭ࣌Ұཡϖʔδ΁ϦμΠ
    ϨΫτ͢Δ
    <>໰͍߹Θͤͨ͠಺༰͕JORVJSJFTςʔϒϧʹ
    อଘ͞ΕΔ

    View Slide

  50. ·ͣ͸ςετΛॻ͘
    /**
    * @test
    */
    public function ໰͍߹Θͤͨ͠಺༰͕inquiriesςʔϒϧʹอଘ͞ΕΔ()
    {
    $this->enableCsrfToken();
    $this->enableSecurityToken();
    $this->configRequest([
    'environment' => ['REMOTE_ADDR' => '12.34.56.78'],
    ]);
    $data = [
    'title' => 'sample',
    'body' => 'sample',
    'email' => '[email protected]',
    ];
    $this->post('/inquiries/add', $data);
    /** @var Inquiry $saved_inquiry */
    $saved_inquiry = TableRegistry::getTableLocator()
    ->get('inquiries')->find()
    ->where(['title' => 'sample'])->first();
    $this->assertInstanceOf('\App\Model\Entity\Inquiry',
    $saved_inquiry);
    }

    View Slide

  51. 'BJMFEBTTFSUJOHUIBUOVMMJTBOJOTUBODFPGDMBTT
    la"QQa.PEFMa&OUJUZa*ORVJSZ
    5FTUT "TTFSUJPOT 'BJMVSFT

    View Slide

  52. ςετΛ௨ͨ͢ΊͷԾ࣮૷
    public function add()
    {
    $inquiry = $this->Inquiries->newEntity();
    if ($this->request->is('post')) {
    // FIXME temporary
    $inquiry->title = 'sample';
    $inquiry->body = 'sample';
    $inquiry->email = '[email protected]';
    $inquiry->client_ip = ’12.34.56.78';
    $this->Inquiries->save($inquiry);
    return $this->redirect(['action' => 'index']);
    }
    $this->set(compact('inquiry'));
    }

    View Slide

  53. 0, UFTUT BTTFSUJPOT

    View Slide

  54. 3FGBDUPSJOH
    w ظ଴஋ͷςετΛ࡞੒͠ɺςετ͕௨ΔԾ࣮૷Λͨ͠
    w Ծ࣮૷ΛϦϑΝΫλϦϯά͢Δ

    View Slide

  55. ࣮૷ऀ͕಄ʹු͔Μ࣮ͩ૷
    public function saveWithIp(Inquiry $entity,
    \Cake\Http\ServerRequest $request)
    {
    $entity->client_ip = $request->clientIp();
    return $this->save($entity);
    }
    w .PEFM૚ʹϝιουΛ࡞੒
    w ϦΫΤετΦϒδΣΫτΛड͚औͬͯอଘ

    View Slide

  56. ςετΛॻ͘
    /**
    * @test
    */
    public function ໰͍߹Θͤͨ͠಺༰͕inquiriesςʔϒϧʹอଘ͞ΕΔ()
    {
    $inquiry = $this->Inquiries->newEntity(
    [
    'title' => 'sample',
    'body' => 'sample',
    'email' => '[email protected]',
    ]
    );
    $request = new \Cake\Http\ServerRequest('/inquiries/
    add');
    $actual = $this->Inquiries->saveWithIp($inquiry,
    $request);
    // Assert something
    }
    w ϝιου࡞੒Λ૝ఆ͍ͯ͠Δ.PEFMʹςετΛॻ͘

    View Slide

  57. ςετ͔Βҧ࿨ײΛټΈऔΔ
    /**
    * @test
    */
    public function ໰͍߹Θͤͨ͠಺༰͕inquiriesςʔϒϧʹอଘ͞ΕΔ()
    {
    $inquiry = $this->Inquiries->newEntity(
    [
    'title' => 'sample',
    'body' => 'sample',
    'email' => '[email protected]',
    ]
    );
    $request = new \Cake\Http\ServerRequest('/inquiries/
    add');
    $actual = $this->Inquiries->saveWithIp($inquiry,
    $request);
    // Assert something
    }
    ϢχοτςετΛॻͨ͘Ίʹɺ
    ςετ༻ͷϦΫΤετΛੜ੒͢Δख͕ؒൃੜ
    ͍ͯ͠Δ

    View Slide

  58. ҧ࿨ײ͔Βઃܭ൑அ͢Δ
    /**
    * @test
    */
    public function ໰͍߹Θͤͨ͠಺༰͕inquiriesςʔϒϧʹอଘ͞ΕΔ()
    {
    $inquiry = $this->Inquiries->newEntity(
    [
    'title' => 'sample',
    'body' => 'sample',
    'email' => '[email protected]',
    ]
    );
    $request = new \Cake\Http\ServerRequest('/inquiries/
    add');
    $actual = $this->Inquiries->saveWithIp($inquiry,
    $request);
    // Assert something
    }
    ϞσϧΫϥε͕ɺ
    ϦΫΤετΫϥεʹґଘ͍ͯ͠Δઃܭ͸ɺ
    ΍Ίͨ΄͏͕ྑͦ͞͏ͩ

    View Slide

  59. ςετΛม͑ͯΈΔ
    /**
    * @test
    */
    public function ໰͍߹Θͤͨ͠಺༰͕inquiriesςʔϒϧʹอଘ͞ΕΔ()
    {
    $inquiry = $this->Inquiries->newEntity(
    [
    'title' => 'sample',
    'body' => 'sample',
    'email' => '[email protected]',
    ‘client_ip' => ‘171.0.0.1’,
    ]
    );
    $actual = $this->Inquiries->saveWithIp($inquiry);
    // Assert something
    }
    ϦΫΤετΫϥε΁ͷґଘΛɺ
    ςετίʔυͷஈ֊Ͱഉআͨ͠ɻ

    View Slide

  60. ςετΛม͑ͯΈΔ
    /**
    * @test
    */
    public function ໰͍߹Θͤͨ͠಺༰͕inquiriesςʔϒϧʹอଘ͞ΕΔ()
    {
    $inquiry = $this->Inquiries->newEntity(
    [
    'title' => 'sample',
    'body' => 'sample',
    'email' => '[email protected]',
    ‘client_ip' => ‘171.0.0.1’,
    ]
    );
    $actual = $this->Inquiries->saveWithIp($inquiry);
    // Assert something
    }
    ݁ՌతʹɺΤϯςΟςΟΛอଘ͢Δ͚ͩʹ
    ͳͬͨɻ
    ϝιουΛ࡞੒͢Δ͜ͱ͕ͦ΋ͦ΋ෆཁ
    ͱ൑அɻ

    View Slide

  61. Ծ࣮૷Λॻ͖׵͑Δ
    public function add()
    {
    $inquiry = $this->Inquiries->newEntity();
    if ($this->request->is('post')) {
    $inquiry = $this->Inquiries->patchEntity($inquiry, $this-
    >request->getData());
    $inquiry->client_ip = $this->request->clientIp();
    if (!$this->Inquiries->save($inquiry)) {
    // TODO error handling
    }
    return $this->redirect(['action' => 'index']);
    }
    $this->set(compact('inquiry'));
    }

    View Slide

  62. 0, UFTUT BTTFSUJPOT

    View Slide

  63. 50%0Ϧετ
    ໰͍߹ΘͤϖʔδʹΞΫηεͰ͖Δ
    ໰͍߹Θͤอଘ੒ޭ࣌Ұཡϖʔδ΁ϦμΠ
    ϨΫτ͢Δ
    ໰͍߹Θͤͨ͠಺༰͕JORVJSJFTςʔϒϧʹ
    อଘ͞ΕΔ

    View Slide

  64. $BTF4UVEZ͔ΒֶͿ
    w ςετۦಈ։ൃͰਐΊΔதͰɺʮςετͷॻ
    ͖ʹ͘͞ʯΛᄿ͚͗ͭͨ
    w ಘͨϑΟʔυόοΫʹΑͬͯɺʮςετ͕ਏ͍
    ίʔυʯΛճආͨ͠

    View Slide

  65. ҧ࿨ײʹର͢Δײ౓Λ্͛Δ
    w ʮҧ࿨ײΛײ͡Δʯʹ͋ͨΓײ౓Λ͍͔͋͛ͯ
    ͳ͍ͱ͍͚ͳ͍
    w ʮςετ͕͔͚Δʯίʔυ͕ɺʮςετͷॻ͖΍
    ͍͢ʯίʔυʹͳΔΘ͚Ͱ͸ͳ͍
    w ઃܭࢥ૝ɾٕ๏ͳͲΛซͤͯҙ͓ࣝͯ͘͠ඞཁੑ
    w ҙࣝ͠΍͍͢ͱ͜Ζ͔Β࢝ΊͯΈΔ

    View Slide

  66. ςετͷॻ͖΍͍͢ίʔυ
    w Ϋϥεͷґଘؔ܎͕໌ࣔతͰஔ͖׵͑΍͍͢
    w ੹຿͕໌֬Ͱɺݺͼग़͠ɾݕূ͕؆୯ʹͰ͖
    Δ
    w ͍ΘΏΔʮૄ݁߹͔ͭߴڽूʯ

    View Slide

  67. ҧ࿨ײΛײ͡ΔͨΊʹҙࣝ͢Δ͜ͱ
    Ұͭͷ͜ͱΛ͏·͘΍Ε͍ͯΔ͔

    View Slide

  68. rμάϥεɾϚΩϧϩΠ6/*9ͷ࢛൒ੈل
    l͜Ε͕6/*9ͷ఩ֶͰ͋Δɻ
    Ұͭͷ͜ͱΛߦ͍ɺ·ͨͦΕΛ͏·͘΍ΔϓϩάϥϜ
    Λॻ͚ɻ
    ڠௐͯ͠ಈ͘ϓϩάϥϜΛॻ͚ɻz
    https://ja.wikipedia.org/wiki/UNIX఩ֶ

    View Slide

  69. ҧ࿨ײΛײ͡ΔͨΊʹҙࣝ͢Δ͜ͱ
    ҰจͰΫϥεΛઆ໌Ͱ͖Δ͔

    View Slide

  70. ҰจͰΫϥεΛઆ໌Ͱ͖Δ͔
    w ʮΫϥε͕࣮ࡍʹԿΛ͍ͯ͠Δͷ͔͕໌֬Ͱ
    ͋Δ͔ʯΛਤΔ
    w "/%ɾ03ɾ#65Λ࢖Θͣʹઆ໌Ͱ͖Δ͔
    w ࢖ͬͯ͠·ͬͨ৔߹ɺෳ਺ͷ੹຿͕ࠞࡏͯ͠
    ͍ΔՄೳੑ͕͋Δ

    View Slide

  71. $BTF4UVEZ ΞΧ΢ϯτొ࿥

    View Slide

  72. 50%0Ϧετ
    w <>ΞΧ΢ϯτ৘ใ͕σʔλϕʔεʹอଘ͞Ε
    Δ
    w <>ొ࿥׬ྃϝʔϧΛૹ৴͞ΕΔ

    View Slide

  73. ࣦഊ͢ΔςετΛॻ͘
    /**
    * @test
    */
    public function ΞΧ΢ϯτ৘ใ͕σʔλϕʔεʹอଘ͞ΕΔ()
    {
    $result = $this->AccountService->register();
    // σʔλϕʔεʹอଘ͞Εͨ஋ΛΞαʔτ͢Δ
    }

    View Slide

  74. ςετΛ௨࣮͢૷Λ͢Δ
    class AccountService
    {
    public function register()
    {
    // ΞΧ΢ϯτ৘ใอଘॲཧ
    }
    }

    View Slide

  75. 50%0Ϧετ
    w ΞΧ΢ϯτ৘ใ͕σʔλϕʔεʹอଘ͞Ε
    Δ
    w <>ొ࿥׬ྃϝʔϧΛૹ৴͞ΕΔ

    View Slide

  76. ͞ΒʹςετΛ௥Ճ͢Δ
    /**
    * @test
    */
    public function ΞΧ΢ϯτ৘ใ͕σʔλϕʔεʹอଘ͞ΕΔ()
    {
    $result = $this->AccountService->register();
    // σʔλϕʔεʹอଘ͞Εͨ஋ΛΞαʔτ͢Δ
    }
    /**
    * @test
    */
    public function ొ࿥׬ྃϝʔϧΛૹ৴͞ΕΔ()
    {
    $result = $this->AccountService->register();
    // ొ࿥׬ྃϝʔϧ͕ૹ৴͞Εͨ͜ͱΛΞαʔτ͢Δ
    }

    View Slide

  77. SFHJTUFS
    ͕ԿΛ΍Ζ͏ͱ͍ͯ͠Δ͔Λҙࣝ͢Δ
    w SFHJTUFS
    ϝιου͸ɺҰͭͷ͜ͱΛ͏·͘΍
    Ε͍ͯΔ͔ʁ
    w ʮΞΧ΢ϯτ৘ใΛσʔλϕʔεʹอଘ͠ɺ
    ͦͯ͠ొ࿥׬ྃϝʔϧΛૹ৴͢Δʯ
    w ̎ͭͷॲཧΛಉ࣌ʹ΍Ζ͏ͱ͍ͯ͠Δ

    View Slide

  78. SFHJTUFS
    ͷςετͷॻ͖ͮΒ͞
    w ϝʔϧૹ৴ॲཧΛςετ͍ͨ͠৔߹ɺΞΧ΢
    ϯτͷσʔλϕʔεอଘॲཧͱҰॹʹςετ
    ͠ͳ͚Ε͹ͳΒͳ͍
    w ҟͳΔଐੑͷৼΔ෣͍͕ີʹ݁߹ͯ͠͠·͏
    ͱɺಛఆͷৼΔ෣͍ʹରͯ͠ςετ͕ॻ͖ͮ
    Β͘ͳΔ

    View Slide

  79. 50%0Ϧετ΁ୀආ͢Δ
    w ςετίʔυ͔ΒɺSFHJTUFS
    ͸΍Γ͍͗ͯ͢
    Δ͜ͱ͕Θ͔ͬͨ
    w ͔͠͠ɺ·ͣ͸໨ͷલͷ3FEΛ(SFFOʹ͢Δ
    w Ծ࣮૷ͱͯ͢͢͠Ίɺղܾ͢΂͖ߏ଄্ͷ໰
    ୊Λ50%0Ϧετʹ௥Ճ͢Δ

    View Slide

  80. 50%0Ϧετ
    w ΞΧ΢ϯτ৘ใ͕σʔλϕʔεʹอଘ͞Ε
    Δ
    w <>ొ࿥׬ྃϝʔϧΛૹ৴͞ΕΔ
    w <>ొ࿥׬ྃϝʔϧૹ৴͸ɺ"DDPVOU4FSWJDFΫ
    ϥεͷ੹຿͔Β௥͍ग़͢

    View Slide

  81. ςετΛ௨࣮͢૷Λ͢Δ
    class AccountService
    {
    public function register()
    {
    // ΞΧ΢ϯτ৘ใอଘॲཧ
    ɹɹɹɹ // ϝʔϧૹ৴ॲཧ
    }
    }

    View Slide

  82. 50%0Ϧετ
    w ΞΧ΢ϯτ৘ใ͕σʔλϕʔεʹอଘ͞Ε
    Δ
    w ొ࿥׬ྃϝʔϧΛૹ৴͞ΕΔ
    w <>ొ࿥׬ྃϝʔϧૹ৴͸ɺSFHJTUFS
    ͷ੹຿͔
    Β௥͍ग़͢

    View Slide

  83. ࣦഊ͢ΔςετΛॻ͘
    /**
    * @test
    */
    public function ొ࿥׬ྃϝʔϧΛૹ৴͞ΕΔ()
    {
    $result = $this->AccountMailer->send();
    // ొ࿥׬ྃϝʔϧ͕ૹ৴͞Εͨ͜ͱΛΞαʔτ͢Δ
    }
    w ϝʔϧૹ৴Λߦ͏"DDPVOU.BJMFSΫϥεͷςε
    τΛ࡞੒ɺ"DDPVOU4FSWJDF5FTU͔Βςετϝιο
    υΛҠಈ͢Δ

    View Slide

  84. ςετΛ௨࣮͢૷Λ͢Δ
    class AccountMailer
    {
    public function send(Account $account): bool
    {
    // ϝʔϧૹ৴
    }
    }

    View Slide

  85. 50%0Ϧετ
    w ΞΧ΢ϯτ৘ใ͕σʔλϕʔεʹอଘ͞Ε
    Δ
    w ొ࿥׬ྃϝʔϧΛૹ৴͞ΕΔ
    w ొ࿥׬ྃϝʔϧૹ৴͸ɺSFHJTUFS
    ͷ੹຿
    ͔Β௥͍ग़͢

    View Slide

  86. $BTF4UVEZ͔ΒֶͿ
    w ςετίʔυɾಈ࡞ίʔυΛॻ͖ͳ͕ΒɺʮҰ
    ͭͷ͜ͱΛ͏·͘΍Ε͍ͯΔ͔ʯΛҙࣝͨ͠
    w ్தͰ΍Γ͍͗ͯ͢ΔϝιουΛ՝୊ͱͯ͠
    ೝࣝ͠ɺ50%0ʹͨ͠
    w ΍Γ͗ͨ͢ϝιουΛϦϑΝΫλϦϯάͨ͠

    View Slide

  87. 4VNNBSZ
    wઃܭͷิॿಓ۩ͱͯ͠Ϣ
    χοτςετΛ༻͍Δ
    wςετίʔυͷॻ͖ʹ͘
    ͔͞Βɺઃܭʹର͢Δҧ
    ࿨ײΛټΈऔΔ
    wټΈऔͬͨҧ࿨ײΛϦ
    ϑΝΫλϦϯάͰղফ͢
    Δ

    View Slide

  88. ςετۦಈ։ൃ΁ͷ
    ো֐ΛऔΓআ͘

    View Slide

  89. ςετۦಈ։ൃ΁ͷো֐
    w ςετۦಈ։ൃΛ࣮ફ͢ΔͨΊͷো֐ΛऔΓ
    আ͘
    w Ұͭͷέʔεͱͯ͠ɺςετ͕ͳ͍ίʔυʹͭ
    ͍ͯऔΓѻ͏

    View Slide

  90. $BTF4UVEZ ςετ͕ͳ͍ίʔυ

    View Slide

  91. ςετ͕ͳ͍ίʔυ
    public function index()
    {
    $articles = $this->Articles->find('all');
    $this->set([
    '_serialize' => ['articles'],
    'articles' => $articles,
    ]);
    }
    w هࣄҰཡΛKTPOͰฦ٫͢Δγϯϓϧͳ"1*
    w lެ։͍ͯ͠Δ QVCMJTIFE
    zهࣄͷΈฦ٫͢ΔมߋΛ૝ఆ
    w ͔͠͠ɺલ೚ऀʹΑΔςετίʔυ͸ॻ͔Ε͍ͯͳ͔ͬͨ

    View Slide

  92. ςετۦಈ։ൃͷ౔ඨʹ্͛Δ
    w ·ͣ͸ɺطଘͷৼΔ෣͍Λอޢ͢Δςετίʔ
    υΛॻ͘ʢ࢓༷Խςετʣ
    w อޢͨ͠ޙʹςετۦಈ։ൃͷαΠΫϧΛ࣮ફ
    ͢Δ

    View Slide

  93. ࢓༷Խςετ
    wl$IBSBDUFSJ[BUJPOUFTUz
    wίʔυͷৼΔ෣͍Λ໌Β͔ʹ͢Δςετ

    View Slide

  94. ࢓༷Խςετͷखॱ
    ςετίʔυͷதͰର৅ͷίʔυΛݺͼग़͢
    ࣦഊ͢ΔͱΘ͔͍ͬͯΔද໌Λॻ͘
    ࣦഊͨ݁͠Ռ͔Β࣮ࡍͷৼΔ෣͍Λ֬ೝ͢Δ
    ίʔυ͕࣮ݱ͢ΔৼΔ෣͍Λظ଴͢ΔΑ͏ʹɺ
    ςετΛมߋ͢Δ
    ʙΛ܁Γฦ͢

    View Slide

  95. ର৅ίʔυΛݺͼग़͢
    /**
    * @test
    */
    public function هࣄҰཡऔಘʹͯهࣄҰཡ͕ฦ٫͞ΕΔ()
    {
    $this->configRequest([
    'headers' => [
    'Accept' => 'application/json',
    ],
    ]);
    $this->get('/api/articles/index');
    }

    View Slide

  96. ࣦഊ͢Δද໌Λߦ͏
    /**
    * @test
    */
    public function هࣄҰཡऔಘʹͯهࣄҰཡ͕ฦ٫͞ΕΔ()
    {
    $this->configRequest([
    'headers' => [
    'Accept' => 'application/json',
    ],
    ]);
    $this->get('/api/articles/index');
    $expected = json_encode([], JSON_PRETTY_PRINT);
    $this->assertSame($expected, (string)$this-
    >_response->getBody());
    }

    View Slide

  97. ࣦഊͨ݁͠Ռ͔Β࣮ࡍͷৼΔ෣͍Λ֬ೝ͢Δ
    'BJMFEBTTFSUJOHUIBUUXPTUSJOHTBSFJEFOUJDBM

    View Slide

  98. ςετΛมߋ͢Δ
    $expected = [
    'articles' => [
    [
    'id' => 1,
    'user_id' => 1,
    'title' => 'First Published Article',
    'slug' => 'first-published-article',
    'body' => 'First Published Body',
    'published' => 1,
    'created' => '2018-01-07T15:47:01+00:00',
    'modified' => '2018-01-07T15:47:02+00:00',
    ],
    [
    'id' => 2,
    'user_id' => 1,
    'title' => 'Unpublished Article',
    'slug' => 'unpublished-article',
    'body' => 'Unpublished Article Body',
    'published' => 0,
    'created' => '2018-01-07T15:47:01+00:00',
    'modified' => '2018-01-07T15:47:02+00:00',
    ],
    ],
    ];
    $expected = json_encode($expected, JSON_PRETTY_PRINT);
    $this->assertSame($expected, (string)$this->_response->getBody());

    View Slide

  99. 0, UFTU BTTFSUJPO

    View Slide

  100. มߋ͍ͨ͠ৼΔ෣͍Λςετʹॻ͘
    $expected = [
    'articles' => [
    [
    'id' => 1,
    'user_id' => 1,
    'title' => 'First Published Article',
    'slug' => 'first-published-article',
    'body' => 'First Published Body',
    'published' => 1,
    'created' => '2018-01-07T15:47:01+00:00',
    'modified' => '2018-01-07T15:47:02+00:00',
    ],
    ],
    ];
    $expected = json_encode($expected, JSON_PRETTY_PRINT);
    $this->assertSame($expected, (string)$this->_response->getBody());
    w ެ։த QVCMJTIFE
    ͷΈฦ٫͞ΕΔৼΔ෣͍
    Λظ଴஋ͱͯ͠ઃఆ͢Δ

    View Slide

  101. ςετ͕ࣦഊ͢Δ
    'BJMFEBTTFSUJOHUIBUUXPTUSJOHTBSFJEFOUJDBM

    View Slide

  102. ςετΛ௨࣮͢૷Λߦ͏
    public function index()
    {
    $articles = $this->Articles
    ->find()
    ->where(['published' => 1])
    ->all();
    $this->set([
    '_serialize' => ['articles'],
    'articles' => $articles,
    ]);
    }

    View Slide

  103. 0, UFTU BTTFSUJPO

    View Slide

  104. 4VNNBSZ
    wςετ͕ͳ͍ͱ͍͏ো
    ֐ʹରͯ͠͸ɺ࢓༷Խ
    ςετʹΑͬͯอޢ͢
    Δ
    wͦͷޙɺςετۦಈ։
    ൃͷαΠΫϧ΁ਐΊΔ

    View Slide

  105. ͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠ɻ
    5BML4VNNBSZ
    w ʮςετ͕ਏ͍ʯʹର͢Δ༷ʑͳཁҼ͔Βɺ
    ʮςετΛॻ͘ԯ߷͞ʯɾʮςετ͕ॻ͖ͮ
    Β͍ίʔυʹͳΔέʔεʯʹϑΥʔΧεͨ͠
    w ςετۦಈ։ൃͷϦζϜʹΑͬͯɺςετͱಈ
    ࡞ίʔυΛҰͭͷϦζϜͰਐΊΔ
    w ઃܭͷิॿಓ۩ͱͯ͠ϢχοτςετΛ׆༻
    ͢Δ͜ͱͰɺςετ͕ॻ͖ͮΒ͍ίʔυΛճ
    ආ͢Δ

    View Slide