如何在 PHP 中測試 Trait
在使用 Laravel Livewire 的時候,我時常會把 livewire component 中可以重複使用的邏輯或是方法抽出並放在 Trait 中。
Laravel Livewire 官方文件中也建議使用 Trait 來處理經常重複使用的邏輯。詳細可以參考下面兩個連結。
例如我有一個將 Markdown 轉換為 HTML 的方法,因為有很多 livewire component 都會使用到這個方法,所以我將這個方法放到 trait 中。
<?php
namespace App\Http\Traits\Livewire;
use Illuminate\Support\Str;
trait MarkdownConverter
{
public function convertToHtml(string $body): string
{
$html = Str::of($body)->markdown([
'html_input' => 'strip',
]);
}
}
當我想在 livewire component 中使用這個方法時,只要簡單的使用 use
引入即可。
<?php
namespace App\Http\Livewire\Comments;
// ...
use App\Http\Traits\Livewire\MarkdownConverter;
use Livewire\Component;
class Comment extends Component
{
use MarkdownConverter;
// ...
public function getConvertedBodyProperty(): string
{
return $this->convertToHtml($this->body);
}
// ...
}
Trait 用多了之後也讓我遇到一個問題。
那如果我想測試 trait 中的方法,可以怎麼做呢?
以下的測試範例皆使用 Pest,是一個建構於 PHPUnit 上的測試框架。
在測試中使用 use 引入
我們可以在測試中使用 use
將 Trait 引入,這樣就可以直接使用 Trait 中的方法。
use App\Http\Traits\Livewire\MarkdownConverter;
// 使用 use 引入 trait
uses(MarkdownConverter::class);
it('can convert the markdown content to html', function () {
$body = '# Header 1'
// 引入後 trait 就可以在測試中使用 convertToHtml() 方法
$convertedBody = $this->convertToHtml($body);
expect($convertedBody)
->toContain('<h1>Header 1</h1>')
});
使用匿名類別
使用 PHP 的匿名類別,簡單的建立一個實例,並在實例中使用 use
引入 Trait,在之後的測試中,我們就可以使用實例來呼叫 trait 中的方法。
use App\Http\Traits\Livewire\MarkdownConverter;
it('can block the header tag', function () {
// 使用匿名類別建立一個引入 trait 的實例
$trait = new class
{
use MarkdownConverter;
};
$body = '# Header 1'
$convertedBody = $trait->convertToHtml($body);
expect($convertedBody)
->toContain('<h1>Header 1</h1>')
});
使用 PHPUnit 的 getObjectForTrait 方法
與剛剛的方法類似,我們可以使用 PHPUnit 提供 getObjectForTrait()
方法來取得引入 trait 後的實例。
use App\Http\Traits\Livewire\MarkdownConverter;
it('can block the header tag', function () {
// 透過 PHPUnit 的 getObjectForTrait() 方法取得引入 trait 的實例
$trait = $this->getObjectForTrait(MarkdownConverter::class);
$body = '# Header 1'
$convertedBody = $trait->convertToHtml($body);
expect($convertedBody)
->toContain('<h1>Header 1</h1>')
});