WPBrowser scaffold WP-CLI command 05

Mocking calls to shell commands in Behat tests.

Context

The context of the post is my ongoing effort to develop a wp-cli package to allow users to interactively scaffold tests for WordPress plugins and themes based on Codeception and wp-browser.
The scaffolding consists in creating some files like a Composer configuration file and then launching commands like composer update or wpcept boostrap --interactive later.
Using what the wp-cli cookbook and package scaffolding command provide I’m using Behat to develop the CLI commands with a behaviour-driven development approach.

Do we want to update Composer dependencies?

I’ve written about the interactivity tools wp-cli offers in a previous post and found myself in need of testing the step where the Composer composer.json configuration file has been updated or created and the user is asked if the process should go on and run composer update to download the dependencies from the web.
That’s a costly operation in terms of time and bandwidth that the user might want to postpone.
Should this be the case I’ve added a scenario to make sure the user is left with some instructions about the things to do:

Scenario: the command will end if the user wants to manually update composer dependencies
    Given I will answer 'n' to the 'composer update' question
    Given I run `composer init --name=lucatume/some --description=Some --author="Luca Tumedei <luca@theaveragedev.com>" -n` in the 'some-plugin' plugin
    When I run `wp scaffold plugin some-plugin --plugin_name="Some Plugin" --plugin_description="Description of the plugin." --plugin_author="Your Name" --plugin_author_uri="http://example.com"`
    And I run `wp wpb-scaffold plugin-tests some-plugin` with input
    Then STDOUT should contain:
    """
    All done
    """
    Then STDOUT should contain:
    """
    Run `composer update` from this folder to install or update wp-browser
    """
    Then STDOUT should contain:
    """
    Run `./vendor/bin/wpcept bootstrap --interactive-mode` to start wp-browser interactive test setup
    """

Should the user instead decide to run the Composer update immediately then the command composer should be called:

@pathEnv
Scenario: the command will launch composer update if the user wants it to
    Given I will answer 'y' to the 'composer update' question
    Given I will answer 'n' to the 'wpcept bootstrap' question
    When I run `wp scaffold plugin some-plugin --plugin_name="Some Plugin" --plugin_description="Description of the plugin." --plugin_author="Your Name" --plugin_author_uri="http://example.com"`
    And I run `wp wpb-scaffold plugin-tests some-plugin` with input
    Then 'composer' should have been called
    Then STDOUT should contain:
    """
    All done
    """
    Then STDOUT should not contain:
    """
    Run `composer update` from this folder to install or update wp-browser
    """
    Then STDOUT should contain:
    """
    Run `./vendor/bin/wpcept bootstrap --interactive-mode` to start wp-browser interactive test setup
    """

Mocking the composer command

Beside testing some expected output the things to note in the scenario above are:

  • the tagging of the scenario with @pathEnv
  • the step Then 'composer' should have been called

The first one leverages the tagging system provided by Behat to “do something” before and after a scenario: specifically here I’m prepending the path to a test data directory to the real PATH in the first in the FeatureContext class:

/**
 * @BeforeScenario @pathEnv
 */
public function backupPathEnvVar() {
    $this->pathBackup = getenv( 'PATH' );
    $newPath          = 'PATH=' . $this->get_data_dir() . ':' . $this->pathBackup;
    putenv( $newPath );
}

/**
 * @AfterScenario @pathEnv
 */
public function restorePathEnvVar() {
    putenv( 'PATH=' . $this->pathBackup );
}

The data folder contains a simple composer file that will do nothing but print a message to the output when called:

#!/usr/bin/env php

<?php
echo 'Running mocked composer'; return 0;

The check made in the 'composer' should have been called step is simply checking for that string in the output (see code here):

$steps->Then( '/^\'([^\']*)\' should have been called$/', function ( $world, $command ) {
    checkString( trim( $world->result->stdout, "\n" ), 'Running mocked ' . $command, 'contain' );
} );

The combination of the two is that when the “user”, really a mocked input, answers “y” to the confirmation to run composer update the script will check for a composer command in the PATH and will find the first match in the data folder running the “mock” composer script.