主控台測試
簡介
在 Laravel 中除了能簡單地測試 HTTP 外,Laravel 還提供了一個簡單的 API 來測試專案的自訂主控台指令。
預期成功 / 失敗
若要開始,讓我們先來看看如何針對 Artisan 指令的結束代碼 (Exit Code) 作 Assertion (判斷提示)。為此,我們將使用 artisan
方法來在測試中叫用 Artisan 指令。接著,我們會使用 assertExitCode
方法來判斷該指令是否以給定的結束代碼完成:
1test('console command', function () {2 $this->artisan('inspire')->assertExitCode(0);3});
1test('console command', function () {2 $this->artisan('inspire')->assertExitCode(0);3});
1/**2 * Test a console command.3 */4public function test_console_command(): void5{6 $this->artisan('inspire')->assertExitCode(0);7}
1/**2 * Test a console command.3 */4public function test_console_command(): void5{6 $this->artisan('inspire')->assertExitCode(0);7}
可以使用 assertNotExitCode
方法來判斷該指令是否不以給定結束代碼終止:
1$this->artisan('inspire')->assertNotExitCode(1);
1$this->artisan('inspire')->assertNotExitCode(1);
當然,一般來說,所有以狀態碼 0
結束的終端機指令通常都代表成功,而非 0 的結束代碼則代表不成功。因此,為了方便起見,我們可以使用 assertSuccessful
與 assertFailed
Assertion 來判斷給定的指令是否以成功結束碼退出:
1$this->artisan('inspire')->assertSuccessful();23$this->artisan('inspire')->assertFailed();
1$this->artisan('inspire')->assertSuccessful();23$this->artisan('inspire')->assertFailed();
預期的輸入/輸出
Laravel 能讓你輕鬆地通過 expectsQuestion
方法來為主控台指令「模擬(Mock)」使用者輸入。此外,還可以通過 assertExitCode
與 expectsOutput
來指定主控台預期的結束代碼與輸出文字。舉例來說,假設有下列主控台指令:
1Artisan::command('question', function () {2 $name = $this->ask('What is your name?');34 $language = $this->choice('Which language do you prefer?', [5 'PHP',6 'Ruby',7 'Python',8 ]);910 $this->line('Your name is '.$name.' and you prefer '.$language.'.');11});
1Artisan::command('question', function () {2 $name = $this->ask('What is your name?');34 $language = $this->choice('Which language do you prefer?', [5 'PHP',6 'Ruby',7 'Python',8 ]);910 $this->line('Your name is '.$name.' and you prefer '.$language.'.');11});
You may test this command with the following test:
1test('console command', function () {2 $this->artisan('question')3 ->expectsQuestion('What is your name?', 'Taylor Otwell')4 ->expectsQuestion('Which language do you prefer?', 'PHP')5 ->expectsOutput('Your name is Taylor Otwell and you prefer PHP.')6 ->doesntExpectOutput('Your name is Taylor Otwell and you prefer Ruby.')7 ->assertExitCode(0);8});
1test('console command', function () {2 $this->artisan('question')3 ->expectsQuestion('What is your name?', 'Taylor Otwell')4 ->expectsQuestion('Which language do you prefer?', 'PHP')5 ->expectsOutput('Your name is Taylor Otwell and you prefer PHP.')6 ->doesntExpectOutput('Your name is Taylor Otwell and you prefer Ruby.')7 ->assertExitCode(0);8});
1/**2 * Test a console command.3 */4public function test_console_command(): void5{6 $this->artisan('question')7 ->expectsQuestion('What is your name?', 'Taylor Otwell')8 ->expectsQuestion('Which language do you prefer?', 'PHP')9 ->expectsOutput('Your name is Taylor Otwell and you prefer PHP.')10 ->doesntExpectOutput('Your name is Taylor Otwell and you prefer Ruby.')11 ->assertExitCode(0);12}
1/**2 * Test a console command.3 */4public function test_console_command(): void5{6 $this->artisan('question')7 ->expectsQuestion('What is your name?', 'Taylor Otwell')8 ->expectsQuestion('Which language do you prefer?', 'PHP')9 ->expectsOutput('Your name is Taylor Otwell and you prefer PHP.')10 ->doesntExpectOutput('Your name is Taylor Otwell and you prefer Ruby.')11 ->assertExitCode(0);12}
If you are utilizing the search
or multisearch
functions provided by Laravel Prompts, you may use the expectsSearch
assertion to mock the user's input, search results, and selection:
1test('console command', function () {2 $this->artisan('example')3 ->expectsSearch('What is your name?', search: 'Tay', answers: [4 'Taylor Otwell',5 'Taylor Swift',6 'Darian Taylor'7 ], answer: 'Taylor Otwell')8 ->assertExitCode(0);9});
1test('console command', function () {2 $this->artisan('example')3 ->expectsSearch('What is your name?', search: 'Tay', answers: [4 'Taylor Otwell',5 'Taylor Swift',6 'Darian Taylor'7 ], answer: 'Taylor Otwell')8 ->assertExitCode(0);9});
1/**2 * Test a console command.3 */4public function test_console_command(): void5{6 $this->artisan('example')7 ->expectsSearch('What is your name?', search: 'Tay', answers: [8 'Taylor Otwell',9 'Taylor Swift',10 'Darian Taylor'11 ], answer: 'Taylor Otwell')12 ->assertExitCode(0);13}
1/**2 * Test a console command.3 */4public function test_console_command(): void5{6 $this->artisan('example')7 ->expectsSearch('What is your name?', search: 'Tay', answers: [8 'Taylor Otwell',9 'Taylor Swift',10 'Darian Taylor'11 ], answer: 'Taylor Otwell')12 ->assertExitCode(0);13}
You may also assert that a console command does not generate any output using the doesntExpectOutput
method:
1test('console command', function () {2 $this->artisan('example')3 ->doesntExpectOutput()4 ->assertExitCode(0);5});
1test('console command', function () {2 $this->artisan('example')3 ->doesntExpectOutput()4 ->assertExitCode(0);5});
1/**2 * Test a console command.3 */4public function test_console_command(): void5{6 $this->artisan('example')7 ->doesntExpectOutput()8 ->assertExitCode(0);9}
1/**2 * Test a console command.3 */4public function test_console_command(): void5{6 $this->artisan('example')7 ->doesntExpectOutput()8 ->assertExitCode(0);9}
The expectsOutputToContain
and doesntExpectOutputToContain
methods may be used to make assertions against a portion of the output:
1test('console command', function () {2 $this->artisan('example')3 ->expectsOutputToContain('Taylor')4 ->assertExitCode(0);5});
1test('console command', function () {2 $this->artisan('example')3 ->expectsOutputToContain('Taylor')4 ->assertExitCode(0);5});
1/**2 * Test a console command.3 */4public function test_console_command(): void5{6 $this->artisan('example')7 ->expectsOutputToContain('Taylor')8 ->assertExitCode(0);9}
1/**2 * Test a console command.3 */4public function test_console_command(): void5{6 $this->artisan('example')7 ->expectsOutputToContain('Taylor')8 ->assertExitCode(0);9}
預期確認
當撰寫預期答案為「yes」或「no」確認的指令時,可以使用 expectsConfirmation
方法:
1$this->artisan('module:import')2 ->expectsConfirmation('Do you really wish to run this command?', 'no')3 ->assertExitCode(1);
1$this->artisan('module:import')2 ->expectsConfirmation('Do you really wish to run this command?', 'no')3 ->assertExitCode(1);
預期表格
若指令使用 Artisan 的 table
方法來以表格顯示資訊,則要為整個表格撰寫預期的輸出可能很麻煩。因此可以改用 expectsTable
方法來代替。該方法接收表格的標頭作為其第一個引數,以及表格的資料作為其第二引數:
1$this->artisan('users:all')2 ->expectsTable([3 'ID',4 'Email',5 ], [8 ]);
1$this->artisan('users:all')2 ->expectsTable([3 'ID',4 'Email',5 ], [8 ]);
Console 事件
預設情況下,在執行測試時,不會分派 Illuminate\Console\Events\CommandStarting
與 Illuminate\Console\Events\CommandFinished
事件。不過,只要在測試類別中加入 Illuminate\Foundation\Testing\WithConsoleEvents
Trait,就可以啟用這些測試:
1<?php23use Illuminate\Foundation\Testing\WithConsoleEvents;45uses(WithConsoleEvents::class);67// ...
1<?php23use Illuminate\Foundation\Testing\WithConsoleEvents;45uses(WithConsoleEvents::class);67// ...
1<?php23namespace Tests\Feature;45use Illuminate\Foundation\Testing\WithConsoleEvents;6use Tests\TestCase;78class ConsoleEventTest extends TestCase9{10 use WithConsoleEvents;1112 // ...13}
1<?php23namespace Tests\Feature;45use Illuminate\Foundation\Testing\WithConsoleEvents;6use Tests\TestCase;78class ConsoleEventTest extends TestCase9{10 use WithConsoleEvents;1112 // ...13}