溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Laravel怎么進行自動化測試

發(fā)布時間:2022-12-13 09:42:39 來源:億速云 閱讀:109 作者:iii 欄目:編程語言

這篇文章主要介紹“Laravel怎么進行自動化測試”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“Laravel怎么進行自動化測試”文章能幫助大家解決問題。

為什么需要自動化測試

自動化測試并不復雜:它們只是為你運行部分代碼并報告任何錯誤。這是描述它們的最簡單的方式。想象一下,你正在應用程序啟動一項新功能,然后一個機器人助理會為你手動測試新功能,同時測試新代碼是否不會破壞舊功能的任何內(nèi)容。

這樣的好處是:自動重新測試所有功能。這似乎是額外的工作,但如果你不告訴那個「機器人」去做,那么你應該自己手動做,對吧?或者你在沒有詳細測試的情況下推出新功能,希望用戶報告錯誤?我諷刺地稱這種方法為「手指交叉驅動的開發(fā)」。

隨著應用程序的每一項新功能,自動化測試的回報越來越高。

  • 功能 1:手動節(jié)省 X 分鐘的測試時間

  • 功能 2:節(jié)省 2X 分鐘 - 再次用于功能 2 和功能 1

  • 功能 3:節(jié)省 3X 分鐘…

  • 等等。

你應該明白了。想象一下你的應用程序在一兩年內(nèi),團隊中的新開發(fā)人員甚至不知道「功能 1」如何運行或如何重現(xiàn)它以進行測試。所以,你未來的自己會非常感謝你編寫自動化測試。

當然,如果你認為你的項目是一個非常短期的項目,并且你不太關心它的未來…… 不,我相信你的好意,所以讓我告訴你開始測試是多么容易。

開始我們第一個自動化測試

要在 Laravel 中運行第一個自動化測試,你不需要編寫任何代碼。是的,你沒看錯。一切都已經(jīng)在默認的 Laravel 安裝中進行了配置和準備,包括第一個真正的基本示例。

你可以嘗試安裝一個 Laravel 項目并立即運行第一個測試:

laravel  new  project
cd  project
php  artisan  test

按照正常預期,終端將會輸出如下結果:

 PASS  Tests\Unit\ExampleTest
? that true is true

 PASS  Tests\Feature\ExampleTest
? the application returns a successful response

Tests:  2 passed
Time:   0.10s

如果我們看一下默認的 Laravel /tests 文件夾,其中有兩個文件。

tests/Feature/ExampleTest.php:

class ExampleTest extends TestCase
{
    public function test_the_application_returns_a_successful_response()
    {
        $response = $this->get('/');

        $response->assertStatus(200);
    }
}

你無需了解任何語法即可讀懂這段代碼的含義:加載主頁并檢查 HTTP 狀態(tài)代碼是否「200 OK」。

你需要注意:在查看測試結果時,方法名稱 test_the_application_returns_a_successful_response() 如何變?yōu)榭勺x文本,只需將下劃線符號替換為空格即可。

tests/Unit/ExampleTest.php:

class ExampleTest extends TestCase{
    public function test_that_true_is_true()
    {
        $this->assertTrue(true);
    }
}

這樣的代碼看上去讓人感覺毫無意義,檢查結果為 true 很必要嗎?在后面片段中,我們將具體討論單元測試。現(xiàn)在,你只需要了解每次測試中通常發(fā)生的情況。

  • tests/ 文件夾中的每個測試文件都是一個 PHP 類,擴展了 PHPUnit 的 TestCase

  • 在每個類中,你可以創(chuàng)建多個方法,通常一種方法用于一種情況進行測試

  • 每個方法內(nèi)部都有三個動作:準備情況,然后動作,然后檢查(斷言)結果是否符合預期

從結構上講,這就是你需要知道的全部內(nèi)容,其他一切都取決于你要測試的確切內(nèi)容。

要生成一個空的測試類,只需運行以下命令:

php artisan make:test HomepageTest

它會生成文件 tests/Feature/HomepageTest.php

class HomepageTest extends TestCase{
    // Replace this method with your own ones
    public function test_example()
    {
        $response = $this->get('/');

        $response->assertStatus(200);
    }
}

如果測試失敗怎么辦?

讓我向你展示如果測試斷言沒有返回預期結果會發(fā)生什么。
讓我們將示例測試編輯為:

class ExampleTest extends TestCase
{
    public function test_the_application_returns_a_successful_response()
    {
        $response = $this->get('/non-existing-url');

        $response->assertStatus(200);
    }
}


class ExampleTest extends TestCase
{
    public function test_that_true_is_false()
    {
        $this->assertTrue(false);
    }
}

現(xiàn)在,如果我們再次運行 php artisan test

 FAIL  Tests\Unit\ExampleTest
? that true is true

 FAIL  Tests\Feature\ExampleTest
? the application returns a successful response

---

? Tests\Unit\ExampleTest > that true is true
Failed asserting that false is true.

at tests/Unit/ExampleTest.php:16
   12▕      * @return void
   13▕      */
   14▕     public function test_that_true_is_true()
   15▕     {
?  16▕         $this->assertTrue(false);
   17▕     }
   18▕ }
   19▕

? Tests\Feature\ExampleTest > the application returns a successful response
Expected response status code [200] but received 404.
Failed asserting that 200 is identical to 404.

at tests/Feature/ExampleTest.php:19
   15▕     public function test_the_application_returns_a_successful_response()
   16▕     {
   17▕         $response = $this->get('/non-existing-url');
   18▕
?  19▕         $response->assertStatus(200);
   20▕     }
   21▕ }
   22▕


Tests:  2 failed
Time:   0.11s

如你所見,有兩個語句標記為 FAIL,下面有解釋,箭頭指向斷言失敗的確切測試行。所以這就是錯誤的顯示方式。這非常的方便,不是嗎?

簡單示例:注冊表單

讓我們來看看一個現(xiàn)實生活中常見的例子。假設你有一個表單,你需要測試各種情況:檢查是否填充無效數(shù)據(jù)是否失敗,檢查是否輸入正確輸入成功等。

你不一定知道,其實官方的 Laravel Breeze 入門套件附帶了 內(nèi)部功能測試?現(xiàn)在,讓我們從那里看幾個例子:

tests/Feature/RegistrationTest.php

use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class RegistrationTest extends TestCase
{
    use RefreshDatabase;

    public function test_registration_screen_can_be_rendered()
    {
        $response = $this->get('/register');

        $response->assertStatus(200);
    }

    public function test_new_users_can_register()
    {
        $response = $this->post('/register', [
            'name' => 'Test User',
            'email' => 'test@example.com',
            'password' => 'password',
            'password_confirmation' => 'password',
        ]);

        $this->assertAuthenticated();
        $response->assertRedirect(RouteServiceProvider::HOME);
    }
}

在這里,我們在一個類中有兩個測試,因為它們都與注冊表相關:一個是檢查表單是否正確加載了,另一個是檢查提交是否正常。

我們來熟悉另外兩個檢查結果的方法,另外兩個斷言: $this->assertAuthenticated()$response->assertRedirect()。 你可以查看 PHPUnit and Laravel Response 官方文檔中所有可用的斷言。請記住,一些一般的斷言發(fā)生在 $this 對象上,而另一些檢查則來自于路由調(diào)用的特定 $response 語句。

另一件重要的事情是 use RefreshDatabase; 語句,使用這個 trait,包含在這個類的上方。當你的測試操作可能會影響數(shù)據(jù)庫時,需要使用它,例如在本例中,注冊會在 users 數(shù)據(jù)庫表中添加一個新條目。為此,你需要創(chuàng)建一個單獨的測試數(shù)據(jù)庫,該數(shù)據(jù)庫將會在每次測試中使用 php artisan migrate:fresh 命令時被刷新。

你有兩個選擇:物理上創(chuàng)建一個單獨的數(shù)據(jù)庫,或者使用內(nèi)存中的 SQLite 數(shù)據(jù)庫。它都在 Laravel 默認提供的文件 phpunit.xml 中配置。具體來說, 你需要這部分:

<php>
    <env name="APP_ENV" value="testing"/>
    <env name="BCRYPT_ROUNDS" value="4"/>
    <env name="CACHE_DRIVER" value="array"/>
    <!-- <env name="DB_CONNECTION" value="sqlite"/> -->
    <!-- <env name="DB_DATABASE" value=":memory:"/> -->
    <env name="MAIL_MAILER" value="array"/>
    <env name="QUEUE_CONNECTION" value="sync"/>
    <env name="SESSION_DRIVER" value="array"/>
    <env name="TELESCOPE_ENABLED" value="false"/>
</php>

看到被注釋掉的 DB_CONNECTIONDB_DATABASE 了嗎?如果你的服務器上有 SQLite,最簡單的操作就是取消注釋這些行,你的測試將在該內(nèi)存數(shù)據(jù)庫上運行。

在本次測試中,我們斷言用戶通過了身份驗證,并被重定向到正確的首頁,但我們也可以測試數(shù)據(jù)庫中真實的數(shù)據(jù)。

除此代碼之外:

$this->assertAuthenticated();
$response->assertRedirect(RouteServiceProvider::HOME);

我們也可以使用 Database Testing assertions 并執(zhí)行以下操作:

$this->assertDatabaseCount('users', 1);

// 或者...
$this->assertDatabaseHas('users', [
    'email' => 'test@example.com',
]);

另外一個真實示例:登錄表單

讓我們看看另外一個來自 Laravel Breeze 的測試。

tests/Feature/AuthenticationTest.php:

class AuthenticationTest extends TestCase
{
    use RefreshDatabase;

    public function test_login_screen_can_be_rendered()
    {
        $response = $this->get('/login');

        $response->assertStatus(200);
    }

    public function test_users_can_authenticate_using_the_login_screen()
    {
        $user = User::factory()->create();

        $response = $this->post('/login', [
            'email' => $user->email,
            'password' => 'password',
        ]);

        $this->assertAuthenticated();
        $response->assertRedirect(RouteServiceProvider::HOME);
    }

    public function test_users_can_not_authenticate_with_invalid_password()
    {
        $user = User::factory()->create();

        $this->post('/login', [
            'email' => $user->email,
            'password' => 'wrong-password',
        ]);

        $this->assertGuest();
    }
}

這是關于登錄表單的例子。他的邏輯和注冊差不多吧?但不一樣的是使用了三個方法而不是兩個,所以這是一個測試好的和壞的場景的例子。所以,他們共同的邏輯是你應該測試的兩種情況:什么時候順利,什么時候失敗。

此外,你在這個測試中看到的是 Database 工廠類 的使用:Laravel 創(chuàng)建了一個假用戶(再次, 在你的測試數(shù)據(jù)庫刷新) 上,然后嘗試使用正確或不正確的憑據(jù)登錄。

同樣,Laravel 為 User 模型生成帶有假數(shù)據(jù)的默認工廠,開箱即用。

database/factories/UserFactory.php:

class UserFactory extends Factory
{
    public function definition()
    {
        return [
            'name' => $this->faker->name(),
            'email' => $this->faker->unique()->safeEmail(),
            'email_verified_at' => now(),
            'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
            'remember_token' => Str::random(10),
        ];
    }
}

看,有多少東西是 Laravel 本身提供的,所以我們很容易開始測試。

因此,如果我們在安裝 Laravel Breeze 后運行 php artisan test, 我們應該會看到如下內(nèi)容:

 PASS  Tests\Unit\ExampleTest
? that true is true

 PASS  Tests\Feature\Auth\AuthenticationTest
? login screen can be rendered
? users can authenticate using the login screen
? users can not authenticate with invalid password

 PASS  Tests\Feature\Auth\EmailVerificationTest
? email verification screen can be rendered
? email can be verified
? email is not verified with invalid hash

 PASS  Tests\Feature\Auth\PasswordConfirmationTest
? confirm password screen can be rendered
? password can be confirmed
? password is not confirmed with invalid password

 PASS  Tests\Feature\Auth\PasswordResetTest
? reset password link screen can be rendered
? reset password link can be requested
? reset password screen can be rendered
? password can be reset with valid token

 PASS  Tests\Feature\Auth\RegistrationTest
? registration screen can be rendered
? new users can register

 PASS  Tests\Feature\ExampleTest
? the application returns a successful response

Tests:  17 passed
Time:   0.61s

功能測試 VS 單元測試 VS 其他

你已經(jīng)看到了 tests/Featuretests/Unit 子文件夾。兩者之間有什么區(qū)別?答案有點“哲學”。

從測試的全局視角來看,在 Laravel/PHP 生態(tài)系統(tǒng)之外,有不同類型的自動化測試。你可以找到以下術語:

  • 單元測試

  • 功能測試

  • 集成測試

  • 功能測試

  • 端到端測試

  • 驗收測試

  • 煙霧測試

  • 其他

這聽起來很復雜,而且這些測試類型之間的實際差異有時是模糊的。這就是為什么 Laravel 簡化了所有這些令人困惑的術語并將它們分為兩類:單元測試/功能測試。

簡而言之,功能測試嘗試運行應用程序的實際功能:獲取 URL、調(diào)用 API、模擬填寫表單等確切行為。功能測試通常執(zhí)行與任何項目用戶在現(xiàn)實生活中手動執(zhí)行的相同或相似的事情。

單元測試有兩個含義。通常,你可能會發(fā)現(xiàn)任何自動化測試都稱為「單元測試」,而整個過程可能稱為「單元測試」。但是在功能與單元的上下文中,這個過程是關于單獨測試代碼的特定非公共單元。例如,你有一些 Laravel 類,它有一個計算某些東西的方法,比如帶有參數(shù)的訂單的總價格。因此,你的單元測試將斷言該方法(代碼單元)是否返回了具有不同參數(shù)的正確結果。

要生成單元測試,你需要添加一個標志:

php artisan make:test OrderPriceTest --unit

生成的代碼與 Laravel 的默認單元測試相同:

class OrderPriceTest extends TestCase
{
    public function test_example()
    {
        $this->assertTrue(true);
    }
}

如你所見,沒有 RefreshDatabase 行為的定義,這是單元測試最常見的定義之一:它不涉及數(shù)據(jù)庫,它像一個「黑匣子」一樣工作,與正在運行的應用程序隔離。

你可以嘗試模仿我之前提到的示例,假設我們有一個服務類 OrderPrice。

app/Services/OrderPriceService.php:

class OrderPriceService{
    public function calculatePrice($productId, $quantity, $tax = 0.0)
    {
        // 某種計算邏輯
    }
}

然后,單元測試可能看起來像這樣:

class OrderPriceTest extends TestCase{
    public function test_single_product_no_taxes()
    {
        $product = Product::factory()->create(); // 生成假的產(chǎn)品數(shù)據(jù)
        $price = (new OrderPriceService())->calculatePrice($product->id, 1);
        $this->assertEquals(1, $price);
    }

    public function test_single_product_with_taxes()
    {
        $price = (new OrderPriceService())->calculatePrice($product->id, 1, 20);
        $this->assertEquals(1.2, $price);
    }

    // 更多的參數(shù)和案例
}

從我個人對 Laravel 項目的經(jīng)驗而言,絕大多數(shù)測試是功能測試,而不是單元測試。首先,你需要測試你的應用程序是否正常工作,以及真實用戶使用它的方式。

接下來,如果你有可以定義為單元的特殊計算或邏輯,或帶有一些參數(shù),你可以專門為此創(chuàng)建單元測試。

有時候,編寫測試需要更改代碼本身,并將其重構為更「可測試的」:將單元分離為特殊的類或方法。

何時/如何運行測試?

php artisan test 命令的實際用途是什么,我們應該在什么時候運行它?

什么時候運行測試,在開發(fā)過程中并沒有固定的時間節(jié)點或說法,具體取決于你公司的工作流程。通常情況下,在我們將最新的代碼更改推送到代碼倉庫之前,你需要確保所有測試都是「綠色的」(意味著沒有錯誤)。

因此,當你在本地編寫代碼,在你覺得自己已經(jīng)完成了你的任務時,你需要運行測試,用來確保你沒有破壞任何東西。請記住,你的代碼可能不僅會在你自己編寫的代碼邏輯中導致錯誤,而且還會無意中破壞其他人很久以前編寫的代碼中的其他行為。

如果我們更進一步,可以自動化的完成很多事情。如使用各種 CI/CD 工具,你可以指定在有人將更改推送到特定 Git 分支時或在將代碼合并到生產(chǎn)分支之前執(zhí)行的測試。最簡單的工作流程是使用 Github Actions,在這里,我提供了 一個單獨的視頻 演示它。

你應該測試什么?

關于所謂的「測試覆蓋率」應該覆蓋到多大的范圍的爭議,一直以來,有多種意見:你應該測試每個頁面上的每個操作和每個可能的案例,還是只將你的工作限制在最重要的部分。

事實上,這就是我同意人們指責自動化測試花費更多時間而不是帶來實際收益觀點的地方。如果你為每個細節(jié)編寫測試,這種情況就可能出現(xiàn)。也就是說,你的項目可能需要思考這個問題:「代碼中潛在的錯誤會給你帶來多大的成本或代價」。

換句話說,你需要通過“如果此代碼失敗會發(fā)生什么?”這個問題來確定你的測試工作的優(yōu)先級。如果你的支付系統(tǒng)存在錯誤,這將直接影響業(yè)務。如果你的角色/權限功能被破壞,那這將是一個巨大的安全問題。

我喜歡 Matt Stauffer 在一次會議上的措辭:「你需要先測試這些東西,如果它們失敗了,你就會被解雇」。當然,這有點夸張,但你明白了:首先測試重要的事情。然后是其他功能,如果你有時間的話。

PEST:PHPUnit 的新流行替代品

以上所有示例均基于默認的 Laravel 測試工具:PHPUnit。但多年來,生態(tài)系統(tǒng)中出現(xiàn)了其他工具,最新流行的工具之一是 PEST。由 Laravel 官方員工 Nuno Maduro 創(chuàng)建,它的目標是簡化語法,從而更快地編寫測試代碼。

在底層實現(xiàn)上,它基于 PHPUnit 運行;作為一個附屬擴展,它只是試圖最小化 PHPUnit 代碼的一些默認重復部分。

讓我們來看一個例子。還記得 Laravel 中默認的功能測試類嗎?就如下面這段代碼:

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class ExampleTest extends TestCase
{
    public function test_the_application_returns_a_successful_response()
    {
        $response = $this->get('/');

        $response->assertStatus(200);
    }
}

讓我們使用 PEST 來實現(xiàn)同樣的測試,實現(xiàn)后的代碼如下:

test('the application returns a successful response')->get('/')->assertStatus(200);

是的,一行代碼,就是這樣。因此,PEST 的目標是解決以下問題:

  • 為一切創(chuàng)建類和方法;

  • 擴展測試用例;

  • 將所有操作放在一行代碼上 – 在 PEST 中,你可以使用鏈式操作把不同動作串聯(lián)起來。

要在 Laravel 中生成 PEST 測試,你需要指定一個附加標志:

php artisan make:test HomepageTest --pest

關于“Laravel怎么進行自動化測試”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關的知識,可以關注億速云行業(yè)資訊頻道,小編每天都會為大家更新不同的知識點。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。

AI