Skip to content

Commit c8c38e0

Browse files
authored
增加單元測試 (#1)
* 增加基本測試檔案 * 成功完成基本測試 * 增加 filter * 移除 realpath 功能 * 增加 revert 測試 * 完善覆蓋率 * 移除棄用 * 修正版本
1 parent c6df6f4 commit c8c38e0

File tree

6 files changed

+328
-30
lines changed

6 files changed

+328
-30
lines changed

composer.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
"php": "^7.4|^8.0"
1313
},
1414
"require-dev": {
15-
"orchestra/testbench": "6.x"
15+
"orchestra/testbench": "6.x",
16+
"doctrine/dbal": "^2.12.1"
1617
},
1718
"autoload": {
1819
"psr-4": {
@@ -30,5 +31,13 @@
3031
"A2Workspace\\DatabasePatcher\\ServiceProvider"
3132
]
3233
}
34+
},
35+
"scripts": {
36+
"test": [
37+
"vendor/bin/phpunit"
38+
],
39+
"test-coverage": [
40+
"vendor/bin/phpunit --coverage-html coverage"
41+
]
3342
}
3443
}

phpunit.xml.dist

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,14 @@
1313
stopOnFailure="false"
1414
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
1515
>
16+
<coverage>
17+
<include>
18+
<directory suffix=".php">src</directory>
19+
</include>
20+
</coverage>
1621
<testsuites>
17-
<testsuite name="Unit">
18-
<directory suffix="Test.php">./tests/Unit</directory>
19-
</testsuite>
20-
<testsuite name="Feature">
21-
<directory suffix="Test.php">./tests/Feature</directory>
22+
<testsuite name="Package Test">
23+
<directory suffix="Test.php">./tests/</directory>
2224
</testsuite>
2325
</testsuites>
2426
<php>

src/Commands/DbPatchCommand.php

Lines changed: 47 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@
22

33
namespace A2Workspace\DatabasePatcher\Commands;
44

5+
use Illuminate\Support\Str;
56
use Illuminate\Console\Command;
67
use Illuminate\Support\Collection;
7-
use Illuminate\Support\Str;
8-
use Illuminate\Filesystem\Filesystem;
98
use Symfony\Component\Finder\Finder;
109
use Symfony\Component\Finder\SplFileInfo;
1110

@@ -47,44 +46,32 @@ class DbPatchCommand extends Command
4746
*
4847
* @return int
4948
*/
50-
public function handle()
49+
public function handle(): int
5150
{
5251
$file = $this->determinePatchFile();
5352
if (!$file) {
5453
return 1;
5554
}
5655

5756
$path = $file->getRealPath();
58-
$path = Str::after($path, base_path());
59-
60-
// 這邊我們判斷
61-
// 若為回復模式則呼叫滾回命令並傳入補丁檔案路徑
62-
if ($this->option('revert')) {
63-
$this->info("Running: php artisan migrate:rollback --path={$path}");
6457

65-
$this->call('migrate:rollback', [
66-
'--path' => $path,
67-
]);
58+
if ($this->usingRevertion()) {
59+
return $this->callMigrateCommand($path, 'migrate:rollback');
6860
}
6961

70-
// 呼叫遷移命令並傳入補丁檔案路徑
71-
else {
72-
$this->info("Running: php artisan migrate --path={$path}");
73-
74-
$this->call('migrate', [
75-
'--path' => $path,
76-
]);
77-
}
78-
79-
return 0;
62+
return $this->callMigrateCommand($path);
8063
}
8164

65+
// =========================================================================
66+
// = DeterminePatchFile()
67+
// =========================================================================
68+
8269
/**
8370
* 決定要被使用的檔案
8471
*
8572
* @return \Symfony\Component\Finder\SplFileInfo|null
8673
*/
87-
protected function determinePatchFile()
74+
protected function determinePatchFile(): ?SplFileInfo
8875
{
8976
// 取得 patches 目錄的檔案列表,若結果為空則提前終止
9077
$files = $this->getFileList();
@@ -125,7 +112,7 @@ protected function getFilterInput()
125112
*/
126113
protected function getFileList(): Collection
127114
{
128-
$paths = [database_path('patches')];
115+
$paths = $this->laravel['config']['database.patcher.paths'] ?? database_path('patches');
129116

130117
return collect($paths)
131118
->map(fn ($path) => $this->getFileListInDirectory($path))
@@ -144,6 +131,7 @@ protected function getFileListInDirectory(string $path): array
144131
->filter(function (SplFileInfo $file) {
145132
return !in_array($file->getRelativePathname(), $this->excludedNames);
146133
})
134+
->ignoreDotFiles(true)
147135
->files()
148136
->in($path)
149137
->depth(0)
@@ -178,4 +166,39 @@ protected function choiceFromFileList($question, Collection $files): SplFileInfo
178166
return $value[0] === $input;
179167
})[1];
180168
}
169+
170+
// =========================================================================
171+
// = UsingRevertion()
172+
// =========================================================================
173+
174+
/**
175+
* 判定是否為 revert 模式
176+
*
177+
* @return bool
178+
*/
179+
protected function usingRevertion(): bool
180+
{
181+
return $this->input->hasOption('revert') && $this->option('revert');
182+
}
183+
184+
// =========================================================================
185+
// = CallMigrateCommand()
186+
// =========================================================================
187+
188+
/**
189+
* 呼叫運行 migrate 指令並傳入路徑
190+
*
191+
* @param string $path
192+
* @param string $command
193+
* @return int
194+
*/
195+
protected function callMigrateCommand($path, string $command = 'migrate'): int
196+
{
197+
$path = Str::after($path, base_path());
198+
199+
$this->info("Running: php artisan {$command} --path={$path}");
200+
$this->call($command, ['--path' => $path]);
201+
202+
return 0;
203+
}
181204
}
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
<?php
2+
3+
namespace Tests;
4+
5+
use Illuminate\Testing\PendingCommand;
6+
use Illuminate\Support\Facades\Schema;
7+
use Illuminate\Support\Facades\Artisan;
8+
use Illuminate\Database\Schema\Blueprint;
9+
use Tests\TestCase;
10+
11+
class CallDbPatchArtisanCommandTest extends TestCase
12+
{
13+
use TestArtisanCommandHelpers;
14+
15+
protected static $published;
16+
17+
protected function setUp(): void
18+
{
19+
parent::setUp();
20+
21+
Schema::create('products', function (Blueprint $table) {
22+
$table->increments('id');
23+
$table->string('ian')->unique();
24+
$table->string('name');
25+
});
26+
27+
if (empty(static::$published)) {
28+
Artisan::call('vendor:publish', [
29+
'--tag' => '@a2workspace/laravel-database-patcher',
30+
]);
31+
32+
static::$published = database_path('patches');
33+
}
34+
}
35+
36+
public static function tearDownAfterClass(): void
37+
{
38+
@unlink(static::$published);
39+
@rmdir(static::$published);
40+
}
41+
42+
// =========================================================================
43+
// = Tests
44+
// =========================================================================
45+
46+
public function test_call_artisan_command()
47+
{
48+
// =====================================================================
49+
// = Step 1: Install specified patch file.
50+
// =====================================================================
51+
52+
$command = $this->artisan('db:patch');
53+
54+
$command->expectsChoice(
55+
'選擇補丁檔案',
56+
$this->parseLabel('2022_07_19_000000_add_priority_to_products_table.php'),
57+
[
58+
$this->parseLabel('2022_07_19_000000_add_priority_to_products_table.php')
59+
],
60+
);
61+
62+
$command->expectsOutput(sprintf(
63+
'Running: php artisan migrate --path=%s',
64+
$this->resolvePath('/database/patches/2022_07_19_000000_add_priority_to_products_table.php')
65+
));
66+
67+
$command->assertExitCode(0);
68+
$command->run();
69+
70+
$this->assertDatabaseHas(
71+
'migrations',
72+
['migration' => '2022_07_19_000000_add_priority_to_products_table']
73+
);
74+
75+
$this->assertDatabaseTableHasColumn('products', 'priority');
76+
77+
// =====================================================================
78+
// = Step 2: Revert it.
79+
// =====================================================================
80+
81+
$command2 = $this->artisan('db:patch', ['--revert' => true]);
82+
83+
$command2->expectsChoice(
84+
'選擇補丁檔案',
85+
$this->parseLabel('2022_07_19_000000_add_priority_to_products_table.php'),
86+
[
87+
$this->parseLabel('2022_07_19_000000_add_priority_to_products_table.php')
88+
],
89+
);
90+
91+
$command2->expectsOutput(sprintf(
92+
'Running: php artisan migrate:rollback --path=%s',
93+
$this->resolvePath('/database/patches/2022_07_19_000000_add_priority_to_products_table.php')
94+
));
95+
96+
$command2->assertExitCode(0);
97+
$command2->run();
98+
99+
$this->assertDatabaseTableMissingColumn('products', 'priority');
100+
101+
$this->assertDatabaseMissing(
102+
'migrations',
103+
['migration' => '2022_07_19_000000_add_priority_to_products_table']
104+
);
105+
}
106+
107+
public function test_call_artisan_command_with_filter()
108+
{
109+
$command = $this->artisan('db:patch', [
110+
'filter' => 'product',
111+
]);
112+
113+
$command->expectsChoice(
114+
'選擇補丁檔案',
115+
$this->parseLabel('2022_07_19_000000_add_priority_to_products_table.php'),
116+
[
117+
$this->parseLabel('2022_07_19_000000_add_priority_to_products_table.php')
118+
],
119+
);
120+
121+
$command->expectsOutput(sprintf(
122+
'Running: php artisan migrate --path=%s',
123+
$this->resolvePath('/database/patches/2022_07_19_000000_add_priority_to_products_table.php')
124+
));
125+
126+
$command->assertExitCode(0);
127+
$command->run();
128+
129+
$this->assertDatabaseHas(
130+
'migrations',
131+
['migration' => '2022_07_19_000000_add_priority_to_products_table']
132+
);
133+
134+
$this->assertDatabaseTableHasColumn('products', 'priority');
135+
}
136+
137+
public function test_call_artisan_command_with_filter_then_not_found()
138+
{
139+
$command = $this->artisan('db:patch', [
140+
'filter' => '__IMIFUMEI__',
141+
]);
142+
143+
$command->expectsOutput('找不到符合的補丁檔案');
144+
$command->assertExitCode(1);
145+
$command->run();
146+
}
147+
148+
public function test_call_artisan_command_when_directory_is_empty()
149+
{
150+
$this->app->config->set('database.patcher.paths', []);
151+
152+
$command = $this->artisan('db:patch');
153+
154+
$command->expectsOutput('找不到任何補丁檔案');
155+
$command->assertExitCode(1);
156+
$command->run();
157+
}
158+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
namespace Tests;
4+
5+
use Illuminate\Support\Facades\Schema;
6+
7+
trait TestArtisanCommandHelpers
8+
{
9+
/**
10+
* @param string $option
11+
* @return string
12+
*/
13+
private function parseLabel(string $option): string
14+
{
15+
return "-> {$option}";
16+
}
17+
18+
/**
19+
* @param string $table
20+
* @param string $column
21+
* @return self
22+
*/
23+
private function assertDatabaseTableHasColumn($table, $column)
24+
{
25+
$this->assertTrue(
26+
Schema::hasColumn($table, $column),
27+
sprintf(
28+
'The table [%s] doesn\'t have the column named %s',
29+
$table,
30+
$column
31+
)
32+
);
33+
34+
return $this;
35+
}
36+
37+
/**
38+
* @param string $table
39+
* @param string $column
40+
* @return self
41+
*/
42+
private function assertDatabaseTableMissingColumn($table, $column)
43+
{
44+
$this->assertFalse(
45+
Schema::hasColumn($table, $column),
46+
sprintf(
47+
'The table [%s] have the column named %s',
48+
$table,
49+
$column
50+
)
51+
);
52+
53+
return $this;
54+
}
55+
}

0 commit comments

Comments
 (0)