発生事象
Laravel用のパッケージを書いていて、コマンドを追加したのでテストしてみるぞ~、と思ったら、以下のようなエラーメッセージが出た。
//テストはだいたいこんな感じ
class SampleTest extends \Orchestra\Testbench\TestCase {
public function test_command(){
$this->artisan('nice:easy')->assertExitCode(10);
}
}
Symfony\Component\Console\Exception\CommandNotFoundException: The command "nice:easy" does not exist.
コマンドを認識していないようだ。
結論
ココを見ましょう。
https://orchestraplatform.readme.io/docs/testbench#section-custom-service-providers
https://orchestraplatform.readme.io/docs/testbench
To load your package service provider, override the getPackageProviders()
おまえのパッケージのサービスプロバイダをロードするには、getPackageProviders() をオーバーライドせい、ということで、解決。
補足
コマンド登録はパッケージのサービスプロバイダに書く
Artisanコマンドを提供するようなLaravel用パッケージを作成する場合、パッケージ側にサービスプロバイダを用意して、「この辺のクラスをArtisanコマンドに追加せよ」という情報を、利用側のLaravel本体に知らせる必要があります。
=>このへんの話
https://laravel.com/docs/8.x/packages#commands
https://readouble.com/laravel/8.x/ja/packages.html#commands
パッケージ開発のテストには orchestral/testbench を使う
Laravel用パッケージを開発する場合、Testには
https://github.com/orchestral/testbench
を使いましょう。
=>このへんの話
https://readouble.com/laravel/8.x/ja/packages.html#a-note-on-facades
FeatureTestですよ、という話
そもそもの話として、Artisanコマンド(Illuminate\Console\Command を継承したクラス)に対してUnitTestを書こうとすると、色々と面倒なことになる(何が面倒なのか書くのもめんどい)。
Artisanコマンドを書く場合は、責任の範囲を画面との入出力のみに限定し、それ以外の処理に関しては適切に移譲するように注意しする。
端的には、Illuminate\Console\Command を継承して作成するクラスは、ViewとControllerに相当するもの、と扱っておけばよい。(なので、Artisanコマンドに対するテストは、UnitTestでははなくFeatureTestです。)
Commandのコンストラクタをオーバライドするなよ、という話
ちなみに、Illuminate\Console\Command を継承してクラスを作成する場合は コンストラクタをオーバーライドしてはいけない。
理由はこちら https://qiita.com/k-kurikuri/items/cec750ec3d21c6319253
移譲先のオブジェクト生成は、handle()メソッドの先頭で行うようにしておくのが良いと思う。
class TemplateList extends Command
{
/** @var string $signature */
protected $signature = 'big-na-otoko:do-it';
/** @var string $description */
protected $description = '将来なにかデカイことをやるコマンドです';
/** @var NiceService $service */
private $service;
/**
* @return void
*/
public function handle()
{
try {
$this->init();
} catch (\Throwable $e) {
$this->error($e->getMessage());
return 1;
}
//以下に、入出力に関する処理とサービスの呼び出しなどを書いて適宜実装していく
}
/**
* @return void
*/
private function init(): void
{
$this->service = App::make(NiceService ::class);
}
}
こんなノリで書いておけば、だいたい問題なくテストできるはず。