摘要:在中可以在設置數據庫重裝操作這樣就可以在每一個中定義本次污染的數據表,保證下一個在運行前重刷下被污染的數據表,如這樣會極大提高數據庫測試效率,不推薦使用給出的和,效率并不高。
本文主要探討寫數據庫測試。
寫laravel程序時,除了寫生產代碼,還需要寫測試代碼。其中,寫數據庫測試比較麻煩,因為需要針對每一個test case需要建立好數據集,該次test case污染的數據表還需要恢復現場,避免影響下一個test case運行,同時還得保證性能問題,否則隨著程序不斷膨脹,測試數量也越多,那每一次測試運行需要花費大量時間。
有兩個比較好的方法可以提高數據庫測試性能:
對大量的tests按照功能分組。如有1000個tests,可以按照業務功能分組,如group1:1-200, group2:201-800, group3: 801-1000。這樣可以并發運行每組測試包裹。
只恢復每個test case污染的表,而不需要把所有的數據表重新恢復,否則表數量越多測試代碼執行越慢。
這里聊下方法2的具體做法。
假設程序有50張表,每次運行測試時首先需要為每組構建好獨立的對應數據庫,然后創建數據表,最后就是填充測試數據(fixtures)。fixtures可用yml格式定義,既直觀也方便維護,如:
#simple.yml accounts: - id: 1 person_id: 2 type: investment is_included: true - id: 2 person_id: 2 type: investment is_included: true transactions: - account_id: 1 posted_date: "2017-01-01" amount: 10000 transaction_category_id: 1 - account_id: 2 posted_date: "2017-01-02" amount: 10001 transaction_category_id: 2
然后需要寫個yamlSeeder class來把數據集填充到臨時數據庫里:
abstract class YamlSeeder extends IlluminateDatabaseSeeder { private $files; public function __construct(array $files) { $this->files = $files } public function run(array $tables = []): void { // Close unique and foreign key constraint $db = $this->container["db"]; $db->statement("SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;"); $db->statement("SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;"); foreach($this->files as $file) { ... // Convert yaml data to array $fixtures = SymfonyComponentYamlYaml::parse(file_get_contents($file)); ... foreach($fixtures as $table => $data) { // Only seed specified tables, it is important!!! if ($tables && !in_array($table, $tables, true)) { continue; } $db->table($table)->truncate(); if (!$db->table($table)->insert($data)) { throw new RuntimeException("xxx"); } } ... } // Open unique and foreign key constraint $db->statement("SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;"); $db->statement("SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;"); } } class SimpleYamlSeeder extends YamlSeeder { public function __construct() { parent::__construct([database.path("seeds/simple.yml")]); } }
上面的代碼有一個關鍵處是參數$tables:如果參數是空數組,就把所有數據表數據插入隨機數據庫里;如果是指定的數據表,只重刷指定的數據表。這樣會很大提高數據庫測試的性能,因為可以在每一個test case里只需要指定本次測試所污染的數據表。在tests/TestCase.php中可以在setUp()設置數據庫重裝操作:
abstract class TestCase extends IlluminateFoundationTestingTestCase { protected static $tablesToReseed = []; public function seed($class = "DatabaseSeeder", array $tables = []): void { $this->artisan("db:seed", ["--class" => $class, "--tables" => implode(",", $tables)]); } protected function reseed(): void { // TEST_SEEDERS is defined in phpunit.xml, e.g.$seeders = env("TEST_SEEDERS") ? explode(",", env("TEST_SEEDERS")) : []; if ($seeders && is_array(static::$tablesToReseed)) { foreach ($seeders as $seeder) { $this->seed($seeder, static::$tablesToReseed); } } Cache::flush(); static::$tablesToReseed = false; } protected static function reseedInNextTest(array $tables = []): void { static::$tablesToReseed = $tables; } }
這樣就可以在每一個test case中定義本次污染的數據表,保證下一個test case在運行前重刷下被污染的數據表,如:
final class AccountControllerTest extends TestCase { ... public function testUpdateAccount() { static::reseedInNextTest([Account::class, Transaction::class]); ... } }
這樣會極大提高數據庫測試效率,不推薦使用Laravel給出的IlluminateFoundationTestingDatabaseMigrations 和 IlluminateFoundationTestingDatabaseTransactions,效率并不高。
laravel的db:seed命令沒有--tables這個options,所以需要擴展IlluminateDatabaseConsoleSeedsSeedCommand:
class SeedCommand extends IlluminateDatabaseConsoleSeedsSeedCommand { public function fire() { if (!$this->confirmToProceed()) { return; } $this->resolver->setDefaultConnection($this->getDatabase()); Model::unguarded(function () { $this->getSeeder()->run($this->getTables()); }); } protected function getTables() { $tables = $this->input->getOption("tables"); return $tables ? explode(",", $tables) : []; } protected function getOptions() { $options = parent::getOptions(); $options[] = ["tables", null, InputOption::VALUE_OPTIONAL, "A comma-separated list of tables to seed, all if left empty"]; return $options; } }
當然還得寫SeedServiceProvider()來覆蓋原有的IlluminateFoundationProvidersConsoleSupportServiceProvider::registerSeedCommand()中注冊的command.seed,然后在config/app.php中注冊:
class SeedServiceProvider extends ServiceProvider { /** * Indicates if loading of the provider is deferred. * * @var bool */ protected $defer = true; /** * @see IlluminateDatabaseSeedServiceProvider::registerSeedCommand() */ public function register() { $this->app->singleton("command.seed", function ($app) { return new SeedCommand($app["db"]); }); $this->commands("command.seed"); } public function provides() { return ["command.seed"]; } }
OK,這樣所有的工作都做完了。。以后寫數據庫測試性能會提高很多,大量的test case可以在短時間內運行完畢。
最后,寫測試代碼是必須的,好處非常多,隨著項目程序越來越大,就會深深感覺到寫測試是必須的,一勞永逸,值得花時間投資。也是作為一名軟件工程師的必備要求。
RightCapital招聘Laravel DevOps
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/23105.html
摘要:本文主要探討數據庫測試。在寫測試代碼一中聊了關于如何提高數據庫測試性能,其實簡單一句就是每一個只重新被污染的表。最后還得在中創建用戶并授權,以用戶登錄這樣就臨時測試數據庫就準備完畢了,然后就是測試數據,執行,執行等等,可以參考寫測試代碼一。 本文主要探討數據庫測試。 在寫Laravel測試代碼(一) 中聊了關于如何提高 laravel 數據庫測試性能,其實簡單一句就是:每一個test ...
摘要:寫一個,的是,的內容參照寫測試代碼三,然后寫上很明顯,這里測試的是,即和,是一個自定義的,主要功能就是實現了全部,并保存在文件里作為。 本文主要探討寫laravel integration/functional test cases時候,如何assert。前面幾篇文章主要聊了如何reseed測試數據,mock數據,本篇主要聊下assert的可行實踐,盡管laravel官方文檔聊了Tes...
摘要:用也有三四個月了,雖然是兼職開發,但是使用的頻率非常之高,畢竟是產品化的一個項目。第二階段數據庫和開發了比較多的功能之后,會發現需要大量的測試數據,這時候和就該大顯身手了。 用Laravel也有三四個月了,雖然是兼職開發,但是使用的頻率非常之高,畢竟是產品化的一個項目。在這期間,也踩了無數的坑,走了很多彎路,所以準備把最近的感悟記錄下來,方便后來者。 第一階段:簡單的增刪改查 這是最...
摘要:如何在實戰中能應用上設計模式,我思考了接近兩年。最開始我接觸設計模式,出發點就是為了重構代碼,以便更好的復用和測試。也就是說基本是被當成一組靜態函數使用的。 MVC是Laravel自帶的,大家也多少都會用一些。今天我們不談MVC,談一些大中型項目的設計思路。 前言 雖然標題說是設計模式,但是我并不打算去講什么singleton、strategy、factory,不知道為什么,每次看到...
摘要:本文首發于作者這是一篇基礎教程,對標文檔中的數據遷移和數據填充。那么,中的數據庫遷移概念,就是用于解決團隊中保證數據庫結構一致的方案。和不同,如果多次執行就會進行多次數據填充。好了,數據遷移和數據填充的基本操作也就這些了。 showImg(https://segmentfault.com/img/remote/1460000012252769?w=648&h=422); 本文首發于 h...
閱讀 2000·2023-04-25 16:53
閱讀 1442·2021-10-13 09:39
閱讀 606·2021-09-08 09:35
閱讀 1639·2019-08-30 13:03
閱讀 2121·2019-08-30 11:06
閱讀 1831·2019-08-30 10:59
閱讀 3188·2019-08-29 17:00
閱讀 2288·2019-08-23 17:55