Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

alariva's avatar

How do you test your commands? (phpunit)

Hi guys,

I'm trying to write test cases for Commands in PHPUnit, without much success.

At this point I've tried many things, being probably this post the closest approach I found for my purpose. Still, I'm struggling a lot to get this working.

Follows an example output for you:

alariva@trinsic ~/timegrid.io/app $ phpunit --filter=SendBusinessReportTest
PHP Warning:  The use statement with non-compound name 'Artisan' has no effect in /home/alariva/timegrid.io/app/tests/unit/Console/Commands/SendBusinessReportTest.php on line 4
PHP Stack trace:
PHP   1. {main}() /usr/bin/phpunit:0
PHP   2. PHPUnit_TextUI_Command::main() /usr/bin/phpunit:46
PHP   3. PHPUnit_TextUI_Command->run() /usr/share/php/PHPUnit/TextUI/Command.php:129
PHP   4. PHPUnit_TextUI_Command->handleArguments() /usr/share/php/PHPUnit/TextUI/Command.php:138
PHP   5. PHPUnit_Util_Configuration->getTestSuiteConfiguration() /usr/share/php/PHPUnit/TextUI/Command.php:657
PHP   6. PHPUnit_Util_Configuration->getTestSuite() /usr/share/php/PHPUnit/Util/Configuration.php:789
PHP   7. PHPUnit_Framework_TestSuite->addTestFiles() /usr/share/php/PHPUnit/Util/Configuration.php:873
PHP   8. PHPUnit_Framework_TestSuite->addTestFile() /home/alariva/timegrid.io/app/vendor/phpunit/phpunit/src/Framework/TestSuite.php:409
PHP   9. PHPUnit_Util_Fileloader::checkAndLoad() /home/alariva/timegrid.io/app/vendor/phpunit/phpunit/src/Framework/TestSuite.php:335
PHP  10. PHPUnit_Util_Fileloader::load() /usr/share/php/PHPUnit/Util/Fileloader.php:76
PHPUnit 4.8.26 by Sebastian Bergmann and contributors.

PHP Fatal error:  Call to undefined method App\Console\Kernel::resolve() in /home/alariva/timegrid.io/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php on line 217
PHP Stack trace:
PHP   1. {main}() /usr/bin/phpunit:0
PHP   2. PHPUnit_TextUI_Command::main() /usr/bin/phpunit:46
PHP   3. PHPUnit_TextUI_Command->run() /usr/share/php/PHPUnit/TextUI/Command.php:129
PHP   4. PHPUnit_TextUI_TestRunner->doRun() /usr/share/php/PHPUnit/TextUI/Command.php:176
PHP   5. PHPUnit_Framework_TestSuite->run() /home/alariva/timegrid.io/app/vendor/phpunit/phpunit/src/TextUI/TestRunner.php:440
PHP   6. PHPUnit_Framework_TestSuite->run() /home/alariva/timegrid.io/app/vendor/phpunit/phpunit/src/Framework/TestSuite.php:747
PHP   7. PHPUnit_Framework_TestCase->run() /home/alariva/timegrid.io/app/vendor/phpunit/phpunit/src/Framework/TestSuite.php:747
PHP   8. PHPUnit_Framework_TestResult->run() /home/alariva/timegrid.io/app/vendor/phpunit/phpunit/src/Framework/TestCase.php:724
PHP   9. PHPUnit_Framework_TestCase->runBare() /home/alariva/timegrid.io/app/vendor/phpunit/phpunit/src/Framework/TestResult.php:612
PHP  10. PHPUnit_Framework_TestCase->runTest() /home/alariva/timegrid.io/app/vendor/phpunit/phpunit/src/Framework/TestCase.php:768
PHP  11. ReflectionMethod->invokeArgs() /home/alariva/timegrid.io/app/vendor/phpunit/phpunit/src/Framework/TestCase.php:909
PHP  12. SendBusinessReportTest->it_tests_command() /home/alariva/timegrid.io/app/vendor/phpunit/phpunit/src/Framework/TestCase.php:909
PHP  13. Illuminate\Support\Facades\Artisan::resolve() /home/alariva/timegrid.io/app/tests/unit/Console/Commands/SendBusinessReportTest.php:14
PHP  14. Illuminate\Support\Facades\Facade::__callStatic() /home/alariva/timegrid.io/app/tests/unit/Console/Commands/SendBusinessReportTest.php:14

  [Symfony\Component\Debug\Exception\FatalErrorException]  
  Call to undefined method App\Console\Kernel::resolve() 

You can find my current attempt here, (broken tests), but maybe you can hint me something I'm missing :)

Side Note: You may ask why PHPUnit and not another testing framework. So far I'm generating my test coverage with PHPUnit and I'd like to stick to it until I feel the actual need to switch. However, all suggestions are welcome.

Thanks in advance!

0 likes
8 replies
alariva's avatar

Thanks for the hint, I should have started from there!! Will post updates :)

alariva's avatar

@ifpingram , thanks for the hint. As I'm new to some of these concepts, it took me a while to get it working... however, it seems I finally got to the solution. I believe I followed your steps, correctly. Would you help me validate that? (Yep, I know it can be refactored, I'll get over that once everything looks fine :) )

<?php

use App\Console\Commands\SendBusinessReport;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Symfony\Component\Console\Application as ConsoleApplication;
use Symfony\Component\Console\Tester\CommandTester;
use Timegridio\Concierge\Models\Appointment;

class SendBusinessReportTest extends TestCase
{
    use DatabaseTransactions;
    use CreateBusiness, CreateUser, CreateContact, CreateAppointment, CreateService, CreateVacancy;

    /** @test */
    public function it_reports_to_all_businesses()
    {
        $this->arrangeFixture();

        $application = new ConsoleApplication();

        $testedCommand = $this->app->make(SendBusinessReport::class);
        $testedCommand->setLaravel(app());
        $application->add($testedCommand);

        $command = $application->find('business:report');

        $commandTester = new CommandTester($command);

        $commandTester->execute([
            'command' => $command->getName(),
        ]);

        $this->assertRegExp('/Scanning all businesses../', $commandTester->getDisplay());
    }

    /**
     * Arrange Fixture.
     *
     * @return void
     */
    protected function arrangeFixture()
    {
        $this->owner = $this->createUser();
        $this->issuer = $this->createUser();

        $this->business = $this->createBusiness();
        $this->business->owners()->save($this->owner);

        $this->contact = $this->createContact();
        $this->contact->user()->associate($this->issuer);

        $this->service = $this->makeService();
        $this->business->services()->save($this->service);

        $this->vacancy = $this->makeVacancy();
        $this->vacancy->service()->associate($this->service);

        $this->business->vacancies()->save($this->vacancy);

        $appointment = $this->makeAppointment($this->business, $this->issuer, $this->contact, [
            'status' => Appointment::STATUS_CONFIRMED,
            ]);
    }
}
alariva@trinsic ~/timegrid.io/app $ phpunit --filter=it_reports_to_all_businesses
PHPUnit 5.4.6 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 813 ms, Memory: 30.00MB

OK (1 test, 1 assertion)
1 like
alariva's avatar

Finally, a refactored version with 2 test cases

You may find the working code here

<?php

use App\Console\Commands\SendBusinessReport;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Symfony\Component\Console\Application as ConsoleApplication;
use Symfony\Component\Console\Tester\CommandTester;
use Timegridio\Concierge\Models\Appointment;

class SendBusinessReportTest extends TestCase
{
    use DatabaseTransactions;
    use CreateBusiness, CreateUser, CreateContact, CreateAppointment;

    protected $command;

    protected $commandTester;

    /**
     * @var \Timegridio\Concierge\Models\Business
     */
    protected $business;

    public function setUp()
    {
        parent::setUp();

        $application = new ConsoleApplication();

        $testedCommand = $this->app->make(SendBusinessReport::class);
        $testedCommand->setLaravel(app());
        $application->add($testedCommand);

        $this->command = $application->find('business:report');

        $this->commandTester = new CommandTester($this->command);

        $this->arrangeFixture();
    }

    /** @test */
    public function it_reports_to_all_businesses()
    {
        $this->commandTester->execute([
            'command' => $this->command->getName(),
        ]);

        $this->assertRegExp('/Scanning all businesses../', $this->commandTester->getDisplay());
    }

    /** @test */
    public function it_reports_to_a_single_business()
    {
        $this->commandTester->execute([
            'command'  => $this->command->getName(),
            'business' => $this->business->id,
        ]);

        $this->assertRegExp("/Sending to businessId:{$this->business->id}/", $this->commandTester->getDisplay());
    }

    protected function arrangeFixture()
    {
        $issuer = $this->createUser();

        $this->business = $this->createBusiness();

        $contact = $this->createContact();

        $this->makeAppointment($this->business, $issuer, $contact, [
            'status' => Appointment::STATUS_CONFIRMED,
            ]);
    }
}
ifpingram's avatar

@alariva looks good, although I think the naming of arrangeFixture() is wrong; it should be seedAppointment() or similar, as what you are doing is seeding the system prior to testing the report...

1 like
bjones2015's avatar

I shouldn't be resurrecting an old thread I suppose, but I thought i'd throw in a different approach that I use as it can simplify things.

$this->artisan('whatever-the-command-is');

will call the command for you. So for example if testing a daily admin report, you could seed the database, mock the Mailer and then call something like

$this->artisan('my-company:daily-report');

Of course this is assuming you are extending TestCase

1 like
ifpingram's avatar

@bjones2015 this is correct, but IIRC this does not allow the output of the command to be retrieved, hence the need to dig a little deeper and return the following for the assertions:

$this->commandTester->getDisplay();

Please or to participate in this conversation.