免責宣告: 我只是檢視了完整的基於 Laravel 5.3+ 的 Laravel 專案(不包括依賴包)
1. Laravel.io portal
最近重新啟動的 Laravel.io 已經將程式碼在 GitHub 上開源。Dries Vints 在這個專案中寫的測試非常好。
Laravel.io 使用功能測試 (Feature testing) 和元件測試 (Component testing)(和單元測試差不多)。有趣的是,在這兩種測試中都進行了相同或相似的測試。
例子 1 -- tests/Feature/ReplyTest.php
public function users_can_add_a_reply_to_a_thread()
{
factory(Thread::class)->create(['subject' => 'The first thread', 'slug' => 'the-first-thread']);
$this->login();
$this->visit('/forum/the-first-thread')
->type('The first reply', 'body')
->press('Reply')
->see('The first thread')
->see('The first reply')
->see('Reply successfully added!');
}
複製程式碼
例子 2 -- tests/Components/Jobs/CreateReplyTest.php
public function we_can_create_a_reply()
{
$job = new CreateReply('Foo', '', $this->createUser(), factory(Thread::class)->create());
$this->assertInstanceOf(Reply::class, $job->handle());
}
複製程式碼
這樣做很好: 同時測試 Jobs 層和實際在瀏覽器中點選一些東西。
我還注意到 Laravel.io 已經升級到了 Laravel 5.4, 但是測試套件仍然使用的是5.3的風格, 使用 BrowserKitTestCase implementation。 這沒有什麼問題,僅僅是一個提醒。
這個專案也使用了 Travis 進行持續整合, 後來我發現大多數專案都使用了它。
2. Cachet —— 一個開源狀態頁面系統
在 James Brooks 和 Graham Campbell 的帶領下,這個專案有一個龐大的測試元件。他甚至通過觀察表層很難理解。
所以,我們從哪裡開始... 事實上,我甚至不會深度燕郊這個專案的測試邏輯, 因為他太難理解了,這是一個例子 —— tests/Models/ComponentTest.php:
use AltThree\TestBench\ValidationTrait;
use CachetHQ\Cachet\Models\Component;
use CachetHQ\Tests\Cachet\AbstractTestCase;
class ComponentTest extends AbstractTestCase
{
use ValidationTrait;
public function testValidation()
{
$this->checkRules(new Component());
}
}
複製程式碼
好吧,這裡用到了 ValidationTrait,然後是一些 AbstractTestCase。同時這段邏輯是所有的測試 —— 一些抽象的 "魔術" 正在執行所有的工作。
我不是說這是壞事 —— 十分確定他在內在的東西里工作的很好。他只是不容易先學習和遵循。但如果有人想深入研究 —— 祝好運!
3. October CMS
市場上第一款基於 Laravel 的 CMS,他擁有非常不錯的測試元件。
首先 -—— tests 資料夾有一個 真正資訊詳實的 readme.md 檔案,專門用於測試過程。
October CMS 的所有測試包括:
- 單元測試
- 功能測試
- 外掛測試
每個 "區域" 都有對應的基類來擴充套件 —— 有 TestCase,UiTestCase 和 PluginTestCase。
邏輯也非常複雜和抽象 —— 這裡有一個例子 tests/unit/backend/models/ExportModelTest.php:
class ExportModelTest extends TestCase
{
//
// 輔助
//
protected static function callProtectedMethod($object, $name, $params = [])
{
$className = get_class($object);
$class = new ReflectionClass($className);
$method = $class->getMethod($name);
$method->setAccessible(true);
return $method->invokeArgs($object, $params);
}
//
// 測試
//
public function testEncodeArrayValue()
{
$model = new ExampleExportModel;
$data = ['foo', 'bar'];
$result = self::callProtectedMethod($model, 'encodeArrayValue', [$data]);
$this->assertEquals('foo|bar', $result);
$data = ['dps | heals | tank', 'paladin', 'berserker', 'gunner'];
$result = self::callProtectedMethod($model, 'encodeArrayValue', [$data]);
$this->assertEquals('dps \| heals \| tank|paladin|berserker|gunner', $result);
$data = ['art direction', 'roman empire', 'sci-fi'];
$result = self::callProtectedMethod($model, 'encodeArrayValue', [$data, '-']);
$this->assertEquals('art direction-roman empire-sci\-fi', $result);
}
}
複製程式碼
如你所見,這裡有一個靜態輔助方法(順便說一下,在其他類中重複使用),然後獲取類/方法並呼叫他啊, 我確信作者能立即理解邏輯,但這對外人來說很困難。
同樣有趣的是,OctoberCMS 使用 Selenium 來獲取一些功能:tests/readme.md 檔案提到了設定文件。
4. Orgmanager —— GitHub 組織的邀請系統
這是 Miguel Piedrafita 的一個非常簡單的專案,Orgmanager 的測試也是非常簡單易懂的。還分為單元,功能和 API 測試。
我在這裡看到一個有趣的示例 —— 從測試中呼叫 Artisan 命令,例如 unit/JoinTest.php:
public function testJoinCommand()
{
$user = factory(User::class)->create();
$org = factory(Org::class)->create([
'userid' => $user->id,
]);
Github::shouldReceive('authenticate')
->once()
->with($org->user->token, null, 'http_token')
->andReturn();
Artisan::call('orgmanager:joinorg', [
'org' => $org->id,
'username' => $user->github_username,
]);
$this->assertEquals($user->github_username.' was invited to '.$org->name."\n", Artisan::output());
}
複製程式碼
呼叫 artisan 命令並斷言其輸出 —— 非常有趣。我確定他有效,但這是非標準的方式。
5. PHPMap
由 Florian Wartner 建立及維護。
PHPMap 有一個測試元件,使人聯想到 Laracasts 或 測試驅動 Laravel 課程 講述的標準。這是 Feature/FavoritesTest.php 的例子。
public function guests_can_not_favorite_anything()
{
$this->withExceptionHandling()
->post('forum/replies/1/favorites')
->assertRedirect('/login');
}
public function an_authenticated_user_can_favorite_any_reply()
{
$this->signIn();
$reply = create('App\Models\Forum\Reply');
$this->post('forum/replies/'.$reply->id.'/forum/favorites');
$this->assertCount(1, $reply->favorites);
}
複製程式碼
PHPMap 的測試分為單元,功能及 Laravel Dusk 等等!最後我發現了一個真正在生產環境使用 Dusk 的專案。這是他的門面 —— tests/Browser/MapTest.php:
public function testMap()
{
$this->browse(function ($browser) {
$browser->visit('/map')
->assertSee('PHPMap');
});
}
複製程式碼
6. Timegrid —— 免費,開源,線上操作平臺
Timegrid 的最大貢獻者是 Ariel Vallese,同時他在測試方面做了非常好的工作。
這裡只有很多的測試: 單元,驗收和整合,每個檔案都有更深的子資料夾目錄,例如:—— acceptance/scenarios/consulting/ConsultingScenarioTest.php:
public function it_fits_for_consulting_scenario()
{
$this->arrangeScenario();
$this->the_business_publishes_a_consulting_service();
$this->the_business_publishes_vacancies();
$this->a_user_subscribes_to_business();
$this->the_user_queries_vacancies();
$this->it_provides_available_times_for_requested_service_date();
$this->the_user_takes_a_reservation();
$this->the_user_sees_the_reservation_ticket();
}
public function the_business_publishes_a_consulting_service()
{
$this->service = $this->makeService([
'name' => 'OnSite 4hs Support',
'duration' => 60 * 4,
]);
$this->actingAs($this->owner);
$this->call('POST', route('manager.business.service.store', $this->business), $this->service->toArray());
$this->assertCount(1, $this->business->fresh()->services);
}
複製程式碼
一個一體化的方法,之後是一個個列舉更多的測試:
倉庫中的官方統計資料看起來非常好: 89% 的測試覆蓋率。
最後,有趣的是,作者甚至測試了遷移檔案,如 tests/unit/migration/MigrationTest.php:
public function it_refreshes_rollbacks_and_seeds_the_database()
{
$database = env('DB_CONNECTION');
$this->assertNotNull($database);
$exitCode = Artisan::call('migrate:refresh', ['--database' => $database]);
$this->assertEquals(0, $exitCode);
$exitCode = Artisan::call('migrate:rollback', ['--database' => $database]);
$this->assertEquals(0, $exitCode);
$exitCode = Artisan::call('migrate', ['--database' => $database]);
$this->assertEquals(0, $exitCode);
$exitCode = Artisan::call('db:seed', ['--database' => $database]);
$this->assertEquals(0, $exitCode);
}
複製程式碼
在測試中使用 Artisan 命令或許不是最佳的設計模式,但他只是測試任何 web 應用中最重要的功能之一。
總體結論
在看過所有這些不同的專案之後(以及由於各種原因未提及的),以下是我對自己關於測試的主要要求:
- 不在 單元 "或" 功能 中做選擇, —— 大多數專案兩者兼具,或者更多型別的測試;
- 大多數專案使用持續整合(通常是 Travis)和測試元件 —— 否則,為什麼反感寫測試呢?
- 這裡有非常多的不同方式構建測試 —— 這完全取決於專案,這裡沒有“高招”;
- 還有很多方法對內部測試功能分組 —— 輔助方法,抽象類,種子資料等。沒有具體規則,找準適用於你的內容。
- 遷移到較新版本的 Laravel 可能很痛苦 —— 例如,5.3 版本的測試看上去和 5.4 版本不一樣。所以你需要提前考慮更新。
- 從不同角度考慮 —— 當你的專案成長起來,你將不得不回看及修改/新增測試。在這些專案中,我”預感“有一些遺留程式碼,只是因為有些測試將不在被使用。
以上是我的經驗,有沒有你要新增到開源專案列表中來學習測試的內容?