グッズ

PHPUnit と PEST を使用して、簡単な例で Laravel でテストを実行する方法を学びます。

自動テストや単体テストに関しては、どのプログラミング言語においても、次の XNUMX つの反対の意見があります。

  • 失われた時間
  • それなしではやっていけない

したがって、この記事では、特に Laravel で自動テストを始めるのがいかに簡単かを実証することで、前者を説得しようとします。

まず「なぜ」について話してから、その方法の例をいくつか見てみましょう。

自動テストが必要な理由

自動テストはコードの一部を実行し、エラーを報告します。 それがそれらを説明する最も簡単な方法です。 アプリに新機能を導入すると、パーソナル ロボット アシスタントが手動で新機能をテストし、同時に新しいコードが古い機能を壊さないかどうかもテストすることを想像してください。

これが主な利点です。すべての機能を自動的に再テストします。 余計な作業のように思えるかもしれませんが、「ロボット」に指示しないのであれば、代わりに手動で行うべきですよね。 

あるいは、ユーザーがバグを報告することを期待して、機能するかどうかをテストせずに新機能がリリースされる可能性もあります。

自動テストにはいくつかの利点があります。

  • 手動テスト時間を節約します。
  • これらにより、回帰を回避することで、新しい機能の実装と統合された機能の両方で時間を節約できます。
  • このメリットに、すべての新機能とすでに実装されているすべての機能を掛け合わせます。
  • 前の XNUMX つのポイントは、すべての新しいバージョンに適用されます。
  • ...

過去数年に書かれたコードも、そのテスト方法さえも知らない新しい開発者がチームに加わった状態で、XNUMX ~ XNUMX 年後のアプリケーションを想像してみてください。 

最初の自動テスト

最初の操作を実行するには Laravelでの自動テスト、コードを記述する必要はありません。 はい、そのとおりです。 すべてはプレインストールですでに構成および準備されていますdefiLaravel の最初の基本的なサンプルを含む。

Laravel プロジェクトをインストールして、最初のテストをすぐに実行してみてください。

laravel new project
cd project
php artisan test

コンソールには次の結果が表示されるはずです。

プリを見てみるとdefiLaravelの夜 /tests、次の XNUMX つのファイルがあります。

テスト/機能/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_successful_response() 下線記号をスペースに置き換えるだけで、テスト結果を表示するときに読みやすいテキストになります。

テスト/Unit/ExampleTest.php :

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

これが真実かどうかを確認するのは少し無意味に思えますか? 

単体テストについては後ほど詳しく説明します。 現時点では、各テストで一般的に何が起こるかを理解する必要があります。

  • フォルダー内の各テスト ファイル /tests の TestCase を拡張する PHP クラスです。 PHPユニット
  • 各クラス内で複数のメソッドを作成できます。通常は、テストする状況に対して XNUMX つのメソッドを作成します。
  • 各メソッドには XNUMX つのアクションがあります。状況を準備し、アクションを実行し、結果が期待どおりかどうかを検証 (肯定) します。

構造的には、知っておく必要があるのはこれだけです。その他のことは、テストしたい正確な内容によって異なります。

空のテスト クラスを生成するには、次のコマンドを実行します。

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

それでは、Laravel でテストコードが失敗した場合に何が起こるかを見てみましょう

次に、テスト アサーションが期待した結果を返さなかった場合に何が起こるかを見てみましょう。

テスト例を次のように変更してみましょう。

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

そして今、コマンドを実行すると 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

失敗したテストが XNUMX つあり、FAIL としてマークされています。以下の説明と、失敗したテストの正確な行を矢印で示しています。 エラーはこのように表示されます。

例: Laravel での登録フォーム コードのテスト

フォームがあり、さまざまなケースをテストする必要があるとします。無効なデータで失敗するかどうかをチェックし、正しい入力で成功するかどうかをチェックします。

公式スターターキット by ララベル・ブリーズ 私を含む その中の機能をテストする。 そこからいくつかの例を見てみましょう。

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

ここでは、XNUMX つのクラスに XNUMX つのテストがあります。これらは両方とも登録フォームに関連しているためです。XNUMX つはフォームが正しくロードされているかどうかをチェックし、もう XNUMX つは送信が正常に機能するかどうかをチェックします。

結果を検証するためのさらに XNUMX つの方法、つまりさらに XNUMX つのアサーションについて理解しましょう。 $this->assertAuthenticated()$response->assertRedirect()。 公式ドキュメントで利用可能なすべてのアサーションを確認できます。 PHPユニット e Laravelの応答 。 この主題に関していくつかの一般的な主張があることに注意してください $this、他の人は特定のことをチェックします $responseルートコールから。

もう一つ重要なことは、 use RefreshDatabase;ストロークを含むステートメントがクラスの上に挿入されます。 これは、この例のように、テスト アクションがデータベースに影響を与える可能性がある場合に必要です。 usersデータベーステーブル。 このためには、次のように更新される別のテスト データベースを作成する必要があります。 php artisan migrate:freshテストが実行されるたびに。

オプションは XNUMX つあります。別のデータベースを物理的に作成するか、メモリ内の SQLite データベースを使用します。 両方ともファイル内で設定されます phpunit.xmlデフォルトで提供されるdefiニタと ララベル。 具体的には、次の部分が必要です。

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

私たちも使うことができます データベーステストアサーション そして次のようなことをします:

$this->assertDatabaseCount('users', 1);
 
// Or...
$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();
    }
}

ログインフォームについてです。 ロジックは登録と似ていますね。 ただし、XNUMX つではなく XNUMX つの方法を使用しているため、これは良いシナリオと悪いシナリオの両方をテストする例です。 したがって、一般的なロジックは、物事がうまくいったときと失敗したときの両方のケースをテストする必要があるということです。

イノベーションニュースレター
イノベーションに関する最も重要なニュースをお見逃しなく。 メールで受け取るにはサインアップしてください。

また、このテストでわかるのは、 データベースファクトリー : Laravel は偽のユーザーを作成します ( もう一度、更新されたテストデータベース上で )、正しいまたは間違った資格情報を使用してログインを試行します。

もう一度言いますが、Laravel はファクトリープリを生成します。defiニタの虚偽のデータ Userモデル、箱外。

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自体で色々なものが用意されているので、テストは簡単に始められるでしょうか?

したがって、実行すると php artisan testLaravel Breeze をインストールすると、次のような画面が表示されるはずです。

 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

単体テストなどと比較した機能テスト

サブフォルダーを見ました tests/Feature e tests/Unit ?. 

それらの違いは何ですか? 

世界的には、Laravel/PHP エコシステムの外に、いくつかの種類の自動テストがあります。 次のような用語が見つかります。

  • 単体テスト
  • 機能テスト
  • 結合テスト
  • 機能テスト
  • エンドツーエンドのテスト
  • 受け入れテスト
  • 煙テスト

複雑そうに聞こえますし、これらのタイプのテスト間の実際の違いがあいまいになっていることもあります。 そのため、Laravel はこれらの混乱を招く用語をすべて単純化し、ユニット/機能の XNUMX つにグループ化しました。

簡単に言えば、機能テストはアプリケーションの実際の機能を実行しようとします。つまり、URL を取得し、API を呼び出し、フォームへの入力などの正確な動作を模倣します。 機能テストは通常​​、プロジェクト ユーザーが実際に手動で行うのと同じまたは類似の操作を実行します。

単体テストには XNUMX つの意味があります。 一般に、自動化されたテストはすべて「単体テスト」と呼ばれ、プロセス全体も「単体テスト」と呼ばれることがあります。 ただし、機能とユニットという観点から見ると、このプロセスはコードの特定の非公開ユニットを分離してテストすることになります。 たとえば、パラメーターを使用した合計注文価格など、何かを計算するメソッドを備えた Laravel クラスがあるとします。 したがって、単体テストでは、さまざまなパラメーターを使用して、そのメソッド (コード単位) から正しい結果が返されるかどうかが示されます。

単体テストを生成するには、フラグを追加する必要があります。

php artisan make:test OrderPriceTest --unit

生成されたコードは事前単体テストと同じですdefiLaravel システム:

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

ご覧のとおり、存在しません RefreshDatabase、そしてこれはそのうちのXNUMXつです defi最も一般的な単体テスト定義: データベースには触れず、実行中のアプリケーションから隔離された「ブラック ボックス」として機能します。

先ほど述べた例を真似して、サービス クラスがあると想像してみましょう。 OrderPrice.

app/Services/OrderPriceService.php:

class OrderPriceService
{
    public function calculatePrice($productId, $quantity, $tax = 0.0)
    {
        // Some kind of calculation logic
    }
}

その場合、単体テストは次のようになります。

class OrderPriceTest extends TestCase
{
    public function test_single_product_no_taxes()
    {
        $product = Product::factory()->create(); // generate a fake product
        $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);
    }
 
    // More cases with more parameters
}

Laravel プロジェクトに関する私の個人的な経験では、テストの大部分は単体テストではなく機能テストです。 まず、アプリケーションが実際に使用する方法で動作するかどうかをテストする必要があります。

次に、特別な計算またはロジックがある場合は、次のことができます。 definire をユニットとして、パラメーターを使用すると、そのための単体テストを作成できます。

場合によっては、テストを作成するには、コード自体を変更し、より「テストしやすく」するためにコードをリファクタリングする必要があります。つまり、ユニットを特別なクラスまたはメソッドに分離します。

いつ、どのようにテストを実行するか?

これの実際の用途は何ですか php artisan test、いつ実行する必要がありますか?

ビジネス ワークフローに応じてさまざまなアプローチがありますが、一般的には、最終的なコード変更をリポジトリにプッシュする前に、すべてのテストが「グリーン」である (つまり、エラーがない) ことを確認する必要があります。

次に、ローカルでタスクに取り組み、完了したと思ったら、いくつかのテストを実行して、何も壊れていないことを確認します。 あなたのコードはあなたのロジックにバグを引き起こす可能性があるだけでなく、ずっと前に書かれた他の誰かのコードの他の動作を意図せず破壊する可能性があることに注意してください。

さらに一歩進めれば自動化も可能です 多くの もの。 さまざまな CI/CD ツールを使用すると、誰かが特定の Git ブランチに変更をプッシュするたびに、またはコードを運用ブランチにマージする前に実行するテストを指定できます。 最も単純なワークフローは、Github Actions を使用することです。 別のビデオ それが証明されています。

何をテストする必要がありますか?

いわゆる「テスト範囲」をどれくらいの大きさにすべきかについては、さまざまな意見があります。すべてのページで考えられるすべての操作とケースを試すか、作業を最も重要な部分に限定するかです。

実際、この点で、自動テストは実際の利益をもたらすよりも時間がかかると非難する人々に私は同意します。 これは、あらゆる詳細に対してテストを作成した場合に発生する可能性があります。 そうは言っても、プロジェクトによってはそれが必要になる場合があります。主な問題は、「潜在的なエラーの代償はいくらか」ということです。

言い換えれば、「このコードが失敗したらどうなるでしょうか?」という質問をして、テスト作業に優先順位を付ける必要があります。 決済システムにバグがあると、ビジネスに直接的な影響を及ぼします。 したがって、ロール/権限の機能が壊れている場合、これはセキュリティ上大きな問題となります。

マット・シュタウファーがカンファレンスで述べた言葉が気に入っています。「失敗すれば仕事をクビになるようなことをまずテストしなければならない。」 もちろんそれは大げさですが、重要なことを最初に試してみることは理解できます。 時間があれば、その他の機能も追加します。

PEST: PHPUnit の新しい代替手段

上記の例はすべて、Laravel 事前テスト ツールに基づいています。defi夜: PHPユニット 。 しかし、長年にわたって他のツールがエコシステムに登場しており、最新の人気ツールの XNUMX つは次のとおりです。 害虫 。 Laravel の公式従業員によって作成されました ヌーノ・マドゥロ は、構文を簡素化し、テスト用のコードの記述をさらに高速化することを目的としています。

ボンネットの下で動作します su PHPUnit は追加レイヤーとして、事前に繰り返される部分を最小限に抑えようとしていますdefiPHPUnit コードの一部。

例を見てみましょう。 事前機能テストクラスを覚えておいてくださいdefiLaravelで夜を過ごしていますか? 思い出させておきます:

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 で行うとどうなるかご存知ですか?

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

はい、XNUMX 行のコードだけで終わりです。 したがって、PEST の目標は、以下のオーバーヘッドを削除することです。

  • すべてのクラスとメソッドを作成します。
  • テストケースの拡張。
  • アクションを別々の行に置くことで、PEST ではそれらをチェーン化できます。

Laravel で PEST テストを生成するには、追加のフラグを指定する必要があります。

php artisan make:test HomepageTest --pest

この記事の執筆時点では、PEST は Laravel 開発者の間で非常に人気がありますが、この追加ツールを使用してその構文と PHPUnit の注意事項を学ぶかどうかは個人の好みです。

BlogInnovazione.it

イノベーションニュースレター
イノベーションに関する最も重要なニュースをお見逃しなく。 メールで受け取るにはサインアップしてください。

最近の記事

子供のためのぬり絵の利点 - すべての年齢層のための魔法の世界

ぬり絵を通じて細かい運動能力を発達させることで、子供たちは書くなどのより複雑なスキルを習得できるようになります。色…

2月2024

未来はここにあります: 海運業界が世界経済をどのように変革しているか

海軍部門は真の世界経済大国であり、150 億市場に向けて舵を切り続けています...

1月2024

パブリッシャーと OpenAI が人工知能によって処理される情報の流れを規制する契約に署名

先週の月曜日、フィナンシャル・タイムズ紙はOpenAIとの契約を発表した。 FT は世界クラスのジャーナリズムにライセンスを供与しています…

4月30 2024

オンライン支払い: ストリーミング サービスで永久に支払いができる仕組みをご紹介します

何百万人もの人々がストリーミング サービスに月額料金を払っています。あなたは…というのが一般的な意見です。

4月29 2024

あなたの言語でイノベーションを読む

イノベーションニュースレター
イノベーションに関する最も重要なニュースをお見逃しなく。 メールで受け取るにはサインアップしてください。

Seguici