{"id":297528,"date":"2020-01-22T21:00:18","date_gmt":"2020-01-22T21:00:18","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=297528"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=297528","title":{"rendered":"\u041a\u0430\u043a \u0441\u043e\u0437\u0434\u0430\u0442\u044c REST API \u043d\u0430 Laravel \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Test-Driven Development"},"content":{"rendered":"\n<div class=\"post__text post__text-html\" id=\"post-content-body\" data-io-article-url=\"https:\/\/habr.com\/ru\/company\/otus\/blog\/485066\/\"><i>\u0421\u0430\u043b\u044e\u0442, \u0445\u0430\u0431\u0440\u043e\u0432\u0447\u0430\u043d\u0435! \u041c\u044b \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0435\u043c \u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u043c \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u043e\u043c \u0432 \u043f\u0440\u0435\u0434\u0434\u0432\u0435\u0440\u0438\u0438 \u0441\u0442\u0430\u0440\u0442\u0430 \u043a\u0443\u0440\u0441\u0430 <a href=\"https:\/\/otus.pw\/S7GG\/\">\u00abFramework Laravel\u00bb<\/a>. \u041f\u043e\u0435\u0445\u0430\u043b\u0438.<\/i>  <\/p>\n<hr>\n<p>  \u041a\u0430\u043a \u043e\u0434\u043d\u0430\u0436\u0434\u044b \u0441\u043a\u0430\u0437\u0430\u043b <a href=\"https:\/\/wingman-sw.com\/about\">\u0414\u0436\u0435\u0439\u043c\u0441 \u0413\u0440\u0435\u043d\u0438\u043d\u0433<\/a>, \u043e\u0434\u0438\u043d \u0438\u0437 \u043f\u0438\u043e\u043d\u0435\u0440\u043e\u0432 TDD \u0438 \u043c\u0435\u0442\u043e\u0434\u043e\u043b\u043e\u0433\u0438\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 Agile:  <\/p>\n<blockquote><p>\u00ab\u0415\u0441\u043b\u0438 \u0432\u044b \u043d\u0435 \u0437\u0430\u043d\u0438\u043c\u0430\u0435\u0442\u0435\u0441\u044c \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u043e\u0439 \u0447\u0435\u0440\u0435\u0437 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435, \u0442\u043e \u043f\u043e\u0437\u0436\u0435 \u0432\u044b \u0437\u0430\u0439\u043c\u0435\u0442\u0435\u0441\u044c \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u043e\u0439 \u0447\u0435\u0440\u0435\u0437 \u043e\u0442\u043b\u0430\u0434\u043a\u0443\u00bb<\/p>\n<p>  \u2013 \u0414\u0436\u0435\u0439\u043c\u0441 \u0413\u0440\u0435\u043d\u0438\u043d\u0433<\/p><\/blockquote>\n<p>  \u0421\u0435\u0433\u043e\u0434\u043d\u044f \u043c\u044b \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u043c\u0441\u044f \u0432 \u043f\u0443\u0442\u0435\u0448\u0435\u0441\u0442\u0432\u0438\u0435 \u043f\u043e \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u0447\u0435\u0440\u0435\u0437 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0441 Laravel. \u041c\u044b \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c REST API \u043d\u0430 Laravel \u0441 \u043f\u043e\u043b\u043d\u044b\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u043e\u043c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0438 CRUD \u043d\u0435 \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u044f Postman \u0438\u043b\u0438 \u0431\u0440\u0430\u0443\u0437\u0435\u0440.<\/p>\n<p>  <b><i>\u041f\u0440\u0438\u043c\u0435\u0447\u0430\u043d\u0438\u0435:<\/i> \u0421\u0435\u0433\u043e\u0434\u043d\u044f\u0448\u043d\u0435\u0435 \u043f\u043e\u0448\u0430\u0433\u043e\u0432\u043e\u0435 \u0440\u0443\u043a\u043e\u0432\u043e\u0434\u0441\u0442\u0432\u043e \u043f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u0435\u0442, \u0447\u0442\u043e \u0432\u044b \u0437\u043d\u0430\u043a\u043e\u043c\u044b \u0441 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u043c\u0438 \u043f\u043e\u043d\u044f\u0442\u0438\u044f\u043c\u0438 \u0432 <a href=\"https:\/\/laravel.com\/docs\/5.7\">Laravel<\/a> \u0438 <a href=\"https:\/\/phpunit.de\/documentation.html\">PHPUnit<\/a>. \u0415\u0441\u043b\u0438 \u0432\u0441\u0435 \u0432 \u043f\u043e\u0440\u044f\u0434\u043a\u0435, \u0442\u043e\u0433\u0434\u0430 \u043f\u043e\u0435\u0445\u0430\u043b\u0438!<\/b><a name=\"habracut\"><\/a>  <\/p>\n<h3>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/h3>\n<p>  \u041d\u0430\u0447\u043d\u0435\u043c \u043c\u044b \u0441 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043d\u043e\u0432\u043e\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 Laravel \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e c<code>omposer create-project --prefer-dist laravel\/laravel tdd-journey<\/code>.<\/p>\n<p>  \u0414\u0430\u043b\u0435\u0435, \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0441\u043a\u0430\u0444\u0444\u043e\u043b\u0434\u0435\u0440 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f, \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u0435<code> php artisan make:auth<\/code>, \u0430 \u0437\u0430\u0442\u0435\u043c <code>php artisan migrate<\/code>.<\/p>\n<p>  \u041d\u0430 \u0441\u0430\u043c\u043e\u043c \u0434\u0435\u043b\u0435 \u043c\u044b \u043d\u0435 \u0431\u0443\u0434\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u043c\u0438 \u043f\u0443\u0442\u044f\u043c\u0438 \u0438 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f\u043c\u0438. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <a href=\"https:\/\/github.com\/tymondesigns\/jwt-auth\">jwt-auth<\/a>, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0434\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0435\u0433\u043e \u0432 \u0441\u0432\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435. <\/p>\n<p>  <b><i>\u041f\u0440\u0438\u043c\u0435\u0447\u0430\u043d\u0438\u0435:<\/i> \u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u0432\u043e\u0437\u043d\u0438\u043a\u043b\u0438 \u043e\u0448\u0438\u0431\u043a\u0438 \u0432 \u043a\u043e\u043c\u0430\u043d\u0434\u0435 generate JWT, \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u043e\u0442 <a href=\"https:\/\/github.com\/tymondesigns\/jwt-auth\/issues\/1298#issuecomment-330458018\">\u0444\u0438\u043a\u0441<\/a>, \u043f\u043e\u043a\u0430 \u043d\u0435 \u0434\u043e\u0431\u044c\u0435\u0442\u0435\u0441\u044c \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b.<\/b><\/p>\n<p>  \u041d\u0430\u043a\u043e\u043d\u0435\u0446, \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0443\u0434\u0430\u043b\u0438\u0442\u044c <i>ExampleTest <\/i>\u0432 \u043f\u0430\u043f\u043a\u0430\u0445 <i>tests\/Unit<\/i> \u0438 <i>tests\/Feature<\/i>, \u0447\u0442\u043e\u0431\u044b \u043d\u0438\u0447\u0442\u043e \u043d\u0435 \u043c\u0435\u0448\u0430\u043b\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f, \u0438 \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0442\u044c!<\/p>\n<h3>\u041f\u0438\u0448\u0435\u043c \u043a\u043e\u0434<\/h3>\n<p>  1. \u041d\u0430\u0447\u043d\u0435\u043c \u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 <code>auth<\/code>, \u0447\u0442\u043e\u0431\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u0440\u0430\u0439\u0432\u0435\u0440 JWT \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e:<\/p>\n<pre><code class=\"php\">&lt;?php  \/\/ config\/auth.php file 'defaults' =&gt; [     'guard' =&gt; 'api',     'passwords' =&gt; 'users', ],  'guards' =&gt; [     ...     'api' =&gt; [         'driver' =&gt; 'jwt',         'provider' =&gt; 'users',     ], ], view rawauth.php hosted with  by GitHub<\/code><\/pre>\n<p>  \u0417\u0430\u0442\u0435\u043c \u0434\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435 \u0432 \u0444\u0430\u0439\u043b <code>routes\/api.php<\/code>:<\/p>\n<pre><code class=\"php\">&lt;?php Route::group(['middleware' =&gt; 'api', 'prefix' =&gt; 'auth'], function () {      Route::post('authenticate', 'AuthController@authenticate')-&gt;name('api.authenticate');     Route::post('register', 'AuthController@register')-&gt;name('api.register'); }); view rawapi1.php hosted with  by GitHub<\/code><\/pre>\n<p>  2. \u0422\u0435\u043f\u0435\u0440\u044c, \u043a\u043e\u0433\u0434\u0430 \u043c\u044b \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043b\u0438 \u0434\u0440\u0430\u0439\u0432\u0435\u0440, \u043d\u0443\u0436\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0443\u044e \u043c\u043e\u0434\u0435\u043b\u044c \u0442\u0430\u043a\u0438\u043c \u0436\u0435 \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code class=\"php\">&lt;?php ... class User extends Authenticatable implements JWTSubject {     ...      \/\/Get the identifier that will be stored in the subject claim of the JWT.     public function getJWTIdentifier()     {         return $this-&gt;getKey();     }     \/\/ Return a key value array, containing any custom claims to be           added to the JWT.     public function getJWTCustomClaims()     {         return [];     } } view rawUser1.php hosted with  by GitHub<\/code><\/pre>\n<p>  \u0417\u0434\u0435\u0441\u044c \u043c\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043b\u0438 <code>JWTSubject<\/code> \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u043b\u0438 \u0442\u0440\u0435\u0431\u0443\u0435\u043c\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b.<\/p>\n<p>  3. \u041d\u0430\u043a\u043e\u043d\u0435\u0446 \u043d\u0430\u0441\u0442\u0430\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043c\u0435\u0442\u043e\u0434\u044b \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0432 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440.<br \/>  \u0412\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u0435 <code>php artisan make:controller AuthController<\/code> \u0418 \u0434\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u043c\u0435\u0442\u043e\u0434\u044b:<\/p>\n<pre><code class=\"php\">&lt;?php ... class AuthController extends Controller {          public function authenticate(Request $request){         \/\/Validate fields         $this-&gt;validate($request,['email' =&gt; 'required|email','password'=&gt; 'required']);         \/\/Attempt validation         $credentials = $request-&gt;only(['email','password']);         if (! $token = auth()-&gt;attempt($credentials)) {             return response()-&gt;json(['error' =&gt; 'Incorrect credentials'], 401);         }         return response()-&gt;json(compact('token'));     }     public function register(Request $request){         \/\/Validate fields         $this-&gt;validate($request,[             'email' =&gt; 'required|email|max:255|unique:users',             'name' =&gt; 'required|max:255',             'password' =&gt; 'required|min:8|confirmed',         ]);         \/\/Create user, generate token and return         $user =  User::create([             'name' =&gt; $request-&gt;input('name'),             'email' =&gt; $request-&gt;input('email'),             'password' =&gt; Hash::make($request-&gt;input('password')),         ]);         $token = JWTAuth::fromUser($user);         return response()-&gt;json(compact('token'));     } } view rawAuthcontroller1.php hosted with  by GitHub<\/code><\/pre>\n<p>  \u041d\u0430 \u044d\u0442\u043e\u043c \u0448\u0430\u0433\u0435 \u0432\u0441\u0435 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0432\u0441\u0435, \u0447\u0442\u043e \u043c\u044b \u0434\u0435\u043b\u0430\u0435\u043c, \u044d\u0442\u043e \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043c\u0435\u0442\u043e\u0434\u044b <i>authenticate <\/i>\u0438 <i>register <\/i>\u043a \u043d\u0430\u0448\u0435\u043c\u0443 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0443. \u0412 \u043c\u0435\u0442\u043e\u0434\u0435 <i>authenticate <\/i>\u043c\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0432\u0445\u043e\u0434\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435, \u043f\u044b\u0442\u0430\u0435\u043c\u0441\u044f \u0437\u0430\u043b\u043e\u0433\u0438\u043d\u0438\u0442\u044c\u0441\u044f \u0438 \u0432\u0435\u0440\u043d\u0443\u0442\u044c \u0442\u043e\u043a\u0435\u043d \u043f\u0440\u0438 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u043c \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0438. \u0412 \u043c\u0435\u0442\u043e\u0434\u0435 <i>register <\/i>\u043c\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0432\u0445\u043e\u0434\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435, \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043d\u043e\u0432\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0441 \u0432\u0445\u043e\u0434\u043d\u044b\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u0438 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u043c \u0442\u043e\u043a\u0435\u043d \u0434\u043b\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043d\u0430 \u0438\u0445 \u043e\u0441\u043d\u043e\u0432\u0435.<\/p>\n<p>  4. \u0422\u0435\u043f\u0435\u0440\u044c \u043f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u043a \u043f\u0440\u0438\u044f\u0442\u043d\u043e\u0439 \u0447\u0430\u0441\u0442\u0438. \u041f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c \u0442\u043e, \u0447\u0442\u043e \u043c\u044b \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0442\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u043b\u0438. \u0421\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0439\u0442\u0435 \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0435 \u043a\u043b\u0430\u0441\u0441\u044b \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e<code> php artisan make:test AuthTest<\/code>. \u0412 \u043d\u043e\u0432\u044b\u0439 \u0444\u0430\u0439\u043b <code>tests\/Feature\/AuthTest<\/code> \u0434\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u043c\u0435\u0442\u043e\u0434\u044b:<\/p>\n<pre><code class=\"php\">&lt;?php  \/**  * @test   * Test registration  *\/ public function testRegister(){     \/\/User's data     $data = [         'email' =&gt; 'test@gmail.com',         'name' =&gt; 'Test',         'password' =&gt; 'secret1234',         'password_confirmation' =&gt; 'secret1234',     ];     \/\/Send post request     $response = $this-&gt;json('POST',route('api.register'),$data);     \/\/Assert it was successful     $response-&gt;assertStatus(200);     \/\/Assert we received a token     $this-&gt;assertArrayHasKey('token',$response-&gt;json());     \/\/Delete data     User::where('email','test@gmail.com')-&gt;delete(); } \/**  * @test  * Test login  *\/ public function testLogin() {     \/\/Create user     User::create([         'name' =&gt; 'test',         'email'=&gt;'test@gmail.com',         'password' =&gt; bcrypt('secret1234')     ]);     \/\/attempt login     $response = $this-&gt;json('POST',route('api.authenticate'),[         'email' =&gt; 'test@gmail.com',         'password' =&gt; 'secret1234',     ]);     \/\/Assert it was successful and a token was received     $response-&gt;assertStatus(200);     $this-&gt;assertArrayHasKey('token',$response-&gt;json());     \/\/Delete the user     User::where('email','test@gmail.com')-&gt;delete(); } view rawAuthTest1.php hosted with  by GitHub<\/code><\/pre>\n<p>  \u041a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438 \u0432 \u043a\u043e\u0434\u0435 \u0432\u044b\u0448\u0435 \u0432 \u0437\u043d\u0430\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u0441\u0442\u0435\u043f\u0435\u043d\u0438 \u0432\u0441\u0435 \u043e\u0431\u044a\u044f\u0441\u043d\u044f\u044e\u0442. \u0412\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u044c \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 \u0442\u043e, \u043a\u0430\u043a \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0438 \u0443\u0434\u0430\u043b\u044f\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432 \u043a\u0430\u0436\u0434\u043e\u043c \u0442\u0435\u0441\u0442\u0435. \u0412\u0435\u0441\u044c \u0441\u043c\u044b\u0441\u043b \u0442\u0435\u0441\u0442\u043e\u0432 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u043e\u043d\u0438 \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u044b \u0434\u0440\u0443\u0433 \u043e\u0442 \u0434\u0440\u0443\u0433\u0430 \u0438 \u0432 \u0438\u0434\u0435\u0430\u043b\u0435 \u043e\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u0435 <code>$vendor\/bin\/phpunit<\/code> \u0438\u043b\u0438 <code>$phpunit<\/code>, \u0435\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u0430\u044f \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430. \u0423 \u0432\u0430\u0441 \u0434\u043e\u043b\u0436\u043d\u044b \u043f\u043e\u044f\u0432\u0438\u0442\u044c\u0441\u044f \u0443\u0441\u043f\u0435\u0448\u043d\u044b\u0435 \u0443\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f. \u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c, \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043b\u043e\u0433\u0438, \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u0442\u043e, \u0447\u0442\u043e \u043f\u043e\u0448\u043b\u043e \u043d\u0435 \u0442\u0430\u043a \u0438 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0442\u0435\u0441\u0442\u044b \u0441\u043d\u043e\u0432\u0430. \u0422\u0430\u043a \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0445\u043e\u0440\u043e\u0448\u0438\u0439 \u0446\u0438\u043a\u043b TDD.<\/p>\n<p>  5. \u0422\u0435\u043f\u0435\u0440\u044c, \u043a\u043e\u0433\u0434\u0430 \u043d\u0430\u0448\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442, \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u044d\u043b\u0435\u043c\u0435\u043d\u0442 \u0434\u043b\u044f CRUD. \u0412 \u043d\u0430\u0448\u0435\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0440\u0435\u0446\u0435\u043f\u0442\u044b \u0431\u043b\u044e\u0434 \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 CRUD, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e, \u043f\u043e\u0447\u0435\u043c\u0443 \u0431\u044b \u0438 \u043d\u0435\u0442?<\/p>\n<p>  \u041d\u0430\u0447\u043d\u0438\u0442\u0435 \u0441 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438 <code>php artisan make:migration create_recipes_table <\/code>\u0438 \u0434\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435:<\/p>\n<pre><code class=\"php\">&lt;?php  ... public function up() {     Schema::create('recipes', function (Blueprint $table) {         $table-&gt;increments('id');         $table-&gt;string('title');         $table-&gt;text('procedure')-&gt;nullable();         $table-&gt;tinyInteger('publisher_id')-&gt;nullable();         $table-&gt;timestamps();     }); }  public function down() {     Schema::dropIfExists('recipes'); } view rawmigration.php hosted with  by GitHub<\/code><\/pre>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u0435 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u044e. \u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u0434\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u043c\u043e\u0434\u0435\u043b\u044c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <code>php artisan make:model Recipe<\/code> \u0438 \u0434\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0435\u0435 \u043a \u043d\u0430\u0448\u0435\u0439 \u043c\u043e\u0434\u0435\u043b\u0438. <\/p>\n<pre><code class=\"php\">&lt;?php  ... protected $fillable = ['title','procedure'];  \/**  * The owner of this delicious recipe  * @return \\Illuminate\\Database\\Eloquent\\Relations\\BelongsTo  *\/ public function publisher(){     return $this-&gt;belongsTo(User::class); } view rawrecipe1.php hosted with  by GitHub<\/code><\/pre>\n<p>  \u0417\u0430\u0442\u0435\u043c \u0434\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u044d\u0442\u043e\u0442 \u043c\u0435\u0442\u043e\u0434 \u0432 \u043c\u043e\u0434\u0435\u043b\u044c <code>user<\/code>. <\/p>\n<pre><code class=\"php\">&lt;?php ...   \/**  * Get all recipes  * @return \\Illuminate\\Database\\Eloquent\\Relations\\HasMany  *\/ public function recipes(){     return $this-&gt;hasMany(Recipe::class); } view rawuser2.php hosted with  by GitHub<\/code><\/pre>\n<p>  6. \u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u044b \u043a\u043e\u043d\u0435\u0447\u043d\u044b\u0435 \u0442\u043e\u0447\u043a\u0438 \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043d\u0430\u0448\u0438\u0445 \u0440\u0435\u0446\u0435\u043f\u0442\u043e\u0432. \u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 <code>php artisan make:controller RecipeController<\/code>. \u0417\u0430\u0442\u0435\u043c, \u043e\u0442\u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0443\u0435\u043c \u0444\u0430\u0439\u043b <code>routes\/api.php<\/code> \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0442\u0443\u0434\u0430 \u043a\u043e\u043d\u0435\u0447\u043d\u0443\u044e \u0442\u043e\u0447\u043a\u0443 <code>create<\/code>.<\/p>\n<pre><code class=\"php\">&lt;?php  ...   Route::group(['middleware' =&gt; ['api','auth'],'prefix' =&gt; 'recipe'],function (){     Route::post('create','RecipeController@create')-&gt;name('recipe.create'); }); view rawroutes2.php hosted with  by GitHub<\/code><\/pre>\n<p>  \u0422\u0430\u043a\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043c\u0435\u0442\u043e\u0434 <i>create<\/i> \u0432 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440:<\/p>\n<pre><code class=\"php\">&lt;?php  ...   public function create(Request $request){     \/\/Validate     $this-&gt;validate($request,['title' =&gt; 'required','procedure' =&gt; 'required|min:8']);     \/\/Create recipe and attach to user     $user = Auth::user();     $recipe = Recipe::create($request-&gt;only(['title','procedure']));     $user-&gt;recipes()-&gt;save($recipe);     \/\/Return json of recipe     return $recipe-&gt;toJson(); } view rawRecipeController.php hosted with  by GitHub<\/code><\/pre>\n<p>  \u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u0442\u0435\u0441\u0442 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e<code> php artisan make:test RecipeTest<\/code> \u0438 \u043e\u0442\u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0443\u0439\u0442\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u043a\u0430\u043a \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u043e \u043d\u0438\u0436\u0435:<\/p>\n<pre><code class=\"php\">&lt;?php  ... class RecipeTest extends TestCase {     use RefreshDatabase;     ...     \/\/Create user and authenticate the user     protected function authenticate(){         $user = User::create([             'name' =&gt; 'test',             'email' =&gt; 'test@gmail.com',             'password' =&gt; Hash::make('secret1234'),         ]);         $token = JWTAuth::fromUser($user);         return $token;     }         public function testCreate()     {         \/\/Get token         $token = $this-&gt;authenticate();          $response = $this-&gt;withHeaders([             'Authorization' =&gt; 'Bearer '. $token,         ])-&gt;json('POST',route('recipe.create'),[             'title' =&gt; 'Jollof Rice',             'procedure' =&gt; 'Parboil rice, get pepper and mix, and some spice and serve!'         ]);         $response-&gt;assertStatus(200);     } } view rawRecipeTest.php hosted with  by GitHub<\/code><\/pre>\n<p>  \u042d\u0442\u043e\u0442 \u043a\u043e\u0434 \u0442\u043e\u0436\u0435 \u0433\u043e\u0432\u043e\u0440\u0438\u0442 \u0441\u0430\u043c \u0437\u0430 \u0441\u0435\u0431\u044f. \u0412\u0441\u0435, \u0447\u0442\u043e \u043c\u044b \u0434\u0435\u043b\u0430\u0435\u043c, \u2014 \u044d\u0442\u043e \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043c\u0435\u0442\u043e\u0434, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044e \u0442\u043e\u043a\u0435\u043d\u0430, \u0430 \u0437\u0430\u0442\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u044d\u0442\u043e\u0442 \u0442\u043e\u043a\u0435\u043d \u0432 \u043c\u0435\u0442\u043e\u0434\u0435 <code>testCreate()<\/code>. \u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 <code>RefreshDatabase<\/code>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0443\u0434\u043e\u0431\u043d\u044b\u043c \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u043c \u0441\u0431\u0440\u043e\u0441\u0430 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u0441\u043b\u0435 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0430 \u0432 Laravel, \u0447\u0442\u043e \u0438\u0434\u0435\u0430\u043b\u044c\u043d\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442 \u0434\u043b\u044f \u043d\u0430\u0448\u0435\u0433\u043e \u043c\u0430\u043b\u0435\u043d\u044c\u043a\u043e\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430.<\/p>\n<p>  \u0418\u0442\u0430\u043a, \u043f\u043e\u043a\u0430 \u0432\u0441\u0435, \u0447\u0442\u043e \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u2013 \u044d\u0442\u043e \u0441\u0442\u0430\u0442\u0443\u0441 \u043e\u0442\u0432\u0435\u0442\u0430, \u0438\u0434\u0438\u0442\u0435 \u0434\u0430\u043b\u044c\u0448\u0435 \u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u0435 <code>$vendor\/bin\/phpunit<\/code>.<\/p>\n<p>  \u0415\u0441\u043b\u0438 \u0432\u0441\u0435 \u0438\u0434\u0435\u0442 \u043f\u043e \u043f\u043b\u0430\u043d\u0443, \u0442\u043e \u0432\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0435 \u043e\u0448\u0438\u0431\u043a\u0443 <\/p>\n<pre><code class=\"php\">There was 1 failure: 1) Tests\\Feature\\RecipeTest::testCreate Expected status code 200 but received 500. Failed asserting that false is true. \/home\/user\/sites\/tdd-journey\/vendor\/laravel\/framework\/src\/Illuminate\/Foundation\/Testing\/TestResponse.php:133 \/home\/user\/sites\/tdd-journey\/tests\/Feature\/RecipeTest.php:49 FAILURES! Tests: 3, Assertions: 5, Failures: 1.<\/code><\/pre>\n<p>  \u0412\u0437\u0433\u043b\u044f\u043d\u0443\u0432 \u043d\u0430 \u043b\u043e\u0433\u0438, \u043c\u044b \u043f\u043e\u0439\u043c\u0435\u043c, \u0447\u0442\u043e \u0432\u0438\u043d\u043e\u0432\u043d\u0438\u043a\u043e\u043c \u043e\u0448\u0438\u0431\u043a\u0438 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043e\u0442\u043d\u043e\u0448\u0435\u043d\u0438\u0435 \u0438\u0437\u0434\u0430\u0442\u0435\u043b\u044f \u0438 \u0440\u0435\u0446\u0435\u043f\u0442\u043e\u0432 \u0432 \u043a\u043b\u0430\u0441\u0441\u0430\u0445 <i>Recipe <\/i>\u0438 <i>User<\/i>. Laravel \u043f\u044b\u0442\u0430\u0435\u0442\u0441\u044f \u043d\u0430\u0439\u0442\u0438 \u0432 \u0442\u0430\u0431\u043b\u0438\u0446\u0435 \u0441\u0442\u043e\u043b\u0431\u0435\u0446 <code>user_id<\/code> \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0435\u0433\u043e \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0432\u043d\u0435\u0448\u043d\u0435\u0433\u043e \u043a\u043b\u044e\u0447\u0430, \u043e\u0434\u043d\u0430\u043a\u043e \u0432 \u0441\u0432\u043e\u0435\u0439 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u043c\u044b \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043b\u0438 <code>publisher_id<\/code> \u043a\u0430\u043a \u0432\u043d\u0435\u0448\u043d\u0438\u0439 \u043a\u043b\u044e\u0447. \u0422\u0435\u043f\u0435\u0440\u044c \u043f\u0435\u0440\u0435\u043f\u0438\u0448\u0438\u0442\u0435 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u0442\u0440\u043e\u043a, \u043a\u0430\u043a \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u043e \u043d\u0438\u0436\u0435:<\/p>\n<pre><code class=\"php\">\/\/Recipe file public function publisher(){     return $this-&gt;belongsTo(User::class,'publisher_id'); } \/\/User file public function recipes(){     return $this-&gt;hasMany(Recipe::class,'publisher_id'); }<\/code><\/pre>\n<p>  \u0410 \u0437\u0430\u0442\u0435\u043c \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 \u0442\u0435\u0441\u0442\u044b. \u0415\u0441\u043b\u0438 \u0432\u0441\u0435 \u0432 \u043f\u043e\u0440\u044f\u0434\u043a\u0435, \u0442\u043e \u0442\u0435\u0441\u0442\u044b \u0437\u0430\u0432\u0435\u0440\u0448\u0430\u0442\u0441\u044f \u0443\u0441\u043f\u0435\u0448\u043d\u043e! <\/p>\n<pre><code class=\"php\">...                                                                 3 \/ 3 (100%) ... OK (3 tests, 5 assertions)<\/code><\/pre>\n<p>  \u041e\u0434\u043d\u0430\u043a\u043e \u043d\u0430\u043c \u0432\u0441\u0435 \u0435\u0449\u0435 \u043d\u0430\u0434\u043e \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0440\u0435\u0446\u0435\u043f\u0442\u0430. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0443\u0436\u043d\u043e \u0443\u0442\u0432\u0435\u0440\u0436\u0434\u0430\u0442\u044c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0440\u0435\u0446\u0435\u043f\u0442\u043e\u0432 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. \u041e\u0431\u043d\u043e\u0432\u0438\u0442\u0435 \u043c\u0435\u0442\u043e\u0434 <code>testCreate<\/code>, \u043a\u0430\u043a \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u043e \u043d\u0438\u0436\u0435:<\/p>\n<pre><code class=\"php\">&lt;?php ... \/\/Get token $token = $this-&gt;authenticate();  $response = $this-&gt;withHeaders([     'Authorization' =&gt; 'Bearer '. $token, ])-&gt;json('POST',route('recipe.create'),[     'title' =&gt; 'Jollof Rice',     'procedure' =&gt; 'Parboil rice, get pepper and mix, and some spice and serve!' ]); $response-&gt;assertStatus(200); \/\/Get count and assert $count = User::where('email','test@gmail.com')-&gt;first()-&gt;recipes()-&gt;count(); $this-&gt;assertEquals(1,$count); view rawRecipeTest.php hosted with  by GitHub<\/code><\/pre>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u0440\u043e\u0434\u0432\u0438\u043d\u0443\u0442\u044c\u0441\u044f \u0434\u0430\u043b\u044c\u0448\u0435 \u0438 \u0437\u0430\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b. \u041d\u0430\u0441\u0442\u0430\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u043a\u043e\u0435-\u0447\u0442\u043e \u043f\u043e\u043c\u0435\u043d\u044f\u0442\u044c. \u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430, <code>routes\/api.php<\/code>: <\/p>\n<pre><code class=\"php\">&lt;?php ... Route::group(['middleware' =&gt; ['api','auth'],'prefix' =&gt; 'recipe'],function (){     Route::post('create','RecipeController@create')-&gt;name('recipe.create');     Route::get('all','RecipeController@all')-&gt;name('recipe.all');     Route::post('update\/{recipe}','RecipeController@update')-&gt;name('recipe.update');     Route::get('show\/{recipe}','RecipeController@show')-&gt;name('recipe.show');     Route::post('delete\/{recipe}','RecipeController@delete')-&gt;name('recipe.delete'); }); view rawapi.php hosted with  by GitHub<\/code><\/pre>\n<p>  \u0417\u0430\u0442\u0435\u043c \u043c\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043c\u0435\u0442\u043e\u0434\u044b \u043a \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0443. \u041f\u0435\u0440\u0435\u043f\u0438\u0448\u0438\u0442\u0435 \u043a\u043b\u0430\u0441\u0441 <code>RecipeController<\/code> \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c: <\/p>\n<pre><code class=\"php\">&lt;?php  .... \/\/Create recipe public function create(Request $request){     \/\/Validate     $this-&gt;validate($request,['title' =&gt; 'required','procedure' =&gt; 'required|min:8']);      \/\/Create recipe and attach to user     $user = Auth::user();     $recipe = Recipe::create($request-&gt;only(['title','procedure']));     $user-&gt;recipes()-&gt;save($recipe);      \/\/Return json of recipe     return $recipe-&gt;toJson(); }  \/\/Get all recipes public function all(){     return Auth::user()-&gt;recipes; } \/\/Update a recipe public function update(Request $request, Recipe $recipe){     \/\/Check is user is the owner of the recipe     if($recipe-&gt;publisher_id != Auth::id()){         abort(404);         return;     }     \/\/Update and return     $recipe-&gt;update($request-&gt;only('title','procedure'));     return $recipe-&gt;toJson(); } \/\/Show a single recipe's details public function show(Recipe $recipe){     if($recipe-&gt;publisher_id != Auth::id()){         abort(404);         return;     }     return $recipe-&gt;toJson(); } \/\/Delete a recipe public function delete(Recipe $recipe){     if($recipe-&gt;publisher_id != Auth::id()){         abort(404);         return;     }     $recipe-&gt;delete(); } view rawRecipeController.php hosted with  by GitHub<\/code><\/pre>\n<p>  \u041a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438 \u0432 \u043a\u043e\u0434\u0435 \u043f\u043e\u043c\u043e\u0433\u0443\u0442 \u0432\u0430\u043c \u043f\u043e\u043d\u044f\u0442\u044c \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u043d\u043e\u0435. <br \/>  \u041d\u0443 \u0438 \u043d\u0430\u043a\u043e\u043d\u0435\u0446 <code>test\/Feature\/RecipeTest<\/code>:<\/p>\n<pre><code class=\"php\">&lt;?php ...   use RefreshDatabase;  protected $user;  \/\/Create a user and authenticate him protected function authenticate(){     $user = User::create([         'name' =&gt; 'test',         'email' =&gt; 'test@gmail.com',         'password' =&gt; Hash::make('secret1234'),     ]);     $this-&gt;user = $user;     $token = JWTAuth::fromUser($user);     return $token; } \/\/Test the create route public function testCreate() {     \/\/Get token     $token = $this-&gt;authenticate();      $response = $this-&gt;withHeaders([         'Authorization' =&gt; 'Bearer '. $token,     ])-&gt;json('POST',route('recipe.create'),[         'title' =&gt; 'Jollof Rice',         'procedure' =&gt; 'Parboil rice, get pepper and mix, and some spice and serve!'     ]);     $response-&gt;assertStatus(200);     \/\/Get count and assert     $count = $this-&gt;user-&gt;recipes()-&gt;count();     $this-&gt;assertEquals(1,$count); } \/\/Test the display all routes public function testAll(){     \/\/Authenticate and attach recipe to user     $token = $this-&gt;authenticate();     $recipe = Recipe::create([         'title' =&gt; 'Jollof Rice',         'procedure' =&gt; 'Parboil rice, get pepper and mix, and some spice and serve!'     ]);     $this-&gt;user-&gt;recipes()-&gt;save($recipe);      \/\/call route and assert response     $response = $this-&gt;withHeaders([         'Authorization' =&gt; 'Bearer '. $token,     ])-&gt;json('GET',route('recipe.all'));     $response-&gt;assertStatus(200);      \/\/Assert the count is 1 and the title of the first item correlates     $this-&gt;assertEquals(1,count($response-&gt;json()));     $this-&gt;assertEquals('Jollof Rice',$response-&gt;json()[0]['title']); } \/\/Test the update route public function testUpdate(){     $token = $this-&gt;authenticate();     $recipe = Recipe::create([         'title' =&gt; 'Jollof Rice',         'procedure' =&gt; 'Parboil rice, get pepper and mix, and some spice and serve!'     ]);     $this-&gt;user-&gt;recipes()-&gt;save($recipe);      \/\/call route and assert response     $response = $this-&gt;withHeaders([         'Authorization' =&gt; 'Bearer '. $token,     ])-&gt;json('POST',route('recipe.update',['recipe' =&gt; $recipe-&gt;id]),[         'title' =&gt; 'Rice',     ]);     $response-&gt;assertStatus(200);      \/\/Assert title is the new title     $this-&gt;assertEquals('Rice',$this-&gt;user-&gt;recipes()-&gt;first()-&gt;title); } \/\/Test the single show route public function testShow(){     $token = $this-&gt;authenticate();     $recipe = Recipe::create([         'title' =&gt; 'Jollof Rice',         'procedure' =&gt; 'Parboil rice, get pepper and mix, and some spice and serve!'     ]);     $this-&gt;user-&gt;recipes()-&gt;save($recipe);     $response = $this-&gt;withHeaders([         'Authorization' =&gt; 'Bearer '. $token,     ])-&gt;json('GET',route('recipe.show',['recipe' =&gt; $recipe-&gt;id]));     $response-&gt;assertStatus(200);      \/\/Assert title is correct     $this-&gt;assertEquals('Jollof Rice',$response-&gt;json()['title']); } \/\/Test the delete route public function testDelete(){     $token = $this-&gt;authenticate();     $recipe = Recipe::create([         'title' =&gt; 'Jollof Rice',         'procedure' =&gt; 'Parboil rice, get pepper and mix, and some spice and serve!'     ]);     $this-&gt;user-&gt;recipes()-&gt;save($recipe);      $response = $this-&gt;withHeaders([         'Authorization' =&gt; 'Bearer '. $token,     ])-&gt;json('POST',route('recipe.delete',['recipe' =&gt; $recipe-&gt;id]));     $response-&gt;assertStatus(200);      \/\/Assert there are no recipes     $this-&gt;assertEquals(0,$this-&gt;user-&gt;recipes()-&gt;count()); } view rawRecipeTest.php hosted with  by GitHub<\/code><\/pre>\n<p>  \u041a\u0440\u043e\u043c\u0435 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0430, \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u043c \u043e\u0442\u043b\u0438\u0447\u0438\u0435\u043c \u0441\u0442\u0430\u043b\u043e \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0433\u043e \u0444\u0430\u0439\u043b\u0430 \u0434\u043b\u044f \u0432\u0441\u0435\u0433\u043e \u043a\u043b\u0430\u0441\u0441\u0430. \u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u043c\u0435\u0442\u043e\u0434 <code>authenticate<\/code> \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442 \u0442\u043e\u043a\u0435\u043d, \u043d\u043e \u0438 \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u0442 \u0444\u0430\u0439\u043b \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0434\u043b\u044f \u043f\u043e\u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0445 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439. <\/p>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 <code>$ vendor\/bin\/phpunit<\/code> \u0438 \u0432\u0441\u0435 \u0442\u0435\u0441\u0442\u044b \u0443 \u0432\u0430\u0441 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u0441\u044f \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0432 \u0441\u043b\u0443\u0447\u0430\u0435, \u0435\u0441\u043b\u0438, \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u0436\u0435, \u0432\u044b \u0432\u0441\u0435 \u0441\u0434\u0435\u043b\u0430\u043b\u0438 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e.<\/p>\n<h3>\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/h3>\n<p>  \u041d\u0430\u0434\u0435\u044e\u0441\u044c, \u044d\u0442\u0430 \u0441\u0442\u0430\u0442\u044c\u044f \u0434\u0430\u043b\u0430 \u0432\u0430\u043c \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043e \u0442\u043e\u043c, \u043a\u0430\u043a TDD \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0432 Laravel. \u041a\u043e\u043d\u0435\u0447\u043d\u043e \u0436\u0435, \u044d\u0442\u043e \u0433\u043e\u0440\u0430\u0437\u0434\u043e \u0431\u043e\u043b\u0435 \u0448\u0438\u0440\u043e\u043a\u043e\u0435 \u043f\u043e\u043d\u044f\u0442\u0438\u0435, \u0447\u0435\u043c \u043f\u0440\u0438\u0432\u044f\u0437\u043a\u0430 \u043a \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u043c\u0443 \u043c\u0435\u0442\u043e\u0434\u0443.<\/p>\n<p>  \u041d\u0435\u0441\u043c\u043e\u0442\u0440\u044f \u043d\u0430 \u0442\u043e, \u0447\u0442\u043e \u0442\u0430\u043a\u043e\u0439 \u043c\u0435\u0442\u043e\u0434 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c\u0441\u044f \u0431\u043e\u043b\u0435\u0435 \u0434\u043e\u043b\u0433\u0438\u043c, \u0447\u0435\u043c \u043e\u0431\u044b\u0447\u043d\u0430\u044f \u043f\u0440\u043e\u0446\u0435\u0434\u0443\u0440\u0430 \u043e\u0442\u043b\u0430\u0434\u043a\u0438 \u043f\u043e\u0441\u043b\u0435 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u043a\u043e\u0434\u0430, \u043e\u043d \u0438\u0434\u0435\u0430\u043b\u044c\u043d\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442 \u0434\u043b\u044f \u0440\u0430\u043d\u043d\u0435\u0433\u043e \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0431\u043b\u0435\u043c. \u041a\u043e\u043d\u0435\u0447\u043d\u043e, \u0435\u0441\u0442\u044c \u0441\u043b\u0443\u0447\u0430\u0438, \u043a\u043e\u0433\u0434\u0430 \u0441\u0442\u043e\u0438\u0442 \u043e\u0431\u043e\u0439\u0442\u0438\u0441\u044c \u0431\u0435\u0437 \u043f\u043e\u0434\u0445\u043e\u0434\u0430 \u043a \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u0447\u0435\u0440\u0435\u0437 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435, \u043e\u0434\u043d\u0430\u043a\u043e \u0432 \u043b\u044e\u0431\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435, \u043a \u043d\u0435\u043c\u0443 \u0441\u0442\u043e\u0438\u0442 \u043f\u0440\u0438\u0432\u044b\u043a\u043d\u0443\u0442\u044c \u0438 \u0440\u0430\u0437\u0432\u0438\u0442\u044c \u0432 \u0441\u0435\u0431\u0435 \u043d\u0430\u0432\u044b\u043a \u0435\u0433\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f.<\/p>\n<p>  \u0412\u0435\u0441\u044c \u043a\u043e\u0434 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438 \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043d\u0430\u0439\u0442\u0438 \u043d\u0430 <a href=\"https:\/\/github.com\/kofoworola\/tdd-journey\">GitHub<\/a>. \u041d\u0435 \u0441\u0442\u0435\u0441\u043d\u044f\u0439\u0442\u0435\u0441\u044c \u0438\u0433\u0440\u0430\u0442\u044c\u0441\u044f \u0441 \u043d\u0438\u043c. \u0423\u0434\u0430\u0447\u0438!<\/p>\n<p>  <i>\u041d\u0430 \u044d\u0442\u043e\u043c \u043f\u0435\u0440\u0435\u0432\u043e\u0434 \u043f\u043e\u0434\u043e\u0448\u0435\u043b \u043a \u043a\u043e\u043d\u0446\u0443, \u0430 \u043c\u044b \u043f\u0440\u0438\u0433\u043b\u0430\u0448\u0430\u0435\u043c \u0432\u0430\u0441 \u043d\u0430 <a href=\"https:\/\/otus.pw\/S7GG\/\">\u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u044b\u0439 \u0432\u0435\u0431\u0438\u043d\u0430\u0440<\/a>, \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0435\u043c \u043a\u0430\u043a <b>\u0441\u043e\u0437\u0434\u0430\u0442\u044c Telegram-\u0431\u043e\u0442\u0430 \u0434\u043b\u044f \u0437\u0430\u043a\u0430\u0437\u0430 \u043a\u043e\u0444\u0435 \u0432 \u0437\u0430\u0432\u0435\u0434\u0435\u043d\u0438\u0438 \u0438 \u043e\u043f\u043b\u0430\u0442\u044b \u043e\u043d\u043b\u0430\u0439\u043d<\/b>.<\/i><\/div>\n<p> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/company\/otus\/blog\/485066\/\"> https:\/\/habr.com\/ru\/company\/otus\/blog\/485066\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"\n<div class=\"post__text post__text-html\" id=\"post-content-body\" data-io-article-url=\"https:\/\/habr.com\/ru\/company\/otus\/blog\/485066\/\"><i>\u0421\u0430\u043b\u044e\u0442, \u0445\u0430\u0431\u0440\u043e\u0432\u0447\u0430\u043d\u0435! \u041c\u044b \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0435\u043c \u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u043c \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u043e\u043c \u0432 \u043f\u0440\u0435\u0434\u0434\u0432\u0435\u0440\u0438\u0438 \u0441\u0442\u0430\u0440\u0442\u0430 \u043a\u0443\u0440\u0441\u0430 <a href=\"https:\/\/otus.pw\/S7GG\/\">\u00abFramework Laravel\u00bb<\/a>. \u041f\u043e\u0435\u0445\u0430\u043b\u0438.<\/i>  <\/p>\n<hr>\n<p>  \u041a\u0430\u043a \u043e\u0434\u043d\u0430\u0436\u0434\u044b \u0441\u043a\u0430\u0437\u0430\u043b <a href=\"https:\/\/wingman-sw.com\/about\">\u0414\u0436\u0435\u0439\u043c\u0441 \u0413\u0440\u0435\u043d\u0438\u043d\u0433<\/a>, \u043e\u0434\u0438\u043d \u0438\u0437 \u043f\u0438\u043e\u043d\u0435\u0440\u043e\u0432 TDD \u0438 \u043c\u0435\u0442\u043e\u0434\u043e\u043b\u043e\u0433\u0438\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 Agile:  <\/p>\n<blockquote><p>\u00ab\u0415\u0441\u043b\u0438 \u0432\u044b \u043d\u0435 \u0437\u0430\u043d\u0438\u043c\u0430\u0435\u0442\u0435\u0441\u044c \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u043e\u0439 \u0447\u0435\u0440\u0435\u0437 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435, \u0442\u043e \u043f\u043e\u0437\u0436\u0435 \u0432\u044b \u0437\u0430\u0439\u043c\u0435\u0442\u0435\u0441\u044c \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u043e\u0439 \u0447\u0435\u0440\u0435\u0437 \u043e\u0442\u043b\u0430\u0434\u043a\u0443\u00bb<\/p>\n<p>  \u2013 \u0414\u0436\u0435\u0439\u043c\u0441 \u0413\u0440\u0435\u043d\u0438\u043d\u0433<\/p><\/blockquote>\n<p>  \u0421\u0435\u0433\u043e\u0434\u043d\u044f \u043c\u044b \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u043c\u0441\u044f \u0432 \u043f\u0443\u0442\u0435\u0448\u0435\u0441\u0442\u0432\u0438\u0435 \u043f\u043e \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u0447\u0435\u0440\u0435\u0437 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0441 Laravel. \u041c\u044b \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c REST API \u043d\u0430 Laravel \u0441 \u043f\u043e\u043b\u043d\u044b\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u043e\u043c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0438 CRUD \u043d\u0435 \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u044f Postman \u0438\u043b\u0438 \u0431\u0440\u0430\u0443\u0437\u0435\u0440.<\/p>\n<p>  <b><i>\u041f\u0440\u0438\u043c\u0435\u0447\u0430\u043d\u0438\u0435:<\/i> \u0421\u0435\u0433\u043e\u0434\u043d\u044f\u0448\u043d\u0435\u0435 \u043f\u043e\u0448\u0430\u0433\u043e\u0432\u043e\u0435 \u0440\u0443\u043a\u043e\u0432\u043e\u0434\u0441\u0442\u0432\u043e \u043f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u0435\u0442, \u0447\u0442\u043e \u0432\u044b \u0437\u043d\u0430\u043a\u043e\u043c\u044b \u0441 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u043c\u0438 \u043f\u043e\u043d\u044f\u0442\u0438\u044f\u043c\u0438 \u0432 <a href=\"https:\/\/laravel.com\/docs\/5.7\">Laravel<\/a> \u0438 <a href=\"https:\/\/phpunit.de\/documentation.html\">PHPUnit<\/a>. \u0415\u0441\u043b\u0438 \u0432\u0441\u0435 \u0432 \u043f\u043e\u0440\u044f\u0434\u043a\u0435, \u0442\u043e\u0433\u0434\u0430 \u043f\u043e\u0435\u0445\u0430\u043b\u0438!<\/b><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-297528","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/297528","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=297528"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/297528\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=297528"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=297528"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=297528"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}