Saturday 25 March 2017

Keeping your CLI integration tests green on Windows

Lately on a Windows system, some failing integration tests for CLI commands utilising the Symfony Console component caused me some blip headaches by PHPUnit insisting that two strings are not identical due to different line endings. The following post documents the small steps I took to overcome these headaches.

First the assertion message produced by the failing test, see the console output below, got me thinking it might be caused by different encodings and line endings; though the project was utilising an .editorconfig from the early start and the related files were all encoded correctly and had the configured line endings. The Git configuration e.g. core.autocrlf=input also was as it should be.

1) Stolt\LeanPackage\Tests\Commands\InitCommandTest::createsExpectedDefaultLpvFile
Failed asserting that two strings are identical.
--- Expected
+++ Actual
@@ @@
 #Warning: Strings contain different line endings!
-Created default 'C:\Users\stolt\AppData\Local\Temp\lpv\.lpv' file.
+Created default 'C:\Users\stolt\AppData\Local\Temp\lpv\.lpv' file.
Another deeper look at the CommandTester class yielded that it’s possible to disable the command output decoration and also to normalise the command output. So a change of the SUT preparation and a normalisation of the console output, visualised via a git diff -U10, brought the solution for this particular test.

diff --git a/tests/Commands/InitCommandTest.php b/tests/Commands/InitCommandTest.php
index 58e7114..fb406f3 100644
--- a/tests/Commands/InitCommandTest.php
+++ b/tests/Commands/InitCommandTest.php
@@ -48,21 +48,21 @@ class InitCommandTest extends TestCase
     /**
      * @test
      */
     public function createsExpectedDefaultLpvFile()
     {
         $command = $this->application->find('init');
         $commandTester = new CommandTester($command);
         $commandTester->execute([
             'command' => $command->getName(),
             'directory' => WORKING_DIRECTORY,
-        ]);
+        ], ['decorated' => false]);

// ommitted  code

-        $this->assertSame($expectedDisplay, $commandTester->getDisplay());
+        $this->assertSame($expectedDisplay, $commandTester->getDisplay(true));
         $this->assertTrue($commandTester->getStatusCode() == 0);
         $this->assertFileExists($expectedDefaultLpvFile);
Since the SUT had a lot of integration test for its CLI commands, the lazy me took the shortcut to extend the CommandTester and using it, with desired defaults set, instead of changing all of the related command instantiations.

<?php

namespace SUT\Tests;

use Symfony\Component\Console\Tester\CommandTester as ConsoleCommandTester;

class CommandTester extends ConsoleCommandTester
{
    /**
     * Gets the display returned by the last execution of the command.
     *
     * @param bool $normalize Whether to normalize end of lines to \n or not
     *
     * @return string The display
     */
    public function getDisplay($normalize = true)
    {
        return parent::getDisplay($normalize);
    }

    /**
     * Executes the command.
     *
     * Available execution options:
     *
     *  * interactive: Sets the input interactive flag
     *  * decorated:   Sets the output decorated flag
     *  * verbosity:   Sets the output verbosity flag
     *
     * @param array $input   An array of command arguments and options
     * @param array $options An array of execution options
     *
     * @return int The command exit code
     */
    public function execute(
        array $input,
        array $options = ['decorated' => false]
    ) {
        return parent::execute($input, $options);
    }
}
So it's a yay for green CLI command integration tests on Windows from here on. Another measure for the SUT would be to enable Continuous Integration on a Windows system via AppVeyor, but that’s a task for another commit 85bdf22.

No comments: