I'm writing a plugin for CakePHP that import/export databases. This plugin has a shell and its index()
method lists the databases already exported:
public function index()
{
//Gets alla files
$files = BackupManager::index();
$this->out(__d('mysql_backup', 'Backup files found: {0}', count($files)));
if (!empty($files)) {
//Parses files
$files = array_map(function ($file) {
if (isset($file->compression) && !$file->compression) {
$file->compression = __d('mysql_backup', 'none');
}
$file->size = Number::toReadableSize($file->size);
return array_values((array)$file);
}, $files);
//Table headers
$headers = [
__d('mysql_backup', 'Filename'),
__d('mysql_backup', 'Extension'),
__d('mysql_backup', 'Compression'),
__d('mysql_backup', 'Size'),
__d('mysql_backup', 'Datetime'),
];
$this->helper('table')->output(array_merge([$headers], $files));
}
}
Output with no database:
$ bin/cake backup
Welcome to CakePHP v3.3.8 Console
---------------------------------------------------------------
App : src
Path: /home/mirko/Server/mirkopagliai/src/
PHP : 7.0.12-1
---------------------------------------------------------------
Backup files found: 0
Output with some databases:
$ bin/cake backup
Welcome to CakePHP v3.3.8 Console
---------------------------------------------------------------
App : src
Path: /home/mirko/Server/mirkopagliai/src/
PHP : 7.0.12-1
---------------------------------------------------------------
Backup files found: 2
+-------------------------------------------+-----------+-------------+-----------+-----------------+
| Filename | Extension | Compression | Size | Datetime |
+-------------------------------------------+-----------+-------------+-----------+-----------------+
| backup_mirkopagliai_20161113110419.sql.gz | sql.gz | gzip | 51,05 KB | 13/11/16, 11:04 |
| backup_mirkopagliai_20161113110414.sql | sql | none | 150,93 KB | 13/11/16, 11:04 |
+-------------------------------------------+-----------+-------------+-----------+-----------------+
Now I need to write some tests.
I wrote the test for no database code:
public function testIndexNoBackups()
{
$this->io->expects($this->once())
->method('out')
->with('Backup files found: 0', 1);
$this->BackupShell->index();
}
My problem is to write a test of whether the table output (second example).
For now I only wrote this:
public function testIndex()
{
//Creates a database
(new BackupExport())->export();
$this->io->expects($this->once())
->method('out')
->with('Backup files found: 1', 1);
$this->BackupShell->index();
}
Output:
$ phpunit tests/TestCase/Shell/BackupShellTest.php --filter testIndex
PHPUnit 5.4.6 by Sebastian Bergmann and contributors.
E. 2 / 2 (100%)
Time: 114 ms, Memory: 6.00MB
There was 1 error:
1) MysqlBackup\Test\TestCase\Shell\BackupShellTest::testIndex
Error: Call to a member function output() on null
/home/mirko/Libs/Plugins/cakephp-mysql-backup/src/Shell/BackupShell.php:110
/home/mirko/Libs/Plugins/cakephp-mysql-backup/tests/TestCase/Shell/BackupShellTest.php:191
ERRORS!
Tests: 2, Assertions: 1, Errors: 1.
So, how to write a test to check the table output? Where can I find an example?
It depends on how clean you want to do it. I would personally, for speed mainly, not use expectations for the output.
Instead I just collect the output in a "pseudo-mock" kind of container:
...
use Tools\TestSuite\ConsoleOutput;
...
$this->out = new ConsoleOutput();
$this->err = new ConsoleOutput();
$io = new ConsoleIo($this->out, $this->err);
$this->Shell = $this->getMockBuilder(InflectShell::class)
->setMethods(['in', '_stop'])
->setConstructorArgs([$io])
->getMock();
This special ConsoleOutput will not write to stdout or stderr, but instead just internally collect it all for you to fetch it afterwards.
After running the command, you can then just do
$output = $this->out->output();
$expected = 'foo-bar';
$this->assertContains($expected, $output);
Check out the test cases in my tools plugin for details. The link also provides you the tooling you would need for the above example.