昨天在把 View 轉換成 Blade 時,會遇到一個重大的難題:我們沒有假資料建立方法可以方便地做自動化測試。
今天會來建立 Eloquent,後面測試如果需要假資料就會比較容易。
建立 entity
首先 Eloquent 的設計是一個資料表,配一個 entity class,設計如下:
namespace App;
use Illuminate\Database\Eloquent\Model;
class Order extends Model { protected $table = 'order';
public $timestamps = false; }
class Product extends Model { protected $table = 'product';
public $timestamps = false;
public function productCategory() { return $this->belongsTo(ProductCategory::class, 'category', 'id'); } }
class ProductCategory extends Model { protected $table = 'product_category';
public $timestamps = false;
public function products() { return $this->hasMany(Product::class, 'category', 'id'); } }
|
接著把產生假資料的方法寫到 database/factories/ProductFactory.php
:
use Faker\Generator as Faker;
$factory->define(App\Product::class, function (Faker $faker) { return [ ]; });
$factory->define(App\ProductCategory::class, function (Faker $faker) { return [ ]; });
$factory->define(App\Order::class, function (Faker $faker) { return [ ]; });
|
測試如果一直建假資料的話,資料庫有可能會爆炸。但在爆炸之前,更為麻煩的問題是,因為資料庫會一直保存狀態,所以每次測試的狀態都無法確定,在這狀況下測試的結果就有可能失真。最常見也最困擾的狀況是:有時候測試會過,有時候不會過。
對於這個問題,Laravel 有提供兩種資料庫還原的方法,一個是 DatabaseMigrations
,另一種是 DatabaseTransactions
。
DatabaseMigrations
原理比較單純,它是執行測試前,會先 rollback
,再重新 migrate
。而 DatabaseTransactions
則是利用執行 beginTransaction()
時做測試,而在測試結束後 rollback()
還原資料庫內容。
很明顯地,DatabaseTransactions
肯定比較快,但它有先天上的限制:必須要同一條 connection 才有辦法取得測試狀態(transaction 狀態)的資料。但因為 Laravel 與原本專案硬幹的 SQL Builder 是使用不同的 connection,所以只能選擇 DatabaseMigrations
。
實際撰寫的測試程式如下:
namespace Tests\Unit;
use App\ProductCategory; use App\Shop\Shop; use Illuminate\Foundation\Testing\DatabaseMigrations; use Tests\TestCase;
class ShopTest extends TestCase { use DatabaseMigrations;
public function shouldGetAllCategoryWhenSeedFactoryCategory() { $excepted = factory(ProductCategory::class)->create();
$target = new Shop(true);
$actual = $target->allCategory();
$this->assertEquals($excepted->id, $actual[1]['id']); $this->assertSame($excepted->title, $actual[1]['title']); } }
|
Browser 的測試範例如下:
use App\ProductCategory; use Illuminate\Foundation\Testing\DatabaseMigrations; use Laravel\Dusk\Browser; use Tests\DuskTestCase;
class ExampleTest extends DuskTestCase { use DatabaseMigrations;
public function shouldSeeNewCategoryWhenCreateNewCategory() { $category = factory(ProductCategory::class)->create();
$this->browse(function (Browser $browser) use ($category) { $browser->visit('/') ->assertSee('產品分類') ->assertSee('未分類') ->assertSee($category->title); }); } }
|
也因為可以產生假資料,很多底層的單元測試都能開始寫了,如 Shop::oneCategory()
:
public function shouldGetOneCategoryWhenSeedFactoryCategory() { $excepted = factory(ProductCategory::class)->create();
$target = new Shop(true);
$actual = $target->oneCategory($excepted->id);
$this->assertEquals($excepted->id, $actual['id']); $this->assertSame($excepted->title, $actual['title']); }
|
今天寫的單元測試將會在明天派得上用場,請大家先看 GitHub PR 吧!
參考資料