142호. Artisan::call(), 커맨드 상태에 주의하세요 free

2020-06-09

매일 점심 사먹을 돈이 최대 1만원까지 채워지는 마법의 지갑이 있다고 생각해봅시다. 오늘 점심을 먹기 위해 지출을 하면 내일 점심 때 다시 1만원이 되어 있는 지갑입니다.


이를 커맨드로 만들어보면 간략히 다음과 같이 만들어볼 수 있을 겁니다.




class LunchWalletCommand extends Command
{
protected $signature = ‘payLunch’;

private $dailyBudget = 10000;

public function handle()
{
$this->pay();

$this->recordRemainBudget();

$this->info($this->dailyBudget();
}

private function pay()
{
$this->dailyBudget -= 8000;
}

private function recordRemainBudget()
{
RemainBudget::create([‘budget’ => $this->dailyBudget]);
}

이 커맨드는 매일 오후 1시에 실행된다고 가정해봅시다.


$schedule->command(‘payLunch’)->dailyAt(‘13:00’);

그러면 예상하시는대로 매일 일간 예산이 1만원으로 설정되고 8000원을 써서 2000원이 남게 됩니다. 남은 금액은 remain_budgets 테이블에 기록 되고요.


이를 테스트하기 위해 다음과 같은 아이디어를 낼 수 있습니다.


/**
* @test
*/
public function it_reset_everyday()
{
Carbon::setTestNow(Carbon::yesterday());
Artisan::call('command:test');

Carbon::setTestNow(Carbon::now());
Artisan::call('command:test');

$this->assertEquals(2000, Artisan::output());
}

어제 날짜로 커맨드를 한 번 실행하고, 오늘 날짜로 다시 한 번 커맨드를 실행해보는 것이죠. 예상되는 결과는 2000이 출력되는 것 입니다. 커맨드 출력값을 확인하는 대신 remain_budgets 테이블에 기록되는 값을 확인하는 방법을 쓸 수도 있습니다.


하지만 이 테스트를 실행해보면 아래와 같이 실패합니다.


There was 1 failure:

1) Tests\Unit\ExampleTest::it_reset_everyday
Failed asserting that '-6000\n
' matches expected 2000.

2000이 출력 됐어야 했는데 -6000이 출력된 것이죠.


처음 실행한 커맨드의 상태가 남아있어서 두 번째 실행에서 $dailyBudget이 2000 - 8000이 되어 -6000이 된 것입니다. 두 번 실행한 커맨드가 독립적으로 실행되지 않고 하나의 인스턴스를 사용한 것으로 보입니다.


$this->artisan()을 사용해도 마찬가지 입니다.


    /**
* @test
*/
public function it_reset_everyday()
{
Carbon::setTestNow(Carbon::yesterday());
$this->artisan('payLunch')->expectsOutput(2000);

Carbon::setTestNow(Carbon::now());
$this->artisan('payLunch')->expectsOutput(2000);
}

이 테스트 역시 통과하지 못합니다.


Artisan::call()로 같은 커맨드를 반복 실행할 때 이전 커맨드 실행의 상태가 남아있어 예상치 못한 결과가 발생할 수 있다는 점을 유념하세요~


그럼 오늘도 즐거운 하루 되세요!


이현석

메쉬 코리아 개발자. 바쁜 팀장님 대신 알려주는 신입 PHP 개발자 안내서를 쓰고, 클린 아키텍처 인 PHP를 번역했습니다. 2020년에 출간될 Laravel Up & Running 2nd Edition을 번역하고 있습니다.