ホームページ  >  記事  >  PHPフレームワーク  >  Laravel でテストを自動化するにはどうすればよいですか? PHPUnitとPESTの共有例

Laravel でテストを自動化するにはどうすればよいですか? PHPUnitとPESTの共有例

青灯夜游
青灯夜游転載
2022-12-12 20:18:451518ブラウズ

Laravel自動テストを実行するにはどうすればよいですか?次の記事では、PHPUnit と PEST を使用した簡単な例を使用して、Laravel でテストを開始する方法を学習します。

Laravel でテストを自動化するにはどうすればよいですか? PHPUnitとPESTの共有例

プログラミング言語の自動テストや単体テストについて話すとき、一般に 2 つのカテゴリーに分けられます。自動テストを書かない人、時間の無駄だと思っている人

    テストを書いても、それなしの仕事は考えられない人
  • そこで、この記事を通して私は次のことを試してみます。前者にそのメリットを見てもらい、Laravel で自動テストを開始するのがいかに簡単かを理解してもらうために。 [関連する推奨事項:
  • laravel ビデオ チュートリアル
]

まず、「理由」について話してから、非常に基本的な「テスト方法」の例をいくつか示します。

自動テストが必要な理由

自動テストは複雑ではありません。コードの一部を実行し、エラーを報告するだけです。それがそれらを説明する最も簡単な方法です。アプリで新しい機能を起動すると、ロボット アシスタントが手動で新しい機能をテストしながら、新しいコードが古い機能の機能を壊さないことをテストしていると想像してください。

この利点は、すべての機能を自動的に再テストできることです。これは余分な作業のように思えるかもしれませんが、「ボット」に実行するように指示しない場合は、手動で実行する必要があります。それとも、詳細なテストを行わずに新機能を展開し、ユーザーがバグを報告することを期待しますか?私はこのアプローチを皮肉を込めて「指を交差させた開発」と呼んでいます。

アプリケーションに新しい機能が追加されるたびに、自動テストからの利益はますます高くなります。

機能 1: 手動でのテスト時間を X 分節約

    機能 2: 2X 分節約 - 機能 2 と機能 1 についても同様
  • 機能 3: 3 倍節約分…
  • など。
  • あなたは理解する必要があります。アプリケーションを開発してから 1 ~ 2 年以内に、チームの新しい開発者が「機能 1」がどのように機能するのか、テストのためにそれを再現する方法さえ知らないと想像してください。したがって、将来のあなたは、自動テストを作成してくれたことに非常に感謝するでしょう。
もちろん、あなたが自分のプロジェクトを非常に短期的なプロジェクトだと考えていて、将来のことはあまり気にしていないのであれば…いいえ、私はあなたの善意を信じています。それでは、どのように簡単かを説明しましょう。それはテストを開始することです。

最初の自動テストの開始

Laravel で最初の自動テストを実行するには、コードを記述する必要はありません。はい、あなたは間違っていません。最初の本当に基本的な例を含め、デフォルトの 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

If we take aデフォルトの Laravel

/tests

フォルダーには 2 つのファイルがあります。

tests/Feature/ExampleTest.php

:

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

        $response->assertStatus(200);
    }
}
このコードの意味を理解するために構文を知る必要はありません。ホームページをロードして、次のことを確認してください。 HTTP ステータス コード「200 OK」。

注意する必要があります。テスト結果を表示するときに、メソッド名

test_the_application_returns_a_ success_response()

を読みやすいテキストに変更する方法は、下線記号をスペースに置き換えるだけです。

tests/Unit/ExampleTest.php

:

class ExampleTest extends TestCase{
    public function test_that_true_is_true()
    {
        $this->assertTrue(true);
    }
}
このようなコードは無意味に思えますが、結果が true であることを確認する必要がありますか?後のセグメントでは、単体テストについて詳しく説明します。現時点では、各テストで通常何が起こるかを理解する必要があるだけです。

tests/ 空のテスト クラスを生成するには、次のコマンドを実行するだけです:

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);
    }
}

テストが失敗した場合はどうなりますか?

テスト アサーションが期待した結果を返さなかった場合に何が起こるかを説明しましょう。 テスト例を次のように編集しましょう:

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);
    }
}

ここで、
phpArtisan 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 とマークされたステートメントが 2 つあります。以下で説明するように、矢印はアサーションが失敗した正確なテスト行を指します。したがって、これがエラーの表示方法です。これはとても便利ですね。


简单示例:注册表单

让我们来看看一个现实生活中常见的例子。假设你有一个表单,你需要测试各种情况:检查是否填充无效数据是否失败,检查是否输入正确输入成功等。

你不一定知道,其实官方的 Laravel Breeze 入门套件附带了 内部功能测试?现在,让我们从那里看几个例子:

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 官方文档中所有可用的断言。请记住,一些一般的断言发生在 $this 对象上,而另一些检查则来自于路由调用的特定 $response 语句。

另一件重要的事情是 use RefreshDatabase; 语句,使用这个 trait,包含在这个类的上方。当你的测试操作可能会影响数据库时,需要使用它,例如在本例中,注册会在 users 数据库表中添加一个新条目。为此,你需要创建一个单独的测试数据库,该数据库将会在每次测试中使用 php artisan migrate:fresh 命令时被刷新。

你有两个选择:物理上创建一个单独的数据库,或者使用内存中的 SQLite 数据库。它都在 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,最简单的操作就是取消注释这些行,你的测试将在该内存数据库上运行。

在本次测试中,我们断言用户通过了身份验证,并被重定向到正确的首页,但我们也可以测试数据库中真实的数据。

除此代码之外:

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

我们也可以使用 Database Testing assertions 并执行以下操作:

$this->assertDatabaseCount(&#39;users&#39;, 1);

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

另外一个真实示例:登录表单

让我们看看另外一个来自 Laravel Breeze 的测试。

tests/Feature/AuthenticationTest.php:

class AuthenticationTest extends TestCase
{
    use RefreshDatabase;

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

        $response->assertStatus(200);
    }

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

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

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

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

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

        $this->assertGuest();
    }
}

这是关于登录表单的例子。他的逻辑和注册差不多吧?但不一样的是使用了三个方法而不是两个,所以这是一个测试好的和坏的场景的例子。所以,他们共同的逻辑是你应该测试的两种情况:什么时候顺利,什么时候失败。

此外,你在这个测试中看到的是 Database 工厂类 的使用:Laravel 创建了一个假用户(再次, 在你的测试数据库刷新) 上,然后尝试使用正确或不正确的凭据登录。

同样,Laravel 为 User 模型生成带有假数据的默认工厂,开箱即用。

database/factories/UserFactory.php:

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

看,有多少东西是 Laravel 本身提供的,所以我们很容易开始测试。

因此,如果我们在安装 Laravel Breeze 后运行 php artisan test, 我们应该会看到如下内容:

 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 其他

你已经看到了 tests/Featuretests/Unit 子文件夹。两者之间有什么区别?答案有点“哲学”。

从测试的全局视角来看,在 Laravel/PHP 生态系统之外,有不同类型的自动化测试。你可以找到以下术语:

  • 单元测试
  • 功能测试
  • 集成测试
  • 功能测试
  • 端到端测试
  • 验收测试
  • 烟雾测试
  • 其他

这听起来很复杂,而且这些测试类型之间的实际差异有时是模糊的。这就是为什么 Laravel 简化了所有这些令人困惑的术语并将它们分为两类:单元测试/功能测试。

简而言之,功能测试尝试运行应用程序的实际功能:获取 URL、调用 API、模拟填写表单等确切行为。功能测试通常执行与任何项目用户在现实生活中手动执行的相同或相似的事情。

单元测试有两个含义。通常,你可能会发现任何自动化测试都称为「单元测试」,而整个过程可能称为「单元测试」。但是在功能与单元的上下文中,这个过程是关于单独测试代码的特定非公共单元。例如,你有一些 Laravel 类,它有一个计算某些东西的方法,比如带有参数的订单的总价格。因此,你的单元测试将断言该方法(代码单元)是否返回了具有不同参数的正确结果。

要生成单元测试,你需要添加一个标志:

php artisan make:test OrderPriceTest --unit

生成的代码与 Laravel 的默认单元测试相同:

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

如你所见,没有 RefreshDatabase 行为的定义,这是单元测试最常见的定义之一:它不涉及数据库,它像一个「黑匣子」一样工作,与正在运行的应用程序隔离。

你可以尝试模仿我之前提到的示例,假设我们有一个服务类 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(); // 生成假的产品数据
        $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);
    }

    // 更多的参数和案例
}

从我个人对 Laravel 项目的经验而言,绝大多数测试是功能测试,而不是单元测试。首先,你需要测试你的应用程序是否正常工作,以及真实用户使用它的方式。

接下来,如果你有可以定义为单元的特殊计算或逻辑,或带有一些参数,你可以专门为此创建单元测试。

有时候,编写测试需要更改代码本身,并将其重构为更「可测试的」:将单元分离为特殊的类或方法。


何时/如何运行测试?

php artisan test 命令的实际用途是什么,我们应该在什么时候运行它?

什么时候运行测试,在开发过程中并没有固定的时间节点或说法,具体取决于你公司的工作流程。通常情况下,在我们将最新的代码更改推送到代码仓库之前,你需要确保所有测试都是「绿色的」(意味着没有错误)。

因此,当你在本地编写代码,在你觉得自己已经完成了你的任务时,你需要运行测试,用来确保你没有破坏任何东西。请记住,你的代码可能不仅会在你自己编写的代码逻辑中导致错误,而且还会无意中破坏其他人很久以前编写的代码中的其他行为。

如果我们更进一步,可以自动化的完成很多事情。如使用各种 CI/CD 工具,你可以指定在有人将更改推送到特定 Git 分支时或在将代码合并到生产分支之前执行的测试。最简单的工作流程是使用 Github Actions,在这里,我提供了 一个单独的视频 演示它。


你应该测试什么?

关于所谓的「测试覆盖率」应该覆盖到多大的范围的争议,一直以来,有多种意见:你应该测试每个页面上的每个操作和每个可能的案例,还是只将你的工作限制在最重要的部分。

事实上,这就是我同意人们指责自动化测试花费更多时间而不是带来实际收益观点的地方。如果你为每个细节编写测试,这种情况就可能出现。也就是说,你的项目可能需要思考这个问题:「代码中潜在的错误会给你带来多大的成本或代价」。

换句话说,你需要通过“如果此代码失败会发生什么?”这个问题来确定你的测试工作的优先级。如果你的支付系统存在错误,这将直接影响业务。如果你的角色/权限功能被破坏,那这将是一个巨大的安全问题。

我喜欢 Matt Stauffer 在一次会议上的措辞:「你需要先测试这些东西,如果它们失败了,你就会被解雇」。当然,这有点夸张,但你明白了:首先测试重要的事情。然后是其他功能,如果你有时间的话。


PEST:PHPUnit 的新流行替代品

以上所有示例均基于默认的 Laravel 测试工具:PHPUnit。但多年来,生态系统中出现了其他工具,最新流行的工具之一是 PEST。由 Laravel 官方员工 Nuno Maduro 创建,它的目标是简化语法,从而更快地编写测试代码。

在底层实现上,它基于 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(&#39;/&#39;);

        $response->assertStatus(200);
    }
}

让我们使用 PEST 来实现同样的测试,实现后的代码如下:

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

是的,一行代码,就是这样。因此,PEST 的目标是解决以下问题:

  • 为一切创建类和方法;
  • 扩展测试用例;
  • 将所有操作放在一行代码上 – 在 PEST 中,你可以使用链式操作把不同动作串联起来。

要在 Laravel 中生成 PEST 测试,你需要指定一个附加标志:

php artisan make:test HomepageTest --pest

在撰写本文时,PEST 在 Laravel 开发人员中相当流行,但是除了众所周知的 PHPUnit 之外,是否使用这个额外的工具并学习它的语法是你个人的喜好。


因此,这就是你需要了解的有关自动化测试基础知识的全部内容。从这里开始,你可以选择创建哪些测试以及如何在你的项目中运行它们。

原文地址:https://laravel-news.com/how-to-start-testing

译文地址:https://learnku.com/laravel/t/67381

更多编程相关知识,请访问:编程教学!!

以上がLaravel でテストを自動化するにはどうすればよいですか? PHPUnitとPESTの共有例の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はlearnku.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。