Fork me on GitHub

The QA Tools project employs the expect tool to perform system testing against the Phar file that is distributed to developers. Contributors can describe their expectations of the interactive dialogue with the tool and assert what the program results are. Together, these form a specification.

System specifications consist of two parts, a dialogue expectation and a test file. These must be placed in the directory ./tests/system/specs.

.
├── tests
│   ├── system
│   │   ├── specs
│   │   │   ├── reticulates-splines.php # Assertions
│   │   │   └── reticulates-splines.tcl # Dialogue expectation

The specification is executed in a temporary directory. Initially, this directory contains symbolic links to the distributable and its public key. This is also the directory where QA Tools will generate files.

/tmp
├── qa-tools_system5790bd1c4b5c68.27898406
│   ├── qa-tools
│   └── qa-tools.pubkey

The dialogue expectation

The dialogue expectations are written in TCL. No comprehensive knowledge of TCL is required to write these dialogue expectations.

First, you indicate what QA Tools subcommand must be executed with which arguments. It is important you specify that no ANSI colouring be included in the QA Tools' output.

test ./qa-tools configure --no-ansi

For each interactive question, state the expected question text, and the answer expect should send in response. The expectation times out after a hard-coded 2 seconds.

answer "What is the project's name?" with "Wobbly Widdershins"

The intermediate presence of a string can also be asserted.

should_see "Reticulating splines..."

answer and should_see are procedures defined in the expectation harness so that expressive dialogue expectations can be written.

Finally, you can assert that the program will end and will exit with a zero exit code. If the program does not end within the configured time-out the expect script exits with exit code 1. When the program exits with a non-zero exit code, the expect script exits with the same exit code.

exits_with 0

Pitfalls

One pitfall is the use of brackets in strings; these execute commands. The below snippet, expecting a specific multiple-choice answer, attempts to execute 0:

answer "[0] Symfony 3" with "0"

The solution is to escape the brackets:

answer "\[0\] Symfony 3" with "0"

The test file

The test file contains plain PHP. The expect script is available as the callable $expect. This enables you to arrange things before executing QA Tools, like adding conflicts to the project's Composer configuration. Note that the test file ought to reside in the namespace Ibuildings\QaTools\SystemTest; this makes sure PHPUnit's assertion functions are in scope.

<?php

namespace Ibuildings\QaTools\SystemTest;

Composer::initialise();

/** @var callable $expect */
$expect();

assertFileExists('qa-tools.json');
Composer::assertPackageIsInstalled('phpmd/phpmd');

Multiple dialogue expectations

When a specification requires multiple interactions with the QA Tools program, you can use multiple dialogue expectations. By passing a chapter to the $expect helper callable, different dialogues are executed.

// 050_my-first-test.php

namespace Ibuildings\QaTools\SystemTest;

// Runs dialogue 050_my-first-test_setup.tcl
$expect('setup');
// Runs dialogue 050_my-first-test_test.tcl
$expect('test');

Example specification

spawn ./qa-tools configure --no-ansi

should_see "Configuring the Ibuildings QA Tools"

answer "What is the project's name?" with "Boolean Bust"

answer "Where would you like to store the generated files?" with "./"

should_see "What type of project would you like to configure?"
answer "\[0\] PHP" with "0"

should_see "What type of project would you like to configure?"
answer "\[1\] Symfony 3" with "1"

answer "Would you like to integrate Travis in your project?" with "Y"

exits_with 0
<?php

namespace Ibuildings\QaTools\SystemTest;

Composer::initialise();

/** @var callable $expect */
$expect();

assertFileExists('qa-tools.json');
Composer::assertPackageIsInstalled('phpmd/phpmd');

Installing Composer packages

Most tools will want to install Composer packages. However, installing these packages during testing, both locally as on Travis, makes tests slow and brittle. To countermand this, all tests involving Composer (pretty much all system tests) work with locally emulated packages. An example of this is phpmd/phpmd, emulated in tests/composer/packages/phpmd/phpmd/composer.json. These emulated packages are registered with Composer during the bootstrap of the test suite in Ibuildings\QaTools\ComposerTest\Composer::mockRepositories().