成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

使用 TDD 測(cè)試驅(qū)動(dòng)開(kāi)發(fā)來(lái)構(gòu)建 Laravel REST API

AlphaWallet / 704人閱讀

摘要:以及敏捷開(kāi)發(fā)的先驅(qū)者之一的有句名言如果你沒(méi)有進(jìn)行測(cè)試驅(qū)動(dòng)開(kāi)發(fā),那么你應(yīng)該正在做開(kāi)發(fā)后堵漏的事今天我們將進(jìn)行一場(chǎng)基于的測(cè)試驅(qū)動(dòng)開(kāi)發(fā)之旅。使用生成測(cè)試類。現(xiàn)在使用命令來(lái)生成模型并將其添加到我們的模型中。

TDD 以及敏捷開(kāi)發(fā)的先驅(qū)者之一的?James Grenning有句名言:

如果你沒(méi)有進(jìn)行測(cè)試驅(qū)動(dòng)開(kāi)發(fā),那么你應(yīng)該正在做開(kāi)發(fā)后堵漏的事 -  James?Grenning

今天我們將進(jìn)行一場(chǎng)基于 Laravel 的測(cè)試驅(qū)動(dòng)開(kāi)發(fā)之旅。 我們將創(chuàng)建一個(gè)完整的 Laravel REST API,其中包含身份驗(yàn)證和 CRUD 功能,而無(wú)需打開(kāi) Postman 或?yàn)g覽器。?

注意:本旅程假定你已經(jīng)理解了?Laravel?和?PHPUnit 的基本概念。你是否已經(jīng)明晰了這個(gè)問(wèn)題?那就開(kāi)始吧。
項(xiàng)目設(shè)置

首先創(chuàng)建一個(gè)新的 Laravel 項(xiàng)目?composer create-project --prefer-dist laravel/laravel tdd-journey。

然后,我們需要?jiǎng)?chuàng)建 用戶認(rèn)證 腳手架,執(zhí)行 ?php artisan make:auth?,設(shè)置好 .env 文件中的數(shù)據(jù)庫(kù)配置后,執(zhí)行數(shù)據(jù)庫(kù)遷移?php artisan migrate

本測(cè)試項(xiàng)目并不會(huì)使用到剛生成的用戶認(rèn)證相關(guān)的路由和視圖。我們將使用?jwt-auth。所以需要繼續(xù)?安裝?jwt 到項(xiàng)目。

注意:如果您在執(zhí)行?jwt:generate?指令時(shí)遇到錯(cuò)誤, 您可以參考?這里解決這個(gè)問(wèn)題,直到 jwt 被正確安裝到項(xiàng)目中。

最后,您需要在 tests/Unittests/Feature 目錄中刪除 ExampleTest.php 文件,使我們的測(cè)試結(jié)果不被影響。

編碼

首先將?JWT 驅(qū)動(dòng)配置為 auth?配置項(xiàng)的默認(rèn)值:

 [
    "guard" => "api",
    "passwords" => "users",
],
"guards" => [
    ...
    "api" => [
        "driver" => "jwt",
        "provider" => "users",
    ],
],

然后將如下內(nèi)容放到你的 routes/api.php?文件里:

 "api", "prefix" => "auth"], function () {
    Route::post("authenticate", "AuthController@authenticate")->name("api.authenticate");
    Route::post("register", "AuthController@register")->name("api.register");
});

現(xiàn)在我們已經(jīng)將驅(qū)動(dòng)設(shè)置完成了,如法炮制,去設(shè)置你的用戶模型:

getKey();
    }
    // 返回一個(gè)鍵值對(duì)數(shù)組,包含要添加到 JWT 的任何自定義 claim
    public function getJWTCustomClaims()
    {
        return [];
    }
}

我們所需要做的就是實(shí)現(xiàn)?JWTSubject 接口然后添加相應(yīng)的方法即可。

接下來(lái),我們需要增加權(quán)限認(rèn)證方法到控制器中.

運(yùn)行 php artisan make:controller AuthController 并且添加以下方法:

validate($request,["email" => "required|email","password"=> "required"]);
        // 測(cè)試驗(yàn)證
        $credentials = $request->only(["email","password"]);
        if (! $token = auth()->attempt($credentials)) {
            return response()->json(["error" => "Incorrect credentials"], 401);
        }
        return response()->json(compact("token"));
    }
    public function register(Request $request){
        // 表達(dá)驗(yàn)證
        $this->validate($request,[
            "email" => "required|email|max:255|unique:users",
            "name" => "required|max:255",
            "password" => "required|min:8|confirmed",
        ]);
        // 創(chuàng)建用戶并生成 Token
        $user =  User::create([
            "name" => $request->input("name"),
            "email" => $request->input("email"),
            "password" => Hash::make($request->input("password")),
        ]);
        $token = JWTAuth::fromUser($user);
        return response()->json(compact("token"));
    }
}

這一步非常直接,我們要做的就是添加 authenticateregister 方法到我們的控制器中。在 authenticate 方法,我們驗(yàn)證了輸入,嘗試去登錄,如果成功就返回令牌。在 register 方法,我們驗(yàn)證輸入,然后基于此創(chuàng)建一個(gè)用戶并且生成令牌。

4. 接下來(lái),我們進(jìn)入相對(duì)簡(jiǎn)單的部分。 測(cè)試我們剛寫入的內(nèi)容。 使用 php artisan make:test AuthTest 生成測(cè)試類。 在新的 tests / Feature / AuthTest 中添加以下方法:

 "[email protected]",
        "name" => "Test",
        "password" => "secret1234",
        "password_confirmation" => "secret1234",
    ];
    //發(fā)送 post 請(qǐng)求
    $response = $this->json("POST",route("api.register"),$data);
    //判斷是否發(fā)送成功
    $response->assertStatus(200);
    //接收我們得到的 token
    $this->assertArrayHasKey("token",$response->json());
    //刪除數(shù)據(jù)
    User::where("email","[email protected]")->delete();
}
/**
 * @test
 * Test login
 */
public function testLogin()
{
    //創(chuàng)建用戶
    User::create([
        "name" => "test",
        "email"=>"[email protected]",
        "password" => bcrypt("secret1234")
    ]);
    //模擬登陸
    $response = $this->json("POST",route("api.authenticate"),[
        "email" => "[email protected]",
        "password" => "secret1234",
    ]);
    //判斷是否登錄成功并且收到了 token 
    $response->assertStatus(200);
    $this->assertArrayHasKey("token",$response->json());
    //刪除用戶
    User::where("email","[email protected]")->delete();
}

上面代碼中的幾行注釋概括了代碼的大概作用。 您應(yīng)該注意的一件事是我們?nèi)绾卧诿總€(gè)測(cè)試中創(chuàng)建和刪除用戶。 測(cè)試的全部要點(diǎn)是它們應(yīng)該彼此獨(dú)立并且應(yīng)該在你的理想情況下存在數(shù)據(jù)庫(kù)中的狀態(tài)。

如果你想全局安裝它,可以運(yùn)行 $ vendor / bin / phpunit$ phpunit 命令。 運(yùn)行后它應(yīng)該會(huì)給你返回是否安裝成功的數(shù)據(jù)。 如果不是這種情況,您可以瀏覽日志,修復(fù)并重新測(cè)試。 這就是 TDD 的美麗之處。

5. 對(duì)于本教程,我們將使用『菜譜 Recipes』作為我們的 CRUD 數(shù)據(jù)模型。

首先創(chuàng)建我們的遷移數(shù)據(jù)表 php artisan make:migration create_recipes_table 并添加以下內(nèi)容:

increments("id");
        $table->string("title");
        $table->text("procedure")->nullable();
        $table->tinyInteger("publisher_id")->nullable();
        $table->timestamps();
    });
}
public function down()
{
    Schema::dropIfExists("recipes");
}

然后運(yùn)行數(shù)據(jù)遷移。 現(xiàn)在使用命令 php artisan make:model Recipe 來(lái)生成模型并將其添加到我們的模型中。

belongsTo(User::class);
}

然后將此方法添加到 user 模型。

hasMany(Recipe::class);
}

6. 現(xiàn)在我們需要最后一部分設(shè)置來(lái)完成我們的食譜管理。 首先,我們將創(chuàng)建控制器 php artisan make:controller RecipeController 。 接下來(lái),編輯 routes / api.php 文件并添加 create 路由端點(diǎn)。

 ["api","auth"],"prefix" => "recipe"],function (){
    Route::post("create","RecipeController@create")->name("recipe.create");
});

在控制器中,還要添加 create 方法

validate($request,["title" => "required","procedure" => "required|min:8"]);
    //創(chuàng)建配方并附加到用戶
    $user = Auth::user();
    $recipe = Recipe::create($request->only(["title","procedure"]));
    $user->recipes()->save($recipe);
    //返回 json 格式的食譜數(shù)據(jù)
    return $recipe->toJson();
}

使用 php artisan make:test RecipeTest 生成特征測(cè)試并編輯內(nèi)容,如下所示:

 "test",
            "email" => "[email protected]",
            "password" => Hash::make("secret1234"),
        ]);
        $token = JWTAuth::fromUser($user);
        return $token;
    }
  
    public function testCreate()
    {
        //獲取 token
        $token = $this->authenticate();
        $response = $this->withHeaders([
            "Authorization" => "Bearer ". $token,
        ])->json("POST",route("recipe.create"),[
            "title" => "Jollof Rice",
            "procedure" => "Parboil rice, get pepper and mix, and some spice and serve!"
        ]);
        $response->assertStatus(200);
    }
}

上面的代碼你可能還是不太理解。我們所做的就是創(chuàng)建一個(gè)用于處理用戶注冊(cè)和 token 生成的方法,然后在 testCreate() 方法中使用該 token 。注意使用 RefreshDatabase trait ,這個(gè) trait 是 Laravel 在每次測(cè)試后重置數(shù)據(jù)庫(kù)的便捷方式,非常適合我們漂亮的小項(xiàng)目。

好的,所以現(xiàn)在,我們只要判斷當(dāng)前請(qǐng)求是否是響應(yīng)狀態(tài),然后繼續(xù)運(yùn)行 $ vendor/bin/phpunit 。

如果一切運(yùn)行順利,您應(yīng)該收到錯(cuò)誤。 ?

There was 1 failure:

1) TestsFeatureRecipeTest::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.

查看日志文件,我們可以看到罪魁禍?zhǔn)资?RecipeUser 類中的 publisherrecipes 的關(guān)系。 Laravel 嘗試在表中找到一個(gè)字段為 user_id 的列并將其用作于外鍵,但在我們的遷移中,我們將publisher_id 設(shè)置為外鍵。 現(xiàn)在,將行調(diào)整為:

//食譜文件
public function publisher(){
    return $this->belongsTo(User::class,"publisher_id");
}
//用戶文件
public function recipes(){
    return $this->hasMany(Recipe::class,"publisher_id");
}

然后重新運(yùn)行測(cè)試。 如果一切順利,我們將獲得所有綠色測(cè)試!?

...                                                                 
3 / 3 (100%)
...
OK (3 tests, 5 assertions)

現(xiàn)在我們?nèi)匀恍枰獪y(cè)試創(chuàng)建配方的方法。為此,我們可以判斷用戶的『菜譜 Recipes』計(jì)數(shù)。更新你的 testCreate 方法,如下所示:

authenticate();
$response = $this->withHeaders([
    "Authorization" => "Bearer ". $token,
])->json("POST",route("recipe.create"),[
    "title" => "Jollof Rice",
    "procedure" => "Parboil rice, get pepper and mix, and some spice and serve!"
]);
$response->assertStatus(200);
//得到計(jì)數(shù)做出判斷
$count = User::where("email","[email protected]")->first()->recipes()->count();
$this->assertEquals(1,$count);

我們現(xiàn)在可以繼續(xù)編寫其余的方法。首先,編寫我們的 routes/api.php

 ["api","auth"],"prefix" => "recipe"],function (){
    Route::post("create","RecipeController@create")->name("recipe.create");
    Route::get("all","RecipeController@all")->name("recipe.all");
    Route::post("update/{recipe}","RecipeController@update")->name("recipe.update");
    Route::get("show/{recipe}","RecipeController@show")->name("recipe.show");
    Route::post("delete/{recipe}","RecipeController@delete")->name("recipe.delete");
});

接下來(lái),我們將方法添加到控制器。 以下面這種方式更新 RecipeController 類。

validate($request,["title" => "required","procedure" => "required|min:8"]);
    //創(chuàng)建配方并附加到用戶
    $user = Auth::user();
    $recipe = Recipe::create($request->only(["title","procedure"]));
    $user->recipes()->save($recipe);
    //返回配方的 json 格式數(shù)據(jù)
    return $recipe->toJson();
}
//獲取所有的配方
public function all(){
    return Auth::user()->recipes;
}
//更新配方
public function update(Request $request, Recipe $recipe){
    //檢查用戶是否是配方的所有者
    if($recipe->publisher_id != Auth::id()){
        abort(404);
        return;
    }
    //更新并返回
    $recipe->update($request->only("title","procedure"));
    return $recipe->toJson();
}
//顯示單個(gè)食譜的詳細(xì)信息
public function show(Recipe $recipe){
    if($recipe->publisher_id != Auth::id()){
        abort(404);
        return;
    }
    return $recipe->toJson();
}
//刪除一個(gè)配方
public function delete(Recipe $recipe){
    if($recipe->publisher_id != Auth::id()){
        abort(404);
        return;
    }
    $recipe->delete();
}

代碼和注釋已經(jīng)很好地解釋了這個(gè)邏輯。

最后我們的?test/Feature/RecipeTest:

 "test",
        "email" => "[email protected]",
        "password" => Hash::make("secret1234"),
    ]);
    $this->user = $user;
    $token = JWTAuth::fromUser($user);
    return $token;
}
// 測(cè)試創(chuàng)建路由
public function testCreate()
{
    // 獲取令牌
    $token = $this->authenticate();
    $response = $this->withHeaders([
        "Authorization" => "Bearer ". $token,
    ])->json("POST",route("recipe.create"),[
        "title" => "Jollof Rice",
        "procedure" => "Parboil rice, get pepper and mix, and some spice and serve!"
    ]);
    $response->assertStatus(200);
    // 獲取計(jì)數(shù)并斷言
    $count = $this->user->recipes()->count();
    $this->assertEquals(1,$count);
}
// 測(cè)試顯示所有路由
public function testAll(){
    // 驗(yàn)證并將配方附加到用戶
    $token = $this->authenticate();
    $recipe = Recipe::create([
        "title" => "Jollof Rice",
        "procedure" => "Parboil rice, get pepper and mix, and some spice and serve!"
    ]);
    $this->user->recipes()->save($recipe);
    // 調(diào)用路由并斷言響應(yīng)
    $response = $this->withHeaders([
        "Authorization" => "Bearer ". $token,
    ])->json("GET",route("recipe.all"));
    $response->assertStatus(200);
    // 斷言計(jì)數(shù)為1,第一項(xiàng)的標(biāo)題相關(guān)
    $this->assertEquals(1,count($response->json()));
    $this->assertEquals("Jollof Rice",$response->json()[0]["title"]);
}
// 測(cè)試更新路由
public function testUpdate(){
    $token = $this->authenticate();
    $recipe = Recipe::create([
        "title" => "Jollof Rice",
        "procedure" => "Parboil rice, get pepper and mix, and some spice and serve!"
    ]);
    $this->user->recipes()->save($recipe);
    // 調(diào)用路由并斷言響應(yīng)
    $response = $this->withHeaders([
        "Authorization" => "Bearer ". $token,
    ])->json("POST",route("recipe.update",["recipe" => $recipe->id]),[
        "title" => "Rice",
    ]);
    $response->assertStatus(200);
    // 斷言標(biāo)題是新標(biāo)題
    $this->assertEquals("Rice",$this->user->recipes()->first()->title);
}
// 測(cè)試單一的展示路由
public function testShow(){
    $token = $this->authenticate();
    $recipe = Recipe::create([
        "title" => "Jollof Rice",
        "procedure" => "Parboil rice, get pepper and mix, and some spice and serve!"
    ]);
    $this->user->recipes()->save($recipe);
    $response = $this->withHeaders([
        "Authorization" => "Bearer ". $token,
    ])->json("GET",route("recipe.show",["recipe" => $recipe->id]));
    $response->assertStatus(200);
    // 斷言標(biāo)題是正確的
    $this->assertEquals("Jollof Rice",$response->json()["title"]);
}
// 測(cè)試刪除路由
public function testDelete(){
    $token = $this->authenticate();
    $recipe = Recipe::create([
        "title" => "Jollof Rice",
        "procedure" => "Parboil rice, get pepper and mix, and some spice and serve!"
    ]);
    $this->user->recipes()->save($recipe);
    $response = $this->withHeaders([
        "Authorization" => "Bearer ". $token,
    ])->json("POST",route("recipe.delete",["recipe" => $recipe->id]));
    $response->assertStatus(200);
    // 斷言沒(méi)有食譜
    $this->assertEquals(0,$this->user->recipes()->count());
}

除了附加測(cè)試之外,我們還添加了一個(gè)類范圍的 $user 屬性。 這樣,我們不止可以利用 $user 來(lái)使用?authenticate?方法不僅生成令牌,而且還為后續(xù)其他對(duì) $user 的操作做好了準(zhǔn)備。

現(xiàn)在運(yùn)行?$ vendor/bin/phpunit?如果操作正確,你應(yīng)該進(jìn)行所有綠色測(cè)試。

結(jié)論

希望這能讓你深度了解在 TDD 在 Laravel 項(xiàng)目中的運(yùn)行方式。 他絕對(duì)是一個(gè)比這更寬泛的概念,一個(gè)不受特地方法約束的概念。

雖然這種開(kāi)發(fā)方法看起來(lái)比常見(jiàn)的調(diào)試后期程序要耗時(shí), 但他很適合在代碼中盡早捕獲錯(cuò)誤。雖然有些情況下非 TDD 方式會(huì)更有用,但習(xí)慣于 TDD 模式開(kāi)發(fā)是一種可靠的技能和習(xí)慣。

本演練的全部代碼可參見(jiàn) Github?here 倉(cāng)庫(kù)。請(qǐng)隨意使用。

干杯!

文章轉(zhuǎn)自:https://learnku.com/laravel/t... 

更多文章:https://learnku.com/laravel/c...

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/30965.html

相關(guān)文章

  • 使用 TDD 測(cè)試驅(qū)動(dòng)開(kāi)發(fā)來(lái)構(gòu)建 Laravel REST API

    摘要:以及敏捷開(kāi)發(fā)的先驅(qū)者之一的有句名言如果你沒(méi)有進(jìn)行測(cè)試驅(qū)動(dòng)開(kāi)發(fā),那么你應(yīng)該正在做開(kāi)發(fā)后堵漏的事今天我們將進(jìn)行一場(chǎng)基于的測(cè)試驅(qū)動(dòng)開(kāi)發(fā)之旅。使用生成測(cè)試類?,F(xiàn)在使用命令來(lái)生成模型并將其添加到我們的模型中。 showImg(https://segmentfault.com/img/remote/1460000018404936?w=1440&h=900); TDD 以及敏捷開(kāi)發(fā)的先驅(qū)者之一的?...

    dackel 評(píng)論0 收藏0
  • 快速部署TEST-DRIVEN DEVELOPMENT/DEBUG環(huán)境

    摘要:關(guān)注的目標(biāo)就是在代碼提交之后,順利且迅速的把新的功能部署到產(chǎn)品環(huán)境上。由于是,那么單元測(cè)試,回歸測(cè)試,集成測(cè)試,都是實(shí)現(xiàn)的手段。高質(zhì)量的產(chǎn)品需求書和高質(zhì)量的自動(dòng)化集成測(cè)試用例毫無(wú)疑問(wèn),是高質(zhì)量軟件的保證之一。 showImg(https://segmentfault.com/img/remote/1460000006877091?w=800&h=600); 什么是Test-Driven...

    SHERlocked93 評(píng)論0 收藏0
  • 《Python Web開(kāi)發(fā)》作者Harry Percival:TDD就是微小而漸進(jìn)的改變

    摘要:目前就職于,他在各種演講研討會(huì)和開(kāi)發(fā)者大會(huì)上積極推廣測(cè)試驅(qū)動(dòng)開(kāi)發(fā)。問(wèn)很多敏捷教練都表示訓(xùn)練新人做測(cè)試驅(qū)動(dòng)開(kāi)發(fā)是一件辛苦而進(jìn)度緩慢的事,并且收益也不是很大。首先是開(kāi)發(fā)的對(duì)話式風(fēng)格。第一個(gè)問(wèn)題就是測(cè)試套件的速度。 Harry J.W. Percival目前就職于PythonAnywhere,他在各種演講、研討會(huì)和開(kāi)發(fā)者大會(huì)上積極推廣測(cè)試驅(qū)動(dòng)開(kāi)發(fā)(TDD)。他在利物浦大學(xué)獲得計(jì)算機(jī)科學(xué)碩士學(xué)...

    Guakin_Huang 評(píng)論0 收藏0
  • 《Python Web開(kāi)發(fā)》作者Harry Percival:TDD就是微小而漸進(jìn)的改變

    摘要:目前就職于,他在各種演講研討會(huì)和開(kāi)發(fā)者大會(huì)上積極推廣測(cè)試驅(qū)動(dòng)開(kāi)發(fā)。問(wèn)很多敏捷教練都表示訓(xùn)練新人做測(cè)試驅(qū)動(dòng)開(kāi)發(fā)是一件辛苦而進(jìn)度緩慢的事,并且收益也不是很大。首先是開(kāi)發(fā)的對(duì)話式風(fēng)格。第一個(gè)問(wèn)題就是測(cè)試套件的速度。 Harry J.W. Percival目前就職于PythonAnywhere,他在各種演講、研討會(huì)和開(kāi)發(fā)者大會(huì)上積極推廣測(cè)試驅(qū)動(dòng)開(kāi)發(fā)(TDD)。他在利物浦大學(xué)獲得計(jì)算機(jī)科學(xué)碩士學(xué)...

    k00baa 評(píng)論0 收藏0
  • PHP / Laravel API 開(kāi)發(fā)推薦閱讀清單

    showImg(https://segmentfault.com/img/bV6aHV?w=1280&h=800); 社區(qū)優(yōu)秀文章 Laravel 5.5+passport 放棄 dingo 開(kāi)發(fā) API 實(shí)戰(zhàn),讓 API 開(kāi)發(fā)更省心 - 自造車輪。 API 文檔神器 Swagger 介紹及在 PHP 項(xiàng)目中使用 - API 文檔撰寫方案 推薦 Laravel API 項(xiàng)目必須使用的 8 個(gè)...

    shmily 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<