1

New Research Report - Exploring the 2024 State of Software Quality

Group 370
2

Codacy Product Showcase: January 2025 - Learn About Platform Updates

Group 370
3

Join us at Manchester Tech Festival on October 30th

Group 370

The State of PHP Testing in 2018

In this article:
Subscribe to our blog:

Testing code is an essential aspect of writing software of any level of quality.
It’s essential because it helps us know the code works as it should; whether it’s a specific unit of functionality or the application as a whole from an end user’s perspective.  In this article we will address the status of PHP testing from a variety of perspectives.

From testing languages to people working behind the scenes, I intend to show you it’s an excellent time for testing in PHP right now. Dive in, have a read and share your thoughts in the comments.

Scalar Type-hinting and Return Type Declaration Support

It might seem like a bit of a strange way to start talking about the state of testing, by talking about PHP’s scalar type-hinting and return type declaration support.

However, without this addition I wouldn’t consider the state of testing to be as mature as it now is. If you’re not familiar with scalar type-hinting and return type declarations in PHP, take a look at the following code.

<?php
class Document
{
  public function titlecase(string $text): string
  {
    if (strlen($text) === 0) {
      return;
    }
    return preg_replace_callback(
      self::SEARCH_REGEX,
      function ($match) {
        $tmp = preg_replace_callback(
          sprintf(‘/\b(%s)\b/i’, implode(‘|’, self::SMALL_WORDS)),
          function ($m) {
            return strtolower($m[0]);
          },
          $match[2]
        );
        return sprintf(‘%s[%s]’, $match[1], $tmp);
      },
      $text
    );
  }
}

You can see there’s a class (‘Document’) with one method (‘titlecase’). The method takes one argument, ‘$text’, which must be a string. The function also returns a string.

Let’s consider what this means for the code and its usage. It’s clear you have to supply a string and the function returns a string. To be fair, we could have specified any other scalar type or a user-defined class or interface instead.

So at that level, nothing is particularly special about the use of scalar types.
However, when considered in aggregate, it’s a significant improvement. Why?

Because the code’s intent is more explicit than it was before. It signals its intentions much more concretely. From a validation perspective, the language can now check the types passed to and back from a function, so the need to do so in code is no longer there.

class FilenameToLabelConverterTest extends TestCase
{
  public function converterDataProvider()
  {
    $original = <<<EOD
modules/admin_manual/pages/appliance/ucs/add-groups-and-users.adoc[Add-groups-and-users]
modules/admin_manual/pages/appliance/Active_Directory.adoc[Active Directory]
modules/admin_manual/pages/appliance/Backup.adoc[Backup]
modules/admin_manual/pages/appliance/Collabora.adoc[Collabora]
modules/admin_manual/pages/appliance/clamav.adoc[Clamav]
modules/admin_manual/pages/appliance/howto-update-owncloud.adoc[Howto-update-owncloud]
modules/admin_manual/pages/appliance/index.adoc[Index]
modules/admin_manual/pages/appliance/installation.adoc[Installation]
modules/admin_manual/pages/appliance/managing-ucs.adoc[Managing-ucs]
modules/admin_manual/pages/appliance/what-is-it.adoc[What-is-it]
EOD;

$formatted = <<<EOD
modules/admin_manual/pages/appliance/ucs/add-groups-and-users.adoc[Add groups and users]
modules/admin_manual/pages/appliance/Active_Directory.adoc[Active Directory]
modules/admin_manual/pages/appliance/Backup.adoc[Backup]
modules/admin_manual/pages/appliance/Collabora.adoc[Collabora]
modules/admin_manual/pages/appliance/clamav.adoc[ClamAV]
modules/admin_manual/pages/appliance/howto-update-owncloud.adoc[Howto update ownCloud]
modules/admin_manual/pages/appliance/index.adoc[Index]
modules/admin_manual/pages/appliance/installation.adoc[Installation]
modules/admin_manual/pages/appliance/managing-ucs.adoc[Managing UCS]
modules/admin_manual/pages/appliance/what-is-it.adoc[What is it]
EOD;
    return [
      [
        $original,
        $formatted
      ]
    ];
 }
 /**
  * @dataProvider converterDataProvider
  * @param string $from
  * @param string $to
  */
  public function testCanCorrectlyConvertStringToTitlecase(
    string $from, string $to
  ) {
    $converter = new FilenameToLabelConverter();
    $this->assertSame($to, $converter->titlecase($from));
  }
}

Now let’s validate that assertion. Take the above tests as a jumping off point. You can see they only need to supply string test data.

There’s no need to supply data of any other data type, as the language ensures the function cannot accept it. As a result, the tests are shorter and take less time to writeverify, and maintain. What’s more, they are easier to read!

Should You Always Use Types?

I’m not saying they should always be used. I agree there are times and places for not using them. However, when used appropriately, they reduce the need for testing.

What’s more, there’s less testing and validation required, for checking the return values from function calls. If you’re not sure of what I mean, consider the above example without the use of scalar type-hinting and return type declarations.

In this scenario, the language cannot enforce only strings are supplied to the function. Given that, you would have to add a series of tests to ensure it returned the correct result when it was passed a string, and when it was passed any number of other data types.

  • Which situation is more precise, cleaner, and more maintainable?
  • Which situation results in higher developer satisfaction and less cost?

Taking this, albeit simplistic, example as a reference point, scalar type hinting and return types is the better choice to make. As a result of these features, it’s a lot easier to test in PHP than ever before.

Testing Frameworks

PHP has a large number of testing frameworks and tools available to choose from. Admittedly, of those only a few enjoy a significant level of support.
These include Behat, Codeception, Mockery, and PHPUnit (PHP’s de facto testing tool).

Let’s have a quick look at these now, to see what they offer.

Mockery

Mockery
Mockery

Modern testing can be quite an involved task, requiring a wide-variety of techniques and approaches for fully testing code, and Mockery is an excellent tool to help achieve that. If you have not heard of it before, here’s a short overview, from the project’s documentation:

Mockery is a simple yet flexible PHP mock object framework for use in unit testing with PHPUnit, PHPSpec or any other testing framework. Its core goal is to offer a test double framework with a succinct API capable of clearly defining all possible object operations and interactions using a human-readable Domain Specific Language (DSL).

It provides support for test doublesmethod stubsmethod call expectations, and test spies, along with a host of related functionality.

I admit that, at first, I found it a little confusing. However, after spending some time with it, it is an excellent tool to have in your testing tool belt.

If you’re keen to know more about it, check out the documentation, or Robert Basic’s article “Easier Mocking with Mockery” in this month’s edition of PHP[Arch] magazine.

Behat

Behat — A PHP framework for autotesting your business expectations.
Behat — A PHP framework for autotesting your business expectations.

Quoting Behat’s website:

Behat is an open source Behavior-Driven Development framework for PHP. It is a tool to support you in delivering software that matters through continuous communication, deliberate discovery, and test-automation.

Behat provides BDD (Behavior Driven Development) testing support, and structures tests in the “Context-Action-Outcome” or Gherkin format. It allows a human-readable feature file, such as in the following example, to guide the creation of an almost equally human-readable test suite.

Feature: Product basket
 In order to buy products
 As a customer
 I need to be able to put interesting products into a basket

 Rules:
 — VAT is 20%
 — Delivery for basket under £10 is £3
 — Delivery for basket over £10 is £2

Like Mockery, when I first started with Behat, I found it a bit confusing; perhaps this was because I was so used to using PHPUnit style of testing.
So to change from one style to another was a bit of a stretch. However, having said this, after getting over that initial hurdle, I have found it an excellent tool.

Codeception

Codeception — Elegant and Efficient Testing for PHP.
Codeception — Elegant and Efficient Testing for PHP.

Whereas Behat focuses exclusively on BDD-style testing, Codeception is a comprehensive testing suite. Self-described as “Elegant and Efficient Testing for PHP”, it:

  • Supports acceptance, functional, and unit tests
  • Supports BDD-style testing
  • Supports web services
  • Supports code coverage, parallel execution, and continuous integration
  • Has modules for integrating with AngularJS, Facebook, Laravel 5, MongoDB, Phalcon, Redis, REST and SOAP servers, Zend Framework versions 1 and 2, Zend Expressive, Yii, Symfony Joomla, and WordPress

In addition to these features, it has excellent command-line tooling support for bootstrapping a majority of this functionality. I’ve used it on several projects and can personally testify to just how thorough it is, and just how supportive the core development team is.

If you’re looking for an almost one-stop-testing-shop for PHP, then Codeception is the way to go. What’s more, the core development team have even recently started providing enterprise-level support.

PHPUnit

PHPUnit
PHPUnit

If you’ve been around PHP for any length of time — especially in recent years — then you’ll know about PHPUnit. PHPUnit is the granddaddy of testing tools for PHP, arguably the de facto standard.

After a quick glance at the manual shows you it has significant functionality. It shouldn’t come as a surprise it’s used by so many within the PHP community.

Its functionality includes:

  • Test fixtures
  • Database testing
  • Organizing tests
  • Test doubles
  • Logging
  • Code coverage analysis
  • Docblock annotations
  • Data providers
  • A massive number of assertions
  • A feature-rich command-line test running and an XML-based configuration file

In addition to this, it integrates with numerous other tools, such as Mockery.
If you’re on the lookout for the right tool to use to start testing your PHP code, PHPUnit is the place to start.

Static Code Analysis

Now that we’ve covered several of the most popular testing tools, let’s turn to static code analysis support. If you’re not familiar with the term, static code analysis is:

The analysis of computer software that is performed without actually executing programs. In most cases, the analysis is performed on some version of the source code, and in the other cases, some form of the object code.

By using them, we help ensure our code quality is improving, not declining, and that we can trace the source of bugs back to specific commits which introduced them.

I’m highlighting this topic because many professional services now actively support PHP, whether as their only language, such as RIPS, or as part of the languages which they support.

Moreover, this is significant, because for a long time PHP always seemed to be an afterthought in deference to “more respected” languages, such as Python, Java, C/C++, C#, and Perl. Now, almost every professional static code analysis service supports PHP.

As a side note: Codacy supports PHP as part of their offering.

Testing as a first-class citizen

The final reason why I believe the state of testing in PHP in 2018 is extremely positive, is that testing is a first-class citizen. I make this assertion for many reasons.

Firstly: all the leading frameworks support it. From Laravel, Zend Expressive, and Symfony, to Phalcon, Yii, FuelPHP, and Aura (just to name a few) all make it painfully easy to test applications which are built upon them.

Secondly: because of discussions on testing. If you go to any major conference, whether community or commercial; if you go to PHP meetups; if you read PHP magazines and blogs, you’ll read, see, and hear content about testing.

Whether you’re new to PHP or new to testing, whether you’re a more seasoned developer or tester, there’s content for you. You will always have an opportunity to begin, as well as to further build your skills.

Once, I agree, testing in PHP was seen as an afterthought by many. However, those days are gone.

It’s a testament to the hard work of many people

The state of testing in PHP has come along way and is, at least in my opinion, as good as any other language. That said, it would not be where it is today without the hard work of many in the community, but in particular Chris Hartjes (the Grumpy Programmer) and Sebastian Bergmann (the founder of PHPUnit).

Chris Hartjes, the Grumpy Programmer.

Let’s start with Chris (I’m slightly biased as I count Chris as a friend). He’s been banging the drum for PHP developers (as well as developers from all software development communities) to test their code for years.

Whether through his books, his PluralSight course, his conference speaking, or his Twitter presence, he’s been a tireless advocate for better testing for a long time now. Speaking from personal experience, I know I wouldn’t be testing my code as much as I do if it weren’t for his encouragement.

Sebastian Bergmann, creator of PHPUnit.

And to Sebastian, well what can you say, he created PHP’s de facto testing tool, PHPUnit.

If it weren’t for how easy PHPUnit is to install, configure, and use and for how feature-rich and flexible it is, I’d suggest there would be a lot fewer PHP developers testing their code than there is today.

What’s more, PHPUnit has laid the foundation for a number of the testing tools and frameworks that followed it, including Codeception, Mockery, and Behat. I’m not suggesting it did this directly, but I would suggest it helped make them possible.

Sebastian has been a tireless advocate of testing, whether through his writing, his conference speaking, or all the other ways in which he advocates for testing. So thank you to these two stand-out individuals. I know we’re all better off for their ongoing efforts.

And That’s a Wrap

This is a broad overview of the state of testing in PHP today. While many are quick to predict the demise of PHP, I believe now is one of the best times to use the language.

In addition to a language that is virtually as rich and mature as any other, PHP’s testing infrastructure makes it possible to develop rich and well-tested applications.

Whether you’re using PHP’s de facto tool, PHPUnit, or one of the others, such as Behat, Codeception, and Mockery, or whether you’re using several of them in combination, you have so much high-quality choice.

Combine this with a reduced need to test, because of PHP’s adoption of static type hinting, and those who, in the past, have hated on PHP, claiming it’s a double-ended hammer should revisit those previous assumptions and see they’re no longer valid.

If you’re a newcomer to PHP, what testing tools have you used and what successes have you had with them? If you’re an old hand, what combination of tools do you use, and why?

Find out more about Bitbucket Code Review


Codacy is used by thousands of developers to analyze billions of lines of code every day!

Getting started is easy – and free! Just use your  GitHub, Bitbucket or Google account to sign up.

GET STARTED

RELATED
BLOG POSTS

Navigating the World of SAST: What is Static Application Security Testing?
Static application security testing (SAST) is a core component of robust DevSecOps. By analyzing source code, bytecode, or binaries to pinpoint...
PHP Static Analysis Tools Review
Performing PHP static analysis will help maintain your code quality over time, particularly as it becomes even harder in large projects developed by...
Shift Left Testing: A Complete Guide 
Despite a constant influx of new tools aimed at helping software development teams become more productive, companies continue to struggle to optimize...

Automate code
reviews on your commits and pull request

Group 13