Now We Need to Write our First PHPUnit Tests¶
We need to have at least one test in our project in order to run QA
We will start with some basic "Smoke Test" level testing which add a useful safety net and sanity check to our project.
HTTP Smoke Testing¶
This is a smoke testing framework that will automatically test all configured routes
For the smoke testing system, we need to edit composer.json directly
"require-dev": {
"shopsys/http-smoke-testing": "dev-master@dev"
},
"repositories":[
{
"type": "vcs",
"url": "git@github.com:edmondscommerce/http-smoke-testing.git"
}
]
Writing First Tests¶
We need at least one unit test and get it passing
Before we can do that, we need to handle basic configurations
Now Write Our First Test¶
We will start with a "Service Container Smoke Test" that simply loads every service
Abstract Service Test¶
First of all, we need an abstract container test case
<?php declare(strict_types=1);
namespace MyProject\Tests\Assets;
use MyProject\Helper\EnvironmentHelper;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Yaml\Yaml;
/**
* This abstract test uses the real symfony container but in a test friendly mode that allows all services to be
* retrieved even if they are private
*
* @see https://symfony.com/blog/new-in-symfony-4-1-simpler-service-testing
*/
abstract class AbstractServiceTest extends KernelTestCase
{
protected function setup(): void
{
$this->ensureServicesTestExistsAndIsValid();
}
/**
* We need to ensure that services are public
* We also need to ensure that the services_test.yml file is up to date with the main services.yml file
*/
private function ensureServicesTestExistsAndIsValid(): void
{
$servicesPath = __DIR__ . '/../../config/services.yaml';
$servicesTestPath = __DIR__ . '/../../config/services_test.yaml';
self::assertFileExists($servicesPath);
self::assertFileExists($servicesTestPath);
$servicesContents = \ts\file_get_contents($servicesPath);
$servicesTestContents = \ts\file_get_contents($servicesTestPath);
$servicesYaml = Yaml::parse($servicesContents);
$servicesTestYaml = Yaml::parse($servicesTestContents);
self::assertTrue($servicesTestYaml['services']['_defaults']['public']);
$servicesTestYamlWithoutPublic = $servicesTestYaml;
$servicesTestYamlWithoutPublic['services']['_defaults']['public'] = false;
self::assertSame(
$servicesYaml,
$servicesTestYamlWithoutPublic,
'Your services_test does not seem to be up to date with your services.yml'
);
}
protected function getProjectDir(): string
{
return $this->getContainer()->get(EnvironmentHelper::class)->getProjectDir();
}
/**
* Use this method to load your services.
*
* You will need to explicitly type hint your service as a class property
*
* WARNING - if you are testing a service that is not injected in any other class, it will have been removed from
* the container and you will not be able to load it
*
* @return ContainerInterface
*/
protected function getContainer(): ContainerInterface
{
if (null === static::$container) {
static::bootKernel();
}
return static::$container;
}
}
Service Smoke Test¶
The service smoke test simply ensures we can fire up services without exceptions being thrown
We explicitly remove it from code coverage as it doesn't really test anything as such.
<?php declare(strict_types=1);
namespace MyProject\Tests\Large;
use MyProject\Tests\Assets\AbstractServiceTest;
/**
* @large
* @coversNothing
*/
final class AllServiceSmokeTest extends AbstractServiceTest
{
/**
* @test
* @dataProvider provideService
*
* @param string $service
*/
public function itCanLoadAllServices(string $service): void
{
$object = $this->getContainer()->get($service);
self::assertNotNull($object);
}
/**
* @return array<string,array<string>>
*/
public function provideService(): array
{
$this->setupForDataProviders();
$container = $this->getContainer();
$data = [];
foreach ($container->getServiceIds() as $service) {
$service = (string)$service;
$data[$service] = [$service];
}
return $data;
}
}
Controller Smoke Test¶
Like the all services test, this is a large test and it simply kicks the tyres on each route.
It will automatically test new routes as they are added to configurations.
Routes are likely to require a little configuration to get the test working
TODO: write more docs on the controller smoke test somewhere else
<?php declare(strict_types=1);
namespace ProjectName\Tests\Large;
use ProjectName\Controller\IndexController;
use Shopsys\HttpSmokeTesting\HttpSmokeTestCase;
use Shopsys\HttpSmokeTesting\RouteConfigCustomizer;
/**
* @large
* @coversNothing - smoke tests should not contribute towards coverage
*/
final class AllControllersSmokeTest extends HttpSmokeTestCase
{
private const EXPECT_CODE_ROUTES = [
IndexController::ROUTE_INDEX_NAME => 302,
];
/**
* This method must be implemented to customize and configure the test cases for individual routes
*
* @param RouteConfigCustomizer $routeConfigCustomizer
*/
protected function customizeRouteConfigs(RouteConfigCustomizer $routeConfigCustomizer): void
{
foreach (self::EXPECT_CODE_ROUTES as $routeName => $expectedCode) {
$this->setExpectedCodeForRoute($routeConfigCustomizer, $routeName, $expectedCode);
}
}
}