Behat & Symfony's Process component

Warning: This post is over a year old. The information may be out of date.

Here’s a super-quick way of using the Symfony Process Component in Behat Contexts to run processes in the background.

Chatting with a friend (@pieceofthepie) in the PHP East Midlands slack team and they said this:

The CI I’m playing with has a test step the needs me to start the php server, so I’m doing it in bash with a &

It works, but doing it ‘properly’ would maybe be better.

Now, we could argue the meaning of ‘properly’ all day (their method works, so what’s the problem?) but I thought I’d come up with an alternative for them.

Running background processes in Behat scenarios is something I’d done a number of times before in projects (scenarios required job workers to be running in the background to end-to-end test some async feature within an application) so this wouldn’t be too difficult to do with the Symfony Process Component.

I quickly came up with the following for them:

<?php

namespace App\Context;

use Behat\Behat\Context\Context;
use Symfony\Component\Process\Process;

class PhpServerContext implements Context
{
    const TIMEOUT = 10;
    
    /**
     * @var Process
     */
    protected $process;
    
    /**
     * @BeforeScenario @run-server
     */
    public function startServerProcess()
    {
        $this->process = new Process('exec php -S localhost:8000 -t /path/to/my/public/directory');
        $this->process->start();
    }
    
    /**
     * @AfterScenario @run-server
     */
    public function stopServerProcess()
    {
        $this->process->stop(self::TIMEOUT, SIGTERM);
        $this->process = null;
    }
}

Now tagging a scenario / feature with: @run-server will mean that the PHP built-in webserver will be started before the scenario runs and will be listening on port :8000. It’ll then stop the server when the scenario is finished - job done!

You could take this a step further and have it configurable for ports & docroot, maybe check if something is listening on that port already. Maybe have it run for the full suite, depends on your needs / use case.