Laravel 測試技術

liuqing_hu發表於2018-04-28

本文首發於個人部落格 掌握 Laravel 的測試方法,轉載請註明出處。

不管你承認與否在研發一款產品時,軟體測試對專案而言意義重大,但現實是測試通常被我們視而不見。這篇文章我們主要研究 Laravel 框架的測試方法。

或許你還不知道,Laravel 核心早已繼承了 PHPUnit 單元測試元件。PHPUnit 是 PHP 社群裡使用最廣泛、最受歡迎的測試框架之一。PHPUnit 同時支援「單元測試」和「功能測試」兩種特性。

我們會簡單介紹 PHPUnit 「單元測試」和「功能測試」的基本使用方法。繼而,講解如何在 Laravel 專案中建立「單元測試」和「功能測試」用例。不過本篇我們假定你已經對 PHPUnit 測試框架有了基本的瞭解,所以讓我們把焦點放到 Laravel 中使用 PHPUnit 進行測試這個主題中。

單元測試和功能測試

如果您已經接觸過 PHPUnit 框架,那麼您應該知道,它支援兩種型別特性 -- 「單元測試」和「功能測試」。

「單元測試」的目的是用於測試函式或方法的正確性。更重要的是,我們可以輕鬆實現程式碼邏輯的正確性。

如果您在開發過程中發現某個功能包含多個邏輯處理,那麼最好將每個處理邏輯拆分到不同的方法裡,這樣以確保單個方法和程式碼塊可測試。

我們以一個理想的方法來窺探單元測試的奧祕。

<?php

public function getNameAttribute($value)
{
    return ucfirst($value);
}

如你所見,這個方法僅處理一個業務邏輯,方法內部通過 ucfirst 函式將字元轉換成首字母大寫格式。

單元測試是為了保證每個獨立單元的程式碼正確性;功能測試則是為了保證一個功能的正確性。一言以蔽之,就是通過特定的測試用例模擬使用者訪問應用的行為驗證系統的正確性。

例如,我們可以為包含如下步驟的登入功能實現一個功能測試用例:

  • 發起一個訪問登入頁面的 GET 請求;
  • 判斷我們是否處在登入頁面;
  • 生成用於採用 POST 請求方式登入的登入資料;
  • 判斷是否建立登入會話資料成功。

這就是應該如何建立「功能測試」用例的祕密。接下來我們將建立具體的測試用例,來講解如何在 Laravel 中使用「單元測試」和「功能測試」。

搭建測試環境

建立測試模型

在開始建立測試用例前,我們需要先構建起用於測試的專案依賴。

先執行下面的 artisan 命令建立一個 Post 模型及其對應的遷移檔案。

$ php artisan make:model Post --migration

上面的命令將為我們建立一個 Post 模型類和資料庫遷移檔案。

Post 模型程式碼如下:

<?php
// app/Post.php
namespace App;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    //
}

資料庫遷移檔案 YYYY_MM_DD_HHMMSS_create_posts_table.php 將建立在 database/migrations 目錄中。

Post 資料表會儲存一篇文章的標題。修改後 Post 資料庫遷移檔案程式碼如下:

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreatePostsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('posts');
    }
}

如你所見,我們通過新增的 $table->string('name') 來儲存文章的標題。接下來,執行資料庫遷移命令就回在資料庫中建立對應的資料表了。

$ php artisan migrate

在建立完資料表之後,我們需要向 Post 模型類中加入如下程式碼

<?php
namespace App;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    /**
     * Get the post title.
     *
     * @param  string  $value
     * @return string
     */
    public function getNameAttribute($value)
    {
        return ucfirst($value);
    }
}

我們剛剛新增了 accessor 方法,它的功能是修改文章的標題,這正是我們在單元測試用例中要測試的。以上就是 Post 模型所需要修改的內容。

建立測試控制器

接下來,我們需要建立一個檔名為 app/Http/Controllers/AccessorController.php 的控制器,它將被用於後續功能測試。

<?php

// app/Http/Controllers/AccessorController.php
namespace App\Http\Controllers;

use App\Post;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class AccessorController extends Controller
{
    public function index(Request $request)
    {
        // get the post-id from request params
        $post_id = $request->get("id", 0);

        // load the requested post
        $post = Post::find($post_id);

        // check the name property
        return $post->name;
    }
}

index 方法中,我們通過請求中的 id 引數,從 Post 模型中查詢一篇文章。

最後,在 routes/web.php 路由配置檔案裡定義相關路由。

<?php

Route::get('accessor/index', 'AccessorController@index');

設定完路由後就可以通過 http://your-laravel-site.com/accessor/index 訪問該路由是否能夠正常訪問了。

單元測試

上一節我們搭建了用於測試的環境。本節我們會在 Laravel 中編寫單元測試用例對 Post 模型進行測試。

幸運的是,Laravel 同樣為我們提供了建立測試用例模版檔案的命令工具。

通過在命令列裡執行下面的命令來建立 AccessorTest 單元測試用例類。注意我們需要通過 --unit 引數選項來表明這個命令建立一個單元測試用例。單元測試用例檔案被建立在 tests/Unit 目錄內。

$ php artisan make:test AccessorTest --unit

執行上面建立測試用例命令會建立檔名為 tests/Unit/AccessorTest.php 檔案。

<?php
// tests/Unit/AccessorTest.php
namespace Tests\Unit;

use Tests\TestCase;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;

class AccessorTest extends TestCase
{
    /**
     * A basic test example.
     *
     * @return void
     */
    public function testExample()
    {
        $this->assertTrue(true);
    }
}

testExample 方法替換成更有實際意義的方法:

<?php
// tests/Unit/AccessorTest.php
namespace Tests\Unit;

use Tests\TestCase;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;

class AccessorTest extends TestCase
{
    /**
     * Test accessor method
     *
     * @return void
     */  
    public function testAccessorTest()
    {
        $db_post = DB::select('select * from posts where id = 1');
        $db_post_title = ucfirst($db_post[0]->title);

        $model_post = Post::find(1);
        $model_post_title = $model_post->title;

        $this->assertEquals($db_post_title, $model_post_title);
    }
}

我們可以看到更新後的程式碼和 Laravel 程式碼編碼風格完全一致。在類的開始我們引入了相關依賴類檔案。在 testAccessorTest 方法裡,我們希望驗證定義在 Post 模型裡面的 getNameAttribute 方法的正確性。

為了實現這樣的測試功能,我們通過 DB 類使用原生 SQL 查詢到一篇文章,並將文章的標題賦值給 $db_post_title 變數。

之後,我們通過 Post 模型獲取經過 getNameAttribute 方法處理過後的同一篇文章的標題賦值給 $model_post_title

最後,通過 assertEquals 方法比較兩個變數是否相等。

以上就是如何在 Laravel 中使用單元測試的使用方法。

功能測試

這一節我們將學習如何建立功能測試用例來對先前建立的控制器進行「功能測試」。

通過下面給出的命令,我們將建立一個名為 AccessorTest 的功能測試用例。注意這邊我們沒有使用 --unit 命令列選項,所以命令會在 tests/Feature 目錄下建立一個「功能測試」用例。

$ php artisan make:test AccessorTest

命令會建立檔名為 tests/Feature/AccessorTest.php 的測試類。

<?php
// tests/Feature/AccessorTest.php
namespace Tests\Feature;

use Tests\TestCase;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;

class AccessorTest extends TestCase
{
    /**
     * A basic test example.
     *
     * @return void
     */
    public function testExample()
    {
        $this->assertTrue(true);
    }
}

同樣我們替換掉 testExample 方法。

<?php
// tests/Feature/AccessorTest.php
namespace Tests\Feature;

use Tests\TestCase;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;

class AccessorTest extends TestCase
{
    /**
     * A basic test example.
     *
     * @return void
     */
    public function testBasicTest()
    {
        $db_post = DB::select('select * from posts where id = 1');
        $db_post_title = ucfirst($db_post[0]->name);

        $response = $this->get('/accessor/index?id=1');

        $response->assertStatus(200);
        $response->assertSeeText($db_post_title);
    }
}

熟悉功能測試的朋友應該對上面的程式碼不會太陌生。

首先,我們還是通過 DB 類使用原生 SQL 查詢到一篇文章,並將文章的標題賦值給 $db_post_title 變數。接著我們模擬一個訪問 /accessor/index?id=1 URI 的 GET 請求,並通過 $response 變數接收響應。

然後,我們去匹配請求響應的狀態碼是否為 200。在我們的測試用例中的這個 GET 請求響應狀態碼應該是 200。此外,測試後還將獲取到一個首字母大寫的標題,判斷標題是否與 $db_post_title 相對的方法是 assertSeeText

編寫完成所有的測試用例後。接下來需要去執行這些測試用例。在 Laravel 專案中執行 PHPUnit 測試用例,僅需在專案更目錄執行下面的命令。

$ phpunit

這個命令會執行專案中的所有測試用例。測試中的斷言會以標準的 PHPUnit 輸出顯示在控制檯。

總結

今天,我們探討了 Laravel 內建測試元件 PHPUnit 的測試用例實現方法。本文僅涉及 PHPUnit 「單元測試」和「功能測試」的基礎知識,工作中我們還需要結合實際出發,對 PHPUnit 測試進行深入研究才行。

此外,我們還學習了通過使用 artisan 命令列工具建立分別建立了用於單元測試和功能測試的測試用例的方法。

如果你是 Laravel 初學者,或者抱著學習的目標擴充套件自己的視野,可以關注 Envato Market

另外,不要害羞,就是幹!

原文:https://code.tutsplus.com/tutorials/testing-in-laravel--cms-30465

相關文章