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ย write,ย verify, 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
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 doubles,ย method stubs,ย method 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
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
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
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).
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.
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.
The State of Testing in PHP in 2018…
excellent writeup. You covered all the giants really well and even shout outs to some of the folksinvolved. If you want to try some unit testing with something lighter weight, checkout https://github.com/bitslip6/tinytest. Single file test suite, supports data providers, code coverage (xdebug not required), expected exceptions, code annotations, etc.