01 Introduction
Unit Testing Concepts¶
Unit Tests Overview¶
The objective of Unit Testing is to ensure the code in the application behaves exactly as intended.
PHPUnit Tests do this using PHP code written to call the methods in your application, and running tests ("assertions") that what comes back is what's expected.
A simple pseudo-code example of this is:
...
public function addingNumbersWorks() : void
{
$expected = 5;
$actual = MathsOperations::add(3, 2);
$this->assertSame($expected, $actual);
}
...
Each assert-
method is considered to be one test, and has its own definition of what passes and what fails.
Using PHPUnit¶
PHPUnit provides a framework to implement these Unit Tests by providing classes to inherit from.
To write Unit Tests using PHPUnit, you just create a class that inherits PHPUnit's TestCase
class:
<?php
// Make sure your test class extends the PHPUnit TestCase
class MathsOperationsTest extends \PHPUnit\Framework\TestCase
{
public function addingNumbersWorks() : void
{
// Load up the application class to be tested
$mathsOperations = new \App\Maths\MathsOperations();
// Call the method and capture the result
$actual = $mathsOperations->add(3, 2);
// Specify what you expect the method to return
$expected = 5;
// The test passes and fails based on if $expected is the same as $actual
$this->assertSame($expected, $actual);
}
}
Test Types¶
The traditional test types are "unit", "integration" and "functional", with each test type having in theory a defined purpose. In reality though we've found their definitions and rules contentious and unclear.
Instead we use "small", "medium" and "large", as inspired by this blog post
The tests are defined by what is NOT allowed to be done.
- The test should always be in the smallest name possible
- For each class and method, there should always be at least some small tests, for example testing what happens when invalid parameters are fed in.
Each Test Class should mention which type it is using either an @small
, @medium
, @large
annotation at the top of the class.
Small¶
Should be very fast and test a small piece of functionality, eg a single method.
The test and the code being tested can not:
- make any network or web requests
- access a database
- access a caching system (eg Redis)
- write any changes to the filesystem
A single small test should execute in less than 1 second
Small tests should have no side effects or cleanup required
Medium¶
Medium test are generally a bit slower and have fewer restrictions
They can:
- access the database (localhost or not)
- access the filesystem
- access caching services (localhost or not)
They can not:
- access anything on the network that is not localhost
A single medium test should execute in less than 5 seconds
Large¶
Large tests have no restrictions and can run for an effectively unlimited amount of time, though of course you should always try to make your tests as fast and efficient as possible.
By default, the PHPQA configuration is 300 seconds which should be more than enough for a single test to run.
They can directly access as much or as little of the code as required.
Generally they are testing large pieces of functionality as a whole.
Naming Conventions¶
Test Namespace and Class Names¶
These are in the format Tests\{Test_Type}\{Path}\{To}\{Application}\{Namespace}
.
So a Small test for the App\Maths\MathsOperator
class will be under the App\Tests\Small\Maths
namespace.
These should be named the same as the application class you're testing, suffixed with Test
So a Small Unit Test testing the App\Maths\MathsOperations
class should be named App\Tests\Small\Maths\MathsOperationsTest
, and stored in tests/Small/Maths/MathsOperationsTest.php
Test Method Names¶
There are two conventional ways to name tests:
- Mapping test methods to application methods by naming the test method the same as the application method, and prefixing it with
test
- Writing methods that describe what they're doing, and using an
@test
annotation to indicate that these are to be run as tests.
We've chosen the second one. Combined with the required method naming, this prints out more verbose test outputs which gives good visibility on what is happening. This is detailed in the PHPUnit TestDox documentation
If you're aiming for the Gold standard, you can additionally link up the test to specify which Application method is being tested. This is detailed later, but is optional for the Bronze and Silver levels.
Variables¶
Your standard checks should use the variables $expected
and $actual
When making multiple assertions in one test, you can disambiguate using either $expectedFoo
and $actualFoo
, or $notExpected
as required.