zenstruck/console-test

用于测试 Symfony 控制台命令的替代方案,具有个人观点的辅助工具。

资助包维护!
kbond

v1.6.0 2024-07-24 14:03 UTC

This package is auto-updated.

Last update: 2024-09-16 01:33:55 UTC


README

CI Status codecov

用于测试 Symfony 控制台命令的替代方案,具有个人观点的辅助工具。此包是 Symfony\Component\Console\Tester\CommandTester 的替代品,可以帮助使您的测试更加表达性和简洁。

安装

composer require --dev zenstruck/console-test

Symfony 框架使用

您可以通过在您的 KernelTestCase/WebTestCase 测试中使用 InteractsWithConsole 特性来运行测试中的控制台命令。

use App\Command\CreateUserCommand;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Zenstruck\Console\Test\InteractsWithConsole;

class CreateUserCommandTest extends KernelTestCase
{
    use InteractsWithConsole;

    public function test_can_create_user(): void
    {
        $this->executeConsoleCommand('create:user kbond --admin --role=ROLE_EMPLOYEE --role=ROLE_MANAGER')
            ->assertSuccessful() // command exit code is 0
            ->assertOutputContains('Creating admin user "kbond"')
            ->assertOutputContains('with roles: ROLE_EMPLOYEE, ROLE_MANAGER')
            ->assertOutputNotContains('regular user')
        ;

        // advanced usage
        $this->consoleCommand(CreateUserCommand::class) // can use the command class or "name"
            ->splitOutputStreams() // by default stdout/stderr are combined, this options splits them
            ->addArgument('kbond')
            ->addOption('--admin') // with or without "--" prefix
            ->addOption('role', ['ROLE_EMPLOYEE', 'ROLE_MANAGER'])
            ->addOption('-R') // shortcut options require the "-" prefix
            ->addOption('-vv') // by default, output has normal verbosity, use the standard options to change (-q, -v, -vv, -vvv)
            ->addOption('--ansi') // by default, output is undecorated, use this option to decorate
            ->execute() // run the command
            ->assertSuccessful()
            ->assertStatusCode(0) // equivalent to ->assertSuccessful()
            ->assertOutputContains('Creating admin user "kbond"')
            ->assertErrorOutputContains('this is in stderr') // used in conjunction with ->splitOutputStreams()
            ->assertErrorOutputNotContains('admin user') // used in conjunction with ->splitOutputStreams()
            ->dump() // dump() the status code/outputs and continue
            ->dd() // dd() the status code/outputs
        ;

        // testing interactive commands
        $this->executeConsoleCommand('create:user', ['kbond'])
            ->assertSuccessful()
            ->assertOutputContains('Creating regular user "kbond"')
        ;

        // advanced testing interactive commands
        $this->consoleCommand(CreateUserCommand::class)
            ->addInput('kbond')
            ->addOption('--no-interaction') // commands are run interactively if input is provided, use this option to disable
            ->execute()
            ->assertSuccessful()
            ->assertOutputContains('Creating regular user "kbond"')
        ;

        // test command throws exception
        $this->consoleCommand(CreateUserCommand::class)
            ->expectException(\RuntimeException::class, 'Username required!')
            ->assertStatusCode(1) // equivalent to ->assertFaulty()
            ->assertOutputContains('Could not create user!') // can still make assertions on output before exception was thrown
        ;

        // test completion
        $this->consoleCommand('create:user')
            ->complete('')
                ->is(['kevin', 'john', 'jane'])
                ->contains('kevin') // chain assertions
            ->back() // fluently go back to the TestCommand
            ->complete('kevin --role=')->is(['ROLE_EMPLOYEE', 'ROLE_MANAGER'])
        ;

        // access result
        $result = $this->executeConsoleCommand('create:user');

        $result->statusCode();
        $result->output();
        $result->errorOutput();
    }
}

独立使用

您可以在单元测试或非 Symfony 框架环境中测试命令。

use App\Command\CreateUserCommand;
use PHPUnit\Framework\TestCase;
use Zenstruck\Console\Test\TestCommand;

class CreateUserCommandTest extends TestCase
{
    public function test_can_create_user(): void
    {
        TestCommand::for(new CreateUserCommand(/** args... */))
            ->execute('kbond --admin --role=ROLE_EMPLOYEE --role=ROLE_MANAGER')
            ->assertSuccessful() // command exit code is 0
            ->assertOutputContains('Creating admin user "kbond"')
            ->assertOutputContains('with roles: ROLE_EMPLOYEE, ROLE_MANAGER')
            ->assertOutputNotContains('regular user')
        ;

        // advanced usage
        TestCommand::for(new CreateUserCommand(/** args... */))
            ->splitOutputStreams() // by default stdout/stderr are combined, this options splits them
            ->addArgument('kbond')
            ->addOption('--admin') // with or without "--" prefix
            ->addOption('role', ['ROLE_EMPLOYEE', 'ROLE_MANAGER'])
            ->addOption('-R') // shortcut options require the "-" prefix
            ->addOption('-vv') // by default, output has normal verbosity, use the standard options to change (-q, -v, -vv, -vvv)
            ->addOption('--ansi') // by default, output is undecorated, use this option to decorate
            ->execute()
            ->assertSuccessful()
            ->assertStatusCode(0) // equivalent to ->assertSuccessful()
            ->assertOutputContains('Creating admin user "kbond"')
            ->assertErrorOutputContains('this is in stderr') // used in conjunction with ->splitOutputStreams()
            ->assertErrorOutputNotContains('admin user') // used in conjunction with ->splitOutputStreams()
            ->dump() // dump() the status code/outputs and continue
            ->dd() // dd() the status code/outputs
        ;

        // testing interactive commands
        TestCommand::for(new CreateUserCommand(/** args... */))
            ->addInput('kbond')
            ->addOption('--no-interaction') // commands are run interactively if input is provided, use this option to disable
            ->execute()
            ->assertSuccessful()
            ->assertOutputContains('Creating regular user "kbond"')
        ;

        // test command throws exception
        TestCommand::for(new CreateUserCommand(/** args... */))
            ->expectException(\RuntimeException::class, 'Username required!')
            ->assertStatusCode(1)
            ->assertOutputContains('Could not create user!') // can still make assertions on output before exception was thrown
        ;

        // test completion
        TestCommand::for(new CreateUserCommand(/** args... */))
            ->complete('')
                ->is(['kevin', 'john', 'jane'])
                ->contains('kevin') // chain assertions
            ->back() // fluently go back to the TestCommand
            ->complete('kevin --role=')->is(['ROLE_EMPLOYEE', 'ROLE_MANAGER'])
        ;

        // access result
        $result = TestCommand::for(new CreateUserCommand(/** args... */))->execute();

        $result->statusCode();
        $result->output();
        $result->errorOutput();
    }
}

标准化终端宽度

在不同的终端环境中(例如 Windows、Linux、Github Actions),默认的终端宽度可能不同。由于某些 Symfony 输出辅助工具使用此值来包装长行,这可能导致不同环境中的输出断言失败。建议通过为您的测试套件设置 COLUMNS 环境变量来标准化终端宽度。

<!-- phpunit.xml -->

<phpunit>
    <!-- ... -->
    <php>
        <env name="COLUMNS" value="120" />
    </php>
    <!-- ... -->
</phpunit>