Projections for PHPUnit Coverage Report

Recently at my company we’re pushing for more automated testing and one of the metrics we’re looking at is, of course, code coverage. Although I’m not a big fan of code coverage as a metric, it at least gives you a general idea how well you’re doing. If you’ve worked with PHPUnit before, you definitely have generated a code coverage report for a project. These reports are great, because you can easily spot the parts in your code missing test coverage and tackle these.

At my company, our project is a large – like 10k+ PHP files large – and we have multiple teams working on different areas of that codebase. This makes it a bit tricky when it comes to code coverage, because although it’s great to know how well we’re doing overall, me as a team lead would also like to know how well my team is doing. Besides that, we have different architectural layers and requirements regarding code coverage are different for each.

Wouldn’t it be great to have a dedicated report for each team or each layer?

The most obvious solution to this problem would be to have different phpunit.xml configs whith code coverage whitelist/exclude rules for the files you’re interest in. Yes, that would work, but it is not very efficient to run the tests X times to generate each of those reports and maintain all these config files.

I thought there must be a better way to do this …

At our company we have automated code attribution, which means that each file in the project can be attributed to a team. In addition, we follow some conventions that require code from specific architectural layers to be located under specific paths. This gives us a handy set of file paths patterns, which can be mapped to a team or architectural layer. Great, so we have a list of file path patterns that we’re interested in.

Next thing we need is a code coverage report. Not a normal one, but something that is machine readable. For this use-case PHPUnit supports a PHP report, which is a dump of all the coverage data collected. You can get it by adding --coverage-php=coverage.php when executing PHPUnit.

Now we need to get this all together, and all we need is a little script that takes this data, filters it with the file path patterns and generates a coverage report from it. Thankfully, PHPUnit is build in a very modular way, so we can do this. Here’s an example:

<?php
use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Report\Html\Facade;

/** @var CodeCoverage $codeCoverage */
$codeCoverage = require 'coverage.php';

$filterFunction = function (string $filePath): bool {
    return true; // Here you need to make the decision if the file should be in the report or not
};

$whiteListedFilesFiltered = array_filter($codeCoverage->filter()->getWhitelistedFiles(), $filterFunction, ARRAY_FILTER_USE_KEY);
$dataFiltered = array_filter($codeCoverage->getData(), $filterFunction, ARRAY_FILTER_USE_KEY);

$coverageFiltered = new CodeCoverage;
$coverageFiltered->setData($dataFiltered);
$coverageFiltered->setTests($codeCoverage->getTests());
$coverageFiltered->filter()->setWhitelistedFiles($whiteListedFilesFiltered);

// Generate the HTML report for the projected code coverage
$writer = new Facade;
$writer->process($coverageFiltered, $targetDir);

Voilà, there we have our projected HTML code coverage report.

Since you have all the code coverage data still in coverage.php, you can have as many projections as you want.

Introducing Tombstones for PHP

Earlier this year I took over that project at my new company. A project, that existed for many years and has been continuously growing. My first impression, it was missing some love recently. The repository was cluttered by many files, that could assumed to be dead code. Unfortunately, you never know. Although I felt the urgent need of removing stuff, I was able to keep myself from blindly deleting files and breaking everything ;). The mission was clear: Cleaning up the project, without breaking things.

Read more

Customizing FOSUserBundle

Recently I was working on a project with FOSUserBundle. It was best choice instead of implementing the whole user system from scratch. Unfortunately, after a while it became clear, that the bundle doesn’t fulfil all of my requirements and therefore I had to make some modifications. I’ve found the solutions not to be totally obvious, so I decided to write it together in this blog post. Another post on FOSUserBundle is my tutorial how to change the password constraints in FOSUserBundle.

Note: This post is about the 1.3 version of FOSUserBundle. Version 2.0 is not stable yet, but it has much better extensibility.

The first thing to do, when extending the bundle, is to create a new bundle, which extends the original FOSUserBundle. This will enable you to overwrite controllers and configuration files with your own implementation. I’ll call it Acme\UserBundle in the following examples.

Read more

Filter Tabs in Sonata Admin

When working regularly with Sonata bundles, you’ve might come accross their MediaBundle, which integrates a media management module. You certainly recognized that there is something different in the list view: A tabbed navigation to filter media items. This is how it looks like in the current 2.3 branch:

filter-tabs

Screenshot of the Sonata Demo

I think this is very useful, because you can filter very quickly – literally with one click – instead of wasting three steps on the datagrid (focus widget, select/type value, click filter button). Unfortunately the tab feature is not documented, so I tried to figure out how it works. Here is a tutorial how you can add filter tabs to your own Sonata Admin modules.

Read more

PHPUnit: Remove non-deterministic dependencies

Unit testing is great! But sometimes there are situations where it can become really tough to write proper tests for your code. One of these situations is when your code doesn’t work totally predictable, when it has some kind of randomness in it, that is intended. Then you usually have one of those functions in your code:

  • tempnam(), uniqid()
  • rand(), mt_rand(), shuffle() or any other randomized function
  • time(), date(), new \DateTime() or anything that has to do with the current time

The list isn’t complete, just wanted to mention the most common ones. Those functions are bad for testing, because tests have to be repeatable and those ones will most likely produce a different result on every call.

So how to get rid of the randomness and create predictable and therefore repeatable unit test?
Read more

Say Hello to SchebTwoFactorBundle

I’ve recently done some posts about how to integrate two-factor authentication and Google Authenticator into Symfony2. I thought to myself “Why not make a bundle from it?” and that’s what i did: Say hello to SchebTwoFactorBundle. Now it’s dead simple to add two-factor authentication to your own website. Add the bundle via Composer and enable the authentication method you want.

Currently it supports both the email and the Google Authenticator method I’ve blogged about. It already has some features for customization, but I’m thinking about making it more flexible. Maybe some generic two-factor implementation, which allows you to plug in different kinds of two-factor modules.

 

Google Authenticator in Symfony

If you want to add two-factor (2fa) authentication with Google Authenticator to your project, please use the scheb/2fa bundle for Symfony.
The approach in this blog post is no longer valid and potentially harmful to your application’s security. So don’t do it. Use the bundle instead.


This is the follow up to previous post about two-factor authentication in Symfony2. As promised I also want to show you how to integrate Google Authenticator into your project. If you haven’t read my first post, I’d suggest doing it now, because it explains the principle more in detail. The following example code is widely identical to SonataUserBundle‘s integration.

To get started, you’ll have to install the Sonata Google Authenticator package. If you’re using composer (I guess so), you can simply execute:

php composer.phar require sonata-project/google-authenticator dev-master

Read more

Two-Factor Authentication (2fa) in Symfony

If you want to add two-factor (2fa) authentication to your project, please use the scheb/2fa bundle for Symfony.
The approach in this blog post is no longer valid and potentially harmful to your application’s security. So don’t do it. Use the bundle instead.


For a project of mine I wanted to have some extra security because it contains critical features, only authorized people should have access to in any case. So I did some research if it’s possible to implement two-factor authentication in Symfony2. Sadly I didn’t find any good how-tos about that topic. Then I’ve found out that SonataUserBundle has Google Authenticator as an optional feature, so I did some reverse enginering to figure out how they did it.

This is how you implement two-factor authentication into Symfony2’s security layer. The following example will send out a random code to the user’s email address. I will do another post for Google Authenticator soon.

Read more

Password constraints in FOSUserBundle

I’ve recently tried to modify the password constraints of FOSUserBundle. To my surprise I’ve discovered that this is a little bit tricky.

Before you start I would suggest taking a look at validation.xml in the config directory of FOSUserBundle There you’ll find all the pre-defined constraints. As you can see, there is a class named FOS\UserBundle\Form\Model\ChangePassword. This is the data class, which is used in the change password form instead of the actual entity. So you have to change the password constraints on the User as well as on the ChangePassword class.

I have a bundle, which is extending FOSUserBundle, so I thought it is straight forward: Create a validation.xml which is overwriting the original one and put my own constraints in there. Unfortunatley that doesn’t work, instead my own constraints will simply be added to the default ones. So how to get rid of them? The trick is to define your own validation group.

This is how my configuration for the ChangePassword class looks like. The same goes for the User class.


    
        
            
            
        
        
        
        
            
            
            
        
    

By default FOSUserBundle is using the ChangePassword group to validate the change password form. With some extra lines in config.yml you can tell it to use a different one:

fos_user:
    change_password:
        form:
            validation_groups: [MyChangePassword, Default]

Now FOSUserBundle is using the MyChangePassword validation group for validation. This is also working for other forms like registration or the user profile. Take a look at the class FOS\UserBundle\DependencyInjection\Configuration and search for validation_groups to find out more about the configuration.

Default Values for Parameters

Sometimes it makes sense to have a default value for parameters. This can be done by creating a new file parameters_default.yml (can be any name you like) and adding it to config.yml just before parameters.yml.

imports:
    - { resource: parameters_default.yml }
    - { resource: parameters.yml }
parameters:
    locale: en
    secret: abcdefg1234567890

Now you can overwrite the default values in parameters.yml on demand but you’re not longer forced to define those parameters.

parameters:
    locale: de