這裡有一份備忘單,介紹如何透過添加依賴項注入、外觀以及輕鬆交換偽造的方法來使簡單的服務類更有用。
骨架很簡單:
這是我們最初的服務類別(很抱歉沒有一個令人信服的例子,但實際上沒有必要為此設計一個)。
<?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中文網其他相關文章!