首頁  >  文章  >  後端開發  >  在 Laravel 中建立可測試的外觀

在 Laravel 中建立可測試的外觀

Barbara Streisand
Barbara Streisand原創
2024-09-29 06:08:30371瀏覽

Creating a Testable Facade in Laravel

這裡有一份備忘單,介紹如何透過添加依賴項注入、外觀以及輕鬆交換偽造的方法來使簡單的服務類更有用。

骨架很簡單:

  • 原來的服務等級
  • 創造服務類別遵守的契約
  • 在服務提供者中,在容器中註冊服務類別
  • 建立立面
  • 建立一個可以交換用於測試的虛假合約實作

原來的服務等級

這是我們最初的服務類別(很抱歉沒有一個令人信服的例子,但實際上沒有必要為此設計一個)。

<?php

namespace App\Foo;

class FooService
{
    public function foo(): string
    {
        return 'bar';
    }

    public function fizz(): string
    {
        return 'buzz';
    }
}

合約

首先,我們應該創建一份合同,這樣我們就可以確保我們最終的假貨和我們原來的服務都符合預期。以及任何未來的實施。

<?php

namespace App\Foo\Contracts;

interface Foo
{
    public function foo(): string;

    public function fizz(): string;
}

不要忘記確保服務實現它。

<?php

namespace App;

use App\Foo\Contracts\Foo;

class FooService implements Foo
{
   // ...
}

綁定到容器

接下來,我們應該將具體實作綁定到我們服務提供者中的合約。

<?php

namespace App\Providers;

use App\Foo\Contracts\Foo;
use App\FooService;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     */
    public function register(): void
    {
        $this->app->bind(Foo::class, FooService::class);
    }

   // ...
}

門面

現在,我們可以建立我們的外觀類別。

<?php

namespace App\Foo\Facades;

use Illuminate\Support\Facades\Facade;

/**
* @method static string foo(): string
* @method static string fizz(): string
*/
class Foo extends Facade
{
    protected static function getFacadeAccessor(): string
    {
        return \App\Foo\Contracts\Foo::class;
    }
}

外觀只需要從容器中提取的綁定名稱,並從 getFacadeAccessor 返回。在我們的例子中,這是目前綁定了我們的服務的合約的名稱。

請注意,如果您想要 IDE 支持,則必須在類別上方的文件區塊中重新定義方法簽章。

此時,我們可以使用我們的外觀。

用法

<?php

namespace App\Http\Controllers;

use App\Foo\Facades\Foo;

class FooController extends Controller
{
    public function index()
    {
        return response()->json([
            'foo' => Foo::foo(),
        ]);
    }
}

或者,我們也可以將其作為依賴項注入。

<?php

namespace App\Http\Controllers;

use App\Foo\Contracts;

class FooController extends Controller
{
   public function __construct(protected Foo $foo) {}

    public function index()
    {
        return response()->json([
            'foo' => $this->foo->foo(),
        ]);
    }
}

偽造門面

Laravel 通常提供一種巧妙的方法來輕鬆偽造其外觀,例如事件::假()。我們可以自己實現。

我們所要做的就是創建合約的假實現,然後將假方法添加到我們的外觀中。

<?php

namespace App\Foo;

use App\Foo\Contracts\Foo;

class FakeFooService implements Foo
{
    public function __construct(public Foo $actual) {}

    public function foo(): string
    {
        return 'fake';
    }

    public function fizz(): string
    {
        return 'very fake';
    }
}

在我們的假實作中,我們也建立了對「實際」具體類別的公開引用。

這是我們的門面假實現。您可以看到我們利用了實際的參考。

<?php

namespace App\Foo\Facades;

use App\Foo\FakeFooService;
use Illuminate\Support\Facades\Facade;

/**
* @method static string foo(): string
* @method static string fizz(): string
*/
class Foo extends Facade
{
    public static function fake()
    {
        $actual = static::isFake()
            ? static::getFacadeRoot()->actual
            : static::getFacadeRoot();

        tap(new FakeFooService($actual), function ($fake) {
            static::swap($fake);
        });
    }

   // ...
}

基本測試

現在讓我們寫一個快速測試來存取我們上面創建的控制器範例。

<?php

namespace Tests\Feature;

use App\Foo\Facades\Foo;
use Illuminate\Testing\Fluent\AssertableJson;
use Tests\TestCase;

class FooTest extends TestCase
{
    public function test_foo(): void
    {
        $response = $this->get('/');

        $response->assertJson(fn (AssertableJson $json)
            => $json->where('foo', 'bar'));
    }

    public function test_fake_foo(): void
    {
        Foo::fake();

        $response = $this->get('/');

        $response->assertJson(fn (AssertableJson $json)
            => $json->where('foo', 'fake'));
    }
}

這些測驗沒有用,但它們顯示使用我們的假貨是多麼容易。在 test_fake_foo 中,我們得到 foo=fake,而 test_foo 回傳 foo=bar。

進一步測試

fakes 的有趣之處在於,在我們的 fakes 實作中,我們可以添加額外的方法來測試任何我們可能認為有用的東西。例如,我們可以在 fake 的 foo 方法中設定一個計數器,每次呼叫 foo 時都會遞增。然後我們可以新增一個名為assertFooCount的方法,我們可以在其中斷言該方法被呼叫的次數與我們預期的一樣多。

<?php

namespace App\Foo;

use App\Foo\Contracts\Foo;
use Illuminate\Testing\Assert;

class FakeFooService implements Foo
{
    public int $fooCount = 0;

    public function __construct(public Foo $actual) {}

    public function foo(): string
    {
        $this->fooCount++;

        return 'fake';
    }

    public function fizz(): string
    {
        return 'very fake';
    }

    public function assertFooCount(int $count)
    {
        Assert::assertSame($this->fooCount, $count);
    }
}

如你所見,我們使用 Laravel 的 IlluminateTestingAssert 來進行斷言。那我們的測試就可以是這樣的。

public function test_incrementor(): void
{
    Foo::fake();

    Foo::foo();
    Foo::foo();
    Foo::foo();

    Foo::assertFooCount(3); // pass!
}

就是這樣!

並不是所有東西都需要外觀,但是當您建立內部使用的工具/套件時,外觀通常是值得依賴的強大模式。

這是包含所有程式碼的儲存庫:https://github.com/ClintWinter/laravel-facade-example

以上是在 Laravel 中建立可測試的外觀的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn