Testing CLI Commands
Using MockInputOutput
Added in version 4.5.0.
MockInputOutput
MockInputOutput provides an easy way to write tests for commands that require
user input, such as CLI::prompt()
, CLI::wait()
, and CLI::input()
.
You can replace the InputOutput
class with MockInputOutput
during test
execution to capture inputs and outputs.
Note
When you use MockInputOutput
, you don’t need to use
StreamFilterTrait, CITestStreamFilter, and
PhpStreamWrapper.
Helper Methods
getOutput(?int $index = null): string
Gets the output.
If you call it like
$io->getOutput()
, it returns the whole output string.If you specify
0
or a positive number, it returns the output array item. Each item has the output of aCLI::fwrite()
call.If you specify a negative number
-n
, it returns the lastn
-th item of the output array.
getOutputs(): array
Returns the output array. Each item has the output of a CLI::fwrite()
call.
How to Use
CLI::setInputOutput()
can set theMockInputOutput
instance to theCLI
class.CLI::resetInputOutput()
resets theInputOutput
instance in theCLI
class.MockInputOutput::setInputs()
sets the user input array.MockInputOutput::getOutput()
gets the command output.
The following test code is to test the command spark db:table
:
<?php
use CodeIgniter\CLI\CLI;
use CodeIgniter\Test\CIUnitTestCase;
use CodeIgniter\Test\DatabaseTestTrait;
use CodeIgniter\Test\Mock\MockInputOutput;
final class DbTableTest extends CIUnitTestCase
{
use DatabaseTestTrait;
protected $migrateOnce = true;
public function testDbTable(): void
{
// Set MockInputOutput to CLI.
$io = new MockInputOutput();
CLI::setInputOutput($io);
// User will input "a" (invalid value) and "0".
$io->setInputs(['a', '0']);
command('db:table');
// Get the whole output string.
$output = $io->getOutput();
$expected = 'Which table do you want to see? [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]: a';
$this->assertStringContainsString($expected, $output);
$expected = 'Data of Table "db_migrations":';
$this->assertStringContainsString($expected, $output);
// Remove MockInputOutput.
CLI::resetInputOutput();
}
}
Without MockInputOutput
Testing CLI Output
StreamFilterTrait
Added in version 4.3.0.
StreamFilterTrait provides an alternate to these helper methods.
You may need to test things that are difficult to test. Sometimes, capturing a stream, like PHP’s own STDOUT, or STDERR,
might be helpful. The StreamFilterTrait
helps you capture the output from the stream of your choice.
How to Use
StreamFilterTrait::getStreamFilterBuffer()
gets the captured data from the buffer.StreamFilterTrait::resetStreamFilterBuffer()
resets captured data.
An example demonstrating this inside one of your test cases:
<?php
namespace Tests;
use CodeIgniter\CLI\CLI;
use CodeIgniter\Test\CIUnitTestCase;
use CodeIgniter\Test\StreamFilterTrait;
final class SomeTest extends CIUnitTestCase
{
use StreamFilterTrait;
public function testSomeOutput(): void
{
CLI::write('first.');
$this->assertSame("\nfirst.\n", $this->getStreamFilterBuffer());
$this->resetStreamFilterBuffer();
CLI::write('second.');
$this->assertSame("second.\n", $this->getStreamFilterBuffer());
}
}
The StreamFilterTrait
has a configurator that is called automatically.
See Testing Traits.
If you override the setUp()
or tearDown()
methods in your test, then you must call the parent::setUp()
and
parent::tearDown()
methods respectively to configure the StreamFilterTrait
.
CITestStreamFilter
CITestStreamFilter for manual/single use.
If you need to capture streams in only one test, then instead of using the StreamFilterTrait trait, you can manually add a filter to streams.
How to Use
CITestStreamFilter::registration()
Filter registration.CITestStreamFilter::addOutputFilter()
Adding a filter to the output stream.CITestStreamFilter::addErrorFilter()
Adding a filter to the error stream.CITestStreamFilter::removeOutputFilter()
Removing a filter from the output stream.CITestStreamFilter::removeErrorFilter()
Removing a filter from the error stream.
<?php
namespace Tests;
use CodeIgniter\CLI\CLI;
use CodeIgniter\Test\CIUnitTestCase;
use CodeIgniter\Test\Filters\CITestStreamFilter;
final class SomeTest extends CIUnitTestCase
{
public function testSomeOutput(): void
{
CITestStreamFilter::registration();
CITestStreamFilter::addOutputFilter();
CLI::write('first.');
CITestStreamFilter::removeOutputFilter();
}
}
Testing CLI Input
PhpStreamWrapper
Added in version 4.3.0.
PhpStreamWrapper provides a way to write tests for methods that require user input,
such as CLI::prompt()
, CLI::wait()
, and CLI::input()
.
Note
The PhpStreamWrapper is a stream wrapper class. If you don’t know PHP’s stream wrapper, see The streamWrapper class in the PHP maual.
How to Use
PhpStreamWrapper::register()
Register thePhpStreamWrapper
to thephp
protocol.PhpStreamWrapper::restore()
Restore the php protocol wrapper back to the PHP built-in wrapper.PhpStreamWrapper::setContent()
Set the input data.
Important
The PhpStreamWrapper is intended for only testing php://stdin
.
But when you register it, it handles all the php protocol streams,
such as php://stdout
, php://stderr
, php://memory
.
So it is strongly recommended that PhpStreamWrapper
be registered/unregistered
only when needed. Otherwise, it will interfere with other built-in php streams
while registered.
An example demonstrating this inside one of your test cases:
<?php
namespace Tests;
use CodeIgniter\CLI\CLI;
use CodeIgniter\Test\CIUnitTestCase;
use CodeIgniter\Test\PhpStreamWrapper;
final class SomeTest extends CIUnitTestCase
{
public function testPrompt(): void
{
// Register the PhpStreamWrapper.
PhpStreamWrapper::register();
// Set the user input to 'red'. It will be provided as `php://stdin` output.
$expected = 'red';
PhpStreamWrapper::setContent($expected);
$output = CLI::prompt('What is your favorite color?');
$this->assertSame($expected, $output);
// Restore php protocol wrapper.
PhpStreamWrapper::restore();
}
}