Posts tagged 'php'

Testing Symfony commands with Behat

published on January 18, 2019.

The other day I was creating a Symfony command that will be periodically executed by a cronjob. I decided to write a Behat test for it, to see what a test like that would look like. Plus, just because it is executed by the system from a command line, doesn’t mean we can skimp on the business requirements.

We need Symfony, Behat, and Behat Symfony2 extension. In the behat.yml file we configure the Behat extension to boot up the Kernel for us and pass it in is a constructor argument to our Behat Context:

./behat.yml

default:
    extensions:
        Behat\Symfony2Extension:
          kernel:
            bootstrap: features/bootstrap/bootstrap.php
            class: App\Kernel

    suites:
      system:
        paths:
          - '%paths.base%/features/system.feature'
        contexts:
          - SystemContext:
              kernel: '@kernel'

We enable and configure the Behat Symfony2 extension, and tell Behat that the system suite will use the system.feature feature file and the SystemContext context which takes one constructor argument, the kernel. I don’t like to put everything into the default FeatureContext for Behat, but rather split different contexts into, well, different contexts. That’s why I created the separate SystemContext.

The boostrap.php file is created when installing the extension (at least, it was created for me as I installed it using Symfony Flex):

./features/bootstrap/bootstrap.php

<?php
putenv('APP_ENV='.$_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = 'test');
require dirname(__DIR__, 2).'/config/bootstrap.php';

The system.feature file doesn’t have much, just an example scenario:

./features/system.feature

Feature: System executed commands

  Scenario: Behat testing a Symfony command
    Given I am the system
    When I greet the world
    Then I should say "Hello World"

The SystemContext

The SystemContext file is where it gets interesting:

./features/bootstrap/SystemContext.php

<?php

use App\Kernel;
use Behat\Behat\Context\Context;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Output\BufferedOutput;
use Webmozart\Assert\Assert;

class SystemContext implements Context
{
    /**
     * @var Application
     */
    private $application;

    /**
     * @var BufferedOutput
     */
    private $output;

    public function __construct(Kernel $kernel)
    {
        $this->application = new Application($kernel);
        $this->output = new BufferedOutput();
    }

It implements the Behat Context interface so that Behat recognizes it as a context.

In the constructor we create a new console Application with the Kernel that the Behat Symfony2 extension created for us. We will use this application instance to run the command that we are testing. For all intents and purposes, this application instance acts the same as the application instance that gets created in the bin/console script that we usually use to run Symfony commands.

We also create a BufferedOutput in the constructor, that will hold the output that the command produces, which we can later on use to assert did the command produce the desired output.

Behat step definitions

The steps are defined like so (it’s in the same SystemContext.php file as the previous example):

./features/bootstrap/SystemContext.php

<?php

class SystemContext implements Context
{
    /**
     * @Given I am the system
     */
    public function iAmTheSystem()
    {
        Assert::same('cli', php_sapi_name());
    }

    /**
     * @When I greet the world
     */
    public function iGreetTheWorld()
    {
        $command = 'app:hello-world';
        $input = new ArgvInput(['behat-test', $command, '--env=test']);
        $this->application->doRun($input, $this->output);
    }

    /**
     * @Then I should say :sentence
     */
    public function iShouldSay($sentence)
    {
        $output = $this->output->fetch();

        Assert::same($sentence, $output);
    }

I am the system

In the first Behat step we assert that the PHP interface is the cli. Not sure how it could be anything else in this case, but let’s have that in there.

I greet the world

The second Behat step is where the fun part happens, where we run the command. The ArgvInput takes an array of parameters from the CLI in the argv format. In the case of bin/console it ends up being populated from $_SERVER['argv']. In this case though, we need to populate it on our own.

The first argument is always the name that was used to run the script and it ends up being just a “placeholder”, hence the behat-test value. We can put in anything there, really.

The second parameter is the command that we want to run: app:hello-world. It is the same string we would use when executing that command through bin/console. Because we created an instance of the Application in the constructor, Symfony will know exactly what command that is.

The third parameter is an option to tell Symfony to run the command in the test environment.

Once we have the input ready, we tell the application to run using the doRun method, passing in the input and the output (which is a BufferedOutput).

I should say :sentence

In the third Behat step we fetch the output and assert that it is the same as the output we expected it to be.

Make it reusable

To make it a bit more reusable, the running of the command in the iGreetTheWorld step can be extracted to a private method so that it all reads a little bit nicer. The final result looks something like this:

./features/bootstrap/SystemContext.php

<?php

use App\Kernel;
use Behat\Behat\Context\Context;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Output\BufferedOutput;
use Webmozart\Assert\Assert;

class SystemContext implements Context
{
    /**
     * @var Application
     */
    private $application;

    /**
     * @var BufferedOutput
     */
    private $output;

    public function __construct(Kernel $kernel)
    {
        $this->application = new Application($kernel);
        $this->output = new BufferedOutput();
    }

    /**
     * @Given I am the system
     */
    public function iAmTheSystem()
    {
        Assert::same('cli', php_sapi_name());
    }

    /**
     * @When I greet the world
     */
    public function iGreetTheWorld()
    {
        $this->runCommand('app:hello-world');
    }

    /**
     * @Then I should say :sentence
     */
    public function iShouldSay($sentence)
    {
        $output = $this->output->fetch();

        Assert::same($sentence, $output);
    }

    private function runCommand(string $command)
    {
        $input = new ArgvInput(['behat-test', $command, '--env=test']);
        $this->application->doRun($input, $this->output);
    }

Happy hackin’!

Versions used for examples: Symfony 4.1, Behat 3.4, Behat Symfony2 Extension 2.1.
Tags: symfony, behat, commands, bdd, testing, php.
Categories: Programming, Development.

Easier Mocking With Mockery in php[architect]

published on April 20, 2018.

In early February I got an email from Oscar would I be willing to write an article for php[architect], based on my “Easier mocking with Mockery” talk. It took me maybe 2 seconds to think about it and say “Yes!”. The first part of the article was published in the April’s edition of the magazine. Lots of useful content in there, not just my article!

Initially I was supposed to write a 3000 word article on the subject, but in the end I wrote somewhere around 8000 words. Oops. Oscar was kind enough to let me keep the entire thing, so it was split into a two part article. I really enjoyed writing it and working with the fine folks from php[arch] on was great. I hope I get to write more for them.

If you have an idea for a PHP related article, I wholeheartedly recommend reaching out to them. It is really a great feeling to be a published author, even if it’s “just” 3000 words.

Happy hackin’!

Tags: php, mockery, article.
Categories: Blablabla.

Docker containers for PHP with PHPDocker.io

published on March 27, 2018.

This past weekend I was playing around on some pet projects and wanted to get up and running quickly. My initial reaction was to reach for a Vagrant box provisioned with Ansible. After all, that’s what I’ve been using for a really long time now.

Recently I’ve been also learning a bit more about Docker, so I figured maybe this pet project would be a good project to replace the standard Vagrant set up and go with Docker instead. When it comes to Docker, by now I believe I have a fairly good understanding of how it works and how it’s meant to be used in a development environment. I’ve learned a lot about it from Vranac, as well as poked around it on my own.

While trying to write a set of Docker files and Docker compose files for this project, I thought there must be an easier way to do this… And then I remembered that some time ago I came across a generator to generate Docker environments for PHP projects: PHPDocker.io. As they state on their website:

PHPDocker.io is a tool that will help you build a typical PHP development environment based on Docker with just a few clicks. It supports provisioning of the usual services (MySQL/MariaDB, Redis, Elasticsearch...), with more to come. PHP 7.1 is supported, as well as 7.0 and 5.6.

Click-click-click… done.

What I like about PHPDocker is that it takes a couple of clicks and filling out a couple of text fields to get a nice zip with all the things needed to get a project up and running. It has support for a “generic” PHP application, like Symfony 4, Zend Framework and Expressive, or Laravel, as well as for applications based on Symfony 23, or Silex. PHP versions supported range from 5.6 to 7.2 and a variety of extensions can be additionally enabled. Support for either MySQL, MariaDB, or Postgres is provided, as well as a couple of “zero-config” services like Redis or Mailhog.

The zip file comes with a phpdocker directory that holds the configurations for the specific containers such as the nginx.conf file for the nginx container. In the “root” of the zip there’s a single docker-compose.yml file which configures all the services we told PHPDocker we need:

docker-compose.yml

###############################################################################
#                          Generated on phpdocker.io                          #
###############################################################################
version: "3.1"
services:

    mysql:
      image: mysql:5.7
      container_name: test-mysql
      working_dir: /application
      volumes:
        - .:/application
      environment:
        - MYSQL_ROOT_PASSWORD=root
        - MYSQL_DATABASE=test
        - MYSQL_USER=test
        - MYSQL_PASSWORD=test
      ports:
        - "8082:3306"

    webserver:
      image: nginx:alpine
      container_name: test-webserver
      working_dir: /application
      volumes:
          - .:/application
          - ./phpdocker/nginx/nginx.conf:/etc/nginx/conf.d/default.conf
      ports:
       - "8080:80"

    php-fpm:
      build: phpdocker/php-fpm
      container_name: test-php-fpm
      working_dir: /application
      volumes:
        - .:/application
        - ./phpdocker/php-fpm/php-ini-overrides.ini:/etc/php/7.2/fpm/conf.d/99-overrides.ini

Run docker-compose up in the directory where the docker-compose.yml file is located and it’ll pull and build the required images and containers, and start the services. The application will be accessible from the “host” machine at localhost:8080, as that’s the port I defined I wanted exposed in this case. You can see that in the ports section of the webserver service.

One thing I noticed is that the mysql service doesn’t keep the data around, but that can be fixed by adding a new line to the volumes section of that service: ./data/mysql:/var/lib/mysql. The mysql service definition should now read:

    mysql:
      image: mysql:5.7
      container_name: test-mysql
      working_dir: /application
      volumes:
        - .:/application
        - ./data/mysql:/var/lib/mysql
      environment:
        - MYSQL_ROOT_PASSWORD=root
        - MYSQL_DATABASE=test
        - MYSQL_USER=test
        - MYSQL_PASSWORD=test
      ports:
        - "8082:3306"

Other than this, I didn’t notice any issues (so far) with the environment.

Inside the phpdocker folder there’s also a README file that provides additional information how to use the generated Docker environment, what services are available on what port, a small “cheatsheet” for Docker compose, as well as some recommendations how to interact with the containers.

Happy hackin’!

Mockery partial mocks

published on January 28, 2018.

In dealing with legacy code I often come across some class that extends a big base abstract class, and the methods of that class call methods on that big base abstract class that do an awful lot of things. I myself have written such classes and methods in the past. Live and learn.

One of the biggest problems with this kind of code is that it is pretty hard to test. The methods from the base class can return other objects, have side effects, do HTTP calls…

A typical example of this would be a base model class that has a getDb() method:

AbstractModel.php

<?php

abstract class AbstractModel
{
    protected $db = null;

    protected function getDb()
    {
        if ($this->db == null) {
            $db = Config::get('dbname');
            $user = Config::get('dbuser');
            $pass = Config::get('dbpass');
            $this->db = new PDO('mysql:host=localhost;dbname='.$db, $user, $pass);
        }
        return $this->db;
    }
}

which can be called in child classes to get access to the database connection:

ArticleModel.php

<?php

class ArticleModel extends AbstractModel
{
    public function listArticles()
    {
        $db = $this->getDb();
        $stmt = $db->query('SELECT * FROM articles');

        return $stmt->fetchAll();
    }
}

If we want to write unit tests for this listArticles() method, the best option would probably be to refactor the models so that the database connection can be injected either through the constructor, or with a setter method.

In case refactoring is not an option for whatever reason, what we can do is to create a partial mock of the ArticleModel using Mockery and then mock (well, stub to be more precise) out only the getDb() method that will always return a mocked version of the PDO class:

tests/ArticleModelTest.php

<?php

use Mockery\Adapter\Phpunit;

class ArticleModelTest extends MockeryTestCase
{
    public function testListArticlesReturnsAnEmptyArrayWhenTheTableIsEmpty()
    {
        $stmtMock = \Mockery::mock('\PDOStatement');
        $stmtMock->shouldReceive('fetchAll')
            ->andReturn([]);
        $pdoMock = \Mockery::mock('\PDO');
        $pdoMock->shouldReceive('query')
            ->andReturn($stmtMock);

        // Create a partial mock of ArticleModel
        $articleModel = \Mockery::mock('ArticleModel')->makePartial();

        // Stub the getDb method on the ArticleModel
        $articleModel->shouldReceive('getDb')
            ->andReturn($pdoMock);

        // List all the articles
        $result = $articleModel->listArticles();
        $expected = [];

        $this->assertSame($expected, $result);
    }
}

When we tell Mockery to make a partial mock of a class, any method on that partially mocked class that has expectations set up will be mocked, but calls to other methods Mockery will pass through the real class. In other words, even though the ArticleModel is a partial mock, anytime we call the listArticles() method Mockery will pass that call to the original method, and only the calls to the getDb() method are being mocked.

Using partial mocks should probably be an option of a last resort and we should always aim to refactor code to be easier for testing, but there are cases when they can really help us in testing legacy code.

Happy hackin’!

Prooph query bus

published on December 20, 2017.

Continuing on with the Prooph series, I want to take a look at the query bus of the Prooph service bus component. The query bus provides a way to issue a query (not necessarily a database query!) to a query handler. This handler is then responsible to return a result for our query.

Why would we need a query bus in the first place? While some may argue that the query bus is not really required, it can be a nice addition to complete the CQRS idea. Just as we have a single endpoint to handle all of our commands and events, we have a single endpoint that can handle all the queries.

The query bus allows the handler to do whatever it needs to do to return the result, synchronously or asynchronously. This is achieved by having the query bus return a ReactPHP Promise. The query handler itself will be a deferred unit of work, which allows it to promise to the querier that the query will be resolved or rejected sometime in the future.

Every query message we send with the query bus, must be routed to exactly one query handler on the other side. Of course, multiple query messages can be routed to the same handler.

Prooph’s service bus also supports a plugin system which we can use, for example, to have authorization of commands, events, and queries, logging… But more on that in a future blog post.

A quick example

The query message, just as a command or an event, can be pretty much anything — a primitive like a string or an integer, a custom data transfer object, or a class implementing the Prooph\Common\Messaging\Message interface by extending the Query class from the prooph-common library.

Setting up the query bus for using it is similar to setting up the command bus or the event bus:

  • we create the query bus,
  • we create a query router that the query bus uses to route query messages to query handlers,
  • we route a query message to its query handler,
  • we attach the router to the query bus,
  • and finally, we dispatch the query on the query bus.

Let’s see how it looks like in code:

query-bus.php

<?php declare(strict_types=1);

require_once 'vendor/autoload.php';

use Prooph\ServiceBus\Plugin\Router\QueryRouter;
use Prooph\ServiceBus\QueryBus;

$queryBus = new QueryBus();

$queryRouter = new QueryRouter();

$queryRouter->route('A simple string')
            ->to(new ProophExample\QueryHandler\Primitives());

$queryRouter->attachToMessageBus($queryBus);

$queryBus->dispatch('A simple string')
         ->done(function($result) {
            echo $result . PHP_EOL;
         }, function ($reason) {
            echo $reason . PHP_EOL;
         });

Not much going on but it shows how to set up and use the query bus.

The query handler part of this example looks like this:

src/ProophExample/QueryHandler/Primitives.php

<?php declare(strict_types=1);

namespace ProophExample\QueryHandler;

use React\Promise\Deferred;

class Primitives
{
    public function __invoke(string $query, Deferred $deferred)
    {
        $i = rand(1, 10);

        if ($i % 2 == 0) {
            $deferred->resolve(str_rot13($query));
        } else {
            $deferred->reject("Out of luck");
        }
    }
}

The query handler is an invokable that gets invoked with the string query and a React\Promise\Deferred unit of work, which we use to either resolve or reject the query.

While this example with the primitives gives an overall picture of how to use the query bus, it’s not really useful.

How many open CFPs are on JoindIn?

JoindIn has an open API which we can use to query it about events, like conferences and meetups. I think we can use it to show a better example of the query bus.

We’re going to have a query message that we’ll use to pass the type of the event we’re interested in — all, hot, upcoming, past, cfp — and a query handler that will assemble the URL for the API call and call it with a simple file_get_contents.

The query message for this example looks something like the following:

src/ProophExample/Query/JoindInEvents.php

<?php declare(strict_types=1);
namespace ProophExample\Query;

use Assert\Assertion;

class JoindInEvents
{
    private $type;
    public function __construct(string $type)
    {
        Assertion::choice($type, ['all', 'hot', 'upcoming', 'past', 'cfp']);
        $this->type = $type;
    }

    public function type(): string
    {
        return $this->type;
    }
}

We pass it in a string $type, assert that it is one of the expected values and set it as a class property. Really not much else to it than that.

The query handler will handle that query, issue the API call and resolve the React promise if it manages to decode the JSON response, or reject it if it fails:

src/ProophExample/QueryHandler/JoindInEvents.php

<?php declare(strict_types=1);

namespace ProophExample\QueryHandler;

use ProophExample\Query\JoindInEvents as Query;
use React\Promise\Deferred;

class JoindInEvents
{
    public function __invoke(Query $query, Deferred $deferred)
    {
        $url = 'https://api.joind.in/v2.1/events';

        $eventType = $query->type();

        if ($eventType != 'all') {
            $url .= '?filter=' . $eventType;
        }

        $response = file_get_contents($url);

        $jsonResponse = json_decode($response);

        if ($jsonResponse === null) {
            $deferred->reject("Error decoding json: " . json_last_error_msg());
        }

        $deferred->resolve($jsonResponse);
    }
}

In a real production code we’d probably use a proper HTTP client instead of file_get_contents, do more error checking and stuff, but in only a few lines of code we can see how to create a query handler.

To put it all together and call it, we’d have something like the following example:

query-bus.php

<?php declare(strict_types=1);

require_once 'vendor/autoload.php';

use Prooph\ServiceBus\Plugin\Router\QueryRouter;
use Prooph\ServiceBus\QueryBus;

$queryBus = new QueryBus();

$queryRouter = new QueryRouter();

$queryRouter->route(ProophExample\Query\JoindInEvents::class)
            ->to(new ProophExample\QueryHandler\JoindInEvents());

$queryRouter->attachToMessageBus($queryBus);

$queryBus->dispatch(new ProophExample\Query\JoindInEvents('cfp'))
         ->done(function($result) {
            echo sprintf("There are %d CFPs!", $result->meta->count) . PHP_EOL;
         }, function($reason){
            echo $reason . PHP_EOL;
         });

If the query message was resolved by the query handler we print out how many CFPs are there right now, and if the query handler rejected the query message, we print out the reason of rejection.

As with the command and the event bus, the examples seen here are available in my prooph-examples repository.

Happy hackin’!

P.S.: Thanks to Alexander Miertsch for helping me understand the query bus a little more!

Robert Basic

Robert Basic

Software engineer, consultant, open source contributor.

Let's work together!

If you require outsourcing or consulting help on your projects, I'm available!

Robert Basic © 2008 — 2019
Get the feed