22 \ December 2018 \ http://www.phparch.com
How to Learn PHP Unit Testing With Katas
production code I’m going to write and a tests.php file for
my tests.
PHPUnit comes with an autoloader, and if we use this set
up we can run our tests using a globally configured PHAR
with the following command:
phpunit --colors --bootstrap src.php tests
Strictly speaking, we don’t need the --colors option turned
on, but I like my greens to be green and my reds red. Then
we’re specifying for it to load up (or bootstrap) both our
production code and our tests. Doing so allows us to keep
those both as simple as possible, and allows us to have no
dependence between the two files.
When you have a larger test suite, --bootstrap can be used
to execute a file which executes all the code needed for your
code to run. At the very least, this could be vendor/autoload.
php, which is necessary with a Composer-installed PHPUnit.
In this circumstance you’ll run your tests (assuming an other-
wise similar file layout) with:
./vendor/bin/phpunit --colors --bootstrap \
vendor/autoload.php tests
Writing Your First Test
When writing tests using PHPUnit we start by extending
PHPUnit\Framework\TestCase. It’ll look like this:
<?php
use PHPUnit\Framework\TestCase;
class TestSrc extends TestCase {
}
PHPUnit provides a base test class. This class is most useful
for its assertions (which we’ll cover momentarily) and has
setUp and tearDown functionality (which allow you to run
specific code before each test, or after it; we’ll not say more
about it in this article).
Next, we’ll write our first test. Our tests are methods on the
class we extends from PHPUnit. The test runner expects these
to start with test, although there are other ways to run them
if you insist on not doing so.
Our first method will look a little like:
function test1yieldsI()
{
$this->assertEquals('I', arabicToRoman( 1 ));
}
In general, when people describe what you put in a test,
they’ll talk about the three “A”s (Arrange-Act-Assert). What
they mean is it’s constructive, especially in more complex
testing conditions, to set up your production code first, then
run the code, and finally assert what is true after the code has
run.
For simple tests, I typically skip this structure. However, if
you want to hew to it, a “best practice” first test might look
more like Listing 1.
The most important thing here is in the “act” and “assert”
phases. What might be confusing about the “act” is we’re
calling a function called arabicToRoman without any knowl-
edge it exists. We did the same thing above in the shorter
version. When you’re writing tests first, you’ll be doing this
constantly: calling new methods or functions you’ve not yet
written. Many people advocate TDD for precisely this reason;
it makes you think about how you want to invoke your code
before you start writing it.
Our assert is also interesting. As mentioned, PHPUnit is
a helpful bucket of assertions you can use. For almost all
my tests when doing the Roman numeral kata, I merely use
$this->assertEquals. There are a whole bunch more as well.
Verifying Our Test Fails
To run the test, we’ll use the command from above.
phpunit --colors --bootstrap src.php tests
If you do (with the longer test form from above) you’ll get
the following output (trimmed for conciseness):
There was 1 error:
1 ) TestSrc::test1yieldsI
Error: Call to undefined function arabicToRoman()
tests.php:11
We expected and discussed this error. There’s no code in
our “production” set yet, so the arabicToRoman function we’re
calling is never defined. We need to write it.
Writing the Production Code
My first step is to create the empty function body in src.php.
function arabicToRoman($arabic)
{
}
When I do, the error changes:
1) TestSrc::test1yieldsI
Failed asserting that null matches expected 'I'.
tests.php:13
Listing 1
- function test1yieldsI()
- {
- // arrange
- $startingArabic = 1;
- $expectedRoman = 'I';
- // act
- $receivedRoman = arabicToRoman($startingArabic);
- // assert
- $this->assertEquals($expectedRoman, $receivedRoman);
- }