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

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

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

88964b936e864ca7d326272eaa70fa9a?s=128

Kazuki Higashiguchi

January 24, 2019
Tweet

Transcript

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

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

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

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

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

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

  7. WBS@EVNQ NF  w ౦ޱ࿨ᏻ ,B[VLJ)JHBTIJHVDIJ  w !IHTHUL (JU)VC

    5XJUUFS  w 4FSWFSTJEF&OHJOFFS (P 1)1 FUD  w ॴଐɿ#"4&#"/,גࣜձࣾ w #"4&גࣜձࣾࢠձࣾ
  8. "HFOEB  Ϣχοτςετ͕ਏ͍ཁҼ  ςετۦಈ։ൃͱ͸  ςετۦಈ։ൃΛ׆༻͢Δ  ςετۦಈ։ൃ΁ͷো֐Λ औΓআ͘

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

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

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

    w ςετΛॻ͘ͷ͕೉͍͠ίʔ υʹͳΔ w طଘςετ͕։ൃΛ๦͍͛ͯ Δ
  12. w ςετͷॻ͖ํ͕Θ͔Βͳ͍ w ৗʹҰࠁҰඵΛ૪͍ͬͯΔ w طଘίʔυʹςετ͕ͳ͍ w ςετΛॻ͘ͷ͕ԯ߷ w ςετΛॻ͘ͷ͕೉͍͠ίʔ

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

    υʹͳΔ w طଘςετ͕։ൃΛ๦͍͛ͯ Δ d$BVTFd d1PTTJCMZ4PMVUJPOd w ςετͷॻ͖ํͷशಘ w ϓϩδΣΫτ؅ཧ w ϨΨγʔίʔυվળ w ςετͷॻ͖΍͍͢ઃܭ w Մಡੑɾϝϯςφϯεੑ ͷߴ͍Ϣχοτςετ w ࣮૷தͷςετͷҐஔͮ ͚ͷݟ௚͠ Focus points
  14. ςετΛॻ͘ͷ͕ԯ߷ w ʮςετΛॻ͘ͷ͕ԯ߷ʯͱ͍͏੠Λฉ͘ࡍɺ ಈ࡞ίʔυΛॻ͍ͨޙʹςετΛॻ͘έʔε ͕ଟ͍ w ಈ࡞ίʔυΛॻ͍͔ͯΒɺςετΛॻ͘ͷ͕ ໘౗ w ࣮૷ͷϦζϜͷதʹϢχοτςετ͕͍ͳ͍

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

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

  17. ςετۦಈ։ൃͱ͸

  18. ςετۦಈ։ൃͱ͸ w ϓϩάϥϜ։ൃख๏ͷҰछ w ʮಈ࡞͢Δ͖Ε͍ͳίʔυʯΛΰʔϧͱ͢Δ w ϓϩάϥϛϯάதͷෆ҆Λίϯτϩʔϧ͢Δख ๏ w ࣗಈԽ͞ΕͨςετʹΑͬͯ։ൃΛਪ͠ਐΊ

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

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

    w ͢΂ͯͷςετΛ૸Βͤɺ͢΂ͯ੒ޭ͢Δ͜ͱ Λ֬ೝ͢Δ (SFFO  w ϦϑΝΫλϦϯάΛߦͬͯॏෳΛআڈ͢Δ 3FGBDUPS
  21. ςετۦಈ։ൃͷਐΊํ w 50%0ϦετΛ࡞੒͢Δ w ඞཁʹͳΓͦ͏ͳςετΛॻ͖ग़͍ͯ͘͠ w ్தͰؾ͕͍ͭͨ՝୊Λ௥ه͍ͯ͘͠ w ςετۦಈ։ൃͷαΠΫϧʢ3FE(SFFO 3FGBDUPS

    Λճ͍ͯ͘͠ w 3FE͔Β(SFFOʹ͢ΔͨΊͷzԾ࣮૷zΛ͠ɺ ૉૣ͘αΠΫϧΛճ͢
  22. %FNPOTUSBUJPO wྫɿ$.4ΞϓϦέʔ γϣϯ wهࣄৄࡉऔಘ"1*Λ࣮ ૷͢Δ

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

  24. ςετΛॻ͘ /** * @test */ public function هࣄৄࡉऔಘʹͯ੒ޭϨεϙϯε͕ฦ٫͞ΕΔ() { $this->configRequest([

    'headers' => [ 'Accept' => 'application/json', ], ]); $this->get('/api/articles/view/first'); $this->assertResponseSuccess(); } DEMO ʢࢿྉެ։༻ʣ
  25. 1PTTJCMZSFMBUFEUP $BLFa$POUSPMMFSa&YDFQUJPOa.JTTJOH"DUJPO& YDFQUJPO"DUJPO"SUJDMFT$POUSPMMFSWJFX  DPVMEOPUCFGPVOE PSJTOPUBDDFTTJCMF 'BJMFEBTTFSUJOHUIBUJTCFUXFFO BOE DEMO ʢࢿྉެ։༻ʣ

  26. খ͞ͳมߋΛߦ͏ class ArticlesController extends AppController { public function view($slug =

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

  28. খ͞ͳมߋΛߦ͏ class ArticlesController extends AppController { public function initialize() {

    parent::initialize(); $this->loadComponent('RequestHandler'); } public function view($slug = null) { $this->set([ '_serialize' => ['article'], 'article' => [], ]); } } DEMO ʢࢿྉެ։༻ʣ
  29. 0, UFTUT BTTFSUJPOT DEMO ʢࢿྉެ։༻ʣ

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

  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 ʢࢿྉެ։༻ʣ
  32. DEMO ʢࢿྉެ։༻ʣ 'BJMFEBTTFSUJOHUIBUUXPTUSJOHTBSFJEFOUJDBM

  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 ʢࢿྉެ։༻ʣ
  34. 0, UFTUT BTTFSUJPOT DEMO ʢࢿྉެ։༻ʣ

  35. ϦϑΝΫλϦϯά public function view($slug = null) { $article = $this->Articles->findBySlug($slug)-

    >firstOrFail(); $this->set([ '_serialize' => ['article'], 'article' => $article, ]); } DEMO ʢࢿྉެ։༻ʣ
  36. 0, UFTUT BTTFSUJPOT DEMO ʢࢿྉެ։༻ʣ

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

  38. w ςετͷॻ͖ํ͕Θ͔Βͳ͍ w ৗʹҰࠁҰඵΛ૪͍ͬͯΔ w طଘίʔυʹςετ͕ͳ͍ w ςετΛॻ͘ͷ͕ԯ߷ w ςετΛॻ͘ͷ͕೉͍͠ίʔ

    υʹͳΔ w طଘςετ͕։ൃΛ๦͍͛ͯ Δ d$BVTFd d1PTTJCMZ4PMVUJPOd w ςετͷॻ͖ํͷशಘ w ϓϩδΣΫτ؅ཧ w ϨΨγʔίʔυվળ w ςετͷॻ͖΍͍͢ઃܭ w Մಡੑɾϝϯςφϯεੑ ͷߴ͍Ϣχοτςετ w ࣮૷தͷςετͷҐஔͮ ͚ͷݟ௚͠ Focus points ৼΓฦΓ
  39. ςετۦಈ։ൃ׆༻ͷํ਑ w ςετۦಈ։ൃͷ͍͔ͭ͘ͷଆ໘Λ༗ޮ׆༻ ͢Δ w ςετΛ࠷ॳʹॻ͘ʢςετϑΝʔετʣ͜ ͱʹΑΓɺʮςετ͕ॻ͖ͮΒ͍ʯίʔυΛ ճආ͢Δ w ςετۦಈ։ൃͷϦζϜΛऔΓೖΕΔ͜ͱͰɺ

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

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

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

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

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

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

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

  47. ݱ࣌఺ͷςετ /** * @test */ public function ໰͍߹ΘͤϖʔδʹΞΫηεͰ͖Δ() { $this->get('/inquiries/add');

    $this->assertResponseOk(); } /** * @test */ public function ໰͍߹Θͤอଘ੒ޭ࣌Ұཡϖʔδ΁ϦμΠϨΫτ͢Δ() { $this->enableCsrfToken(); $this->enableSecurityToken(); $data = [ 'title' => 'sample', 'body' => 'sample', 'email' => 'hgsgtk@gmail.com', ]; $this->post('/inquiries/add', $data); $this->assertResponseSuccess(); $this->assertRedirect('/inquiries'); }
  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')); }
  49. 50%0Ϧετ <Y>໰͍߹ΘͤϖʔδʹΞΫηεͰ͖Δ <Y>໰͍߹Θͤอଘ੒ޭ࣌Ұཡϖʔδ΁ϦμΠ ϨΫτ͢Δ <>໰͍߹Θͤͨ͠಺༰͕JORVJSJFTςʔϒϧʹ อଘ͞ΕΔ

  50. ·ͣ͸ςετΛॻ͘ /** * @test */ public function ໰͍߹Θͤͨ͠಺༰͕inquiriesςʔϒϧʹอଘ͞ΕΔ() { $this->enableCsrfToken();

    $this->enableSecurityToken(); $this->configRequest([ 'environment' => ['REMOTE_ADDR' => '12.34.56.78'], ]); $data = [ 'title' => 'sample', 'body' => 'sample', 'email' => 'hgsgtk@gmail.com', ]; $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); }
  51. 'BJMFEBTTFSUJOHUIBUOVMMJTBOJOTUBODFPGDMBTT la"QQa.PEFMa&OUJUZa*ORVJSZ 5FTUT "TTFSUJPOT 'BJMVSFT

  52. ςετΛ௨ͨ͢ΊͷԾ࣮૷ public function add() { $inquiry = $this->Inquiries->newEntity(); if ($this->request->is('post'))

    { // FIXME temporary $inquiry->title = 'sample'; $inquiry->body = 'sample'; $inquiry->email = 'hgsgtk@gmail.com'; $inquiry->client_ip = ’12.34.56.78'; $this->Inquiries->save($inquiry); return $this->redirect(['action' => 'index']); } $this->set(compact('inquiry')); }
  53. 0, UFTUT BTTFSUJPOT

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

  55. ࣮૷ऀ͕಄ʹු͔Μ࣮ͩ૷ public function saveWithIp(Inquiry $entity, \Cake\Http\ServerRequest $request) { $entity->client_ip =

    $request->clientIp(); return $this->save($entity); } w .PEFM૚ʹϝιουΛ࡞੒ w ϦΫΤετΦϒδΣΫτΛड͚औͬͯอଘ
  56. ςετΛॻ͘ /** * @test */ public function ໰͍߹Θͤͨ͠಺༰͕inquiriesςʔϒϧʹอଘ͞ΕΔ() { $inquiry

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

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

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

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

    = $this->Inquiries->newEntity( [ 'title' => 'sample', 'body' => 'sample', 'email' => 'example@example.com', ‘client_ip' => ‘171.0.0.1’, ] ); $actual = $this->Inquiries->saveWithIp($inquiry); // Assert something } ݁ՌతʹɺΤϯςΟςΟΛอଘ͢Δ͚ͩʹ ͳͬͨɻ ϝιουΛ࡞੒͢Δ͜ͱ͕ͦ΋ͦ΋ෆཁ ͱ൑அɻ
  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')); }
  62. 0, UFTUT BTTFSUJPOT

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

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

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

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

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

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

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

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

  71. $BTF4UVEZ ΞΧ΢ϯτొ࿥

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

  73. ࣦഊ͢ΔςετΛॻ͘ /** * @test */ public function ΞΧ΢ϯτ৘ใ͕σʔλϕʔεʹอଘ͞ΕΔ() { $result

    = $this->AccountService->register(); // σʔλϕʔεʹอଘ͞Εͨ஋ΛΞαʔτ͢Δ }
  74. ςετΛ௨࣮͢૷Λ͢Δ class AccountService { public function register() { // ΞΧ΢ϯτ৘ใอଘॲཧ

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

  76. ͞ΒʹςετΛ௥Ճ͢Δ /** * @test */ public function ΞΧ΢ϯτ৘ใ͕σʔλϕʔεʹอଘ͞ΕΔ() { $result

    = $this->AccountService->register(); // σʔλϕʔεʹอଘ͞Εͨ஋ΛΞαʔτ͢Δ } /** * @test */ public function ొ࿥׬ྃϝʔϧΛૹ৴͞ΕΔ() { $result = $this->AccountService->register(); // ొ࿥׬ྃϝʔϧ͕ૹ৴͞Εͨ͜ͱΛΞαʔτ͢Δ }
  77. SFHJTUFS ͕ԿΛ΍Ζ͏ͱ͍ͯ͠Δ͔Λҙࣝ͢Δ w SFHJTUFS ϝιου͸ɺҰͭͷ͜ͱΛ͏·͘΍ Ε͍ͯΔ͔ʁ w ʮΞΧ΢ϯτ৘ใΛσʔλϕʔεʹอଘ͠ɺ ͦͯ͠ొ࿥׬ྃϝʔϧΛૹ৴͢Δʯ w

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

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

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

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

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

  83. ࣦഊ͢ΔςετΛॻ͘ /** * @test */ public function ొ࿥׬ྃϝʔϧΛૹ৴͞ΕΔ() { $result

    = $this->AccountMailer->send(); // ొ࿥׬ྃϝʔϧ͕ૹ৴͞Εͨ͜ͱΛΞαʔτ͢Δ } w ϝʔϧૹ৴Λߦ͏"DDPVOU.BJMFSΫϥεͷςε τΛ࡞੒ɺ"DDPVOU4FSWJDF5FTU͔Βςετϝιο υΛҠಈ͢Δ
  84. ςετΛ௨࣮͢૷Λ͢Δ class AccountMailer { public function send(Account $account): bool {

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

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

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

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

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

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

  91. ςετ͕ͳ͍ίʔυ public function index() { $articles = $this->Articles->find('all'); $this->set([ '_serialize'

    => ['articles'], 'articles' => $articles, ]); } w هࣄҰཡΛKTPOͰฦ٫͢Δγϯϓϧͳ"1* w lެ։͍ͯ͠Δ QVCMJTIFE zهࣄͷΈฦ٫͢ΔมߋΛ૝ఆ w ͔͠͠ɺલ೚ऀʹΑΔςετίʔυ͸ॻ͔Ε͍ͯͳ͔ͬͨ
  92. ςετۦಈ։ൃͷ౔ඨʹ্͛Δ w ·ͣ͸ɺطଘͷৼΔ෣͍Λอޢ͢Δςετίʔ υΛॻ͘ʢ࢓༷Խςετʣ w อޢͨ͠ޙʹςετۦಈ։ൃͷαΠΫϧΛ࣮ફ ͢Δ

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

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

     ʙΛ܁Γฦ͢
  95. ର৅ίʔυΛݺͼग़͢ /** * @test */ public function هࣄҰཡऔಘʹͯهࣄҰཡ͕ฦ٫͞ΕΔ() { $this->configRequest([

    'headers' => [ 'Accept' => 'application/json', ], ]); $this->get('/api/articles/index'); }
  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()); }
  97. ࣦഊͨ݁͠Ռ͔Β࣮ࡍͷৼΔ෣͍Λ֬ೝ͢Δ 'BJMFEBTTFSUJOHUIBUUXPTUSJOHTBSFJEFOUJDBM

  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());
  99. 0, UFTU BTTFSUJPO

  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 ͷΈฦ٫͞ΕΔৼΔ෣͍ Λظ଴஋ͱͯ͠ઃఆ͢Δ
  101. ςετ͕ࣦഊ͢Δ 'BJMFEBTTFSUJOHUIBUUXPTUSJOHTBSFJEFOUJDBM

  102. ςετΛ௨࣮͢૷Λߦ͏ public function index() { $articles = $this->Articles ->find() ->where(['published'

    => 1]) ->all(); $this->set([ '_serialize' => ['articles'], 'articles' => $articles, ]); }
  103. 0, UFTU BTTFSUJPO

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

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

    ઃܭͷิॿಓ۩ͱͯ͠ϢχοτςετΛ׆༻ ͢Δ͜ͱͰɺςετ͕ॻ͖ͮΒ͍ίʔυΛճ ආ͢Δ