Generate your functional tests with PHPUnit

DI DIO Maximilien
Feb 10, 2019 · 3 min read
Image for post
Image for post

Hello ! Did you have to test a specific routes of your amazing Symfony project ? Like a group a GET which must returned 200 http status code ? Ok i’ve a solution for you !

So, imagine the context. You have to test a existing project built on top of Symfony with lot of bundles. Your job is to test if the back office seems to be ok. (200)

But you have an amazing number of routes to tests and you doesn’t have to spend time to verify the work of each other. To test differents routes, the documentation is pretty clear. With PHPUnit, you have to create a provider function to repeat a test for each route

// tests/ApplicationAvailabilityFunctionalTest.php
namespace App\Tests;

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

class ApplicationAvailabilityFunctionalTest extends WebTestCase
{
/**
* @dataProvider urlProvider
*/
public function testPageIsSuccessful($url)
{
$client = self::createClient();
$client->request('GET', $url);

$this->assertTrue($client->getResponse()->isSuccessful());
}

public function urlProvider()
{
yield ['/'];
yield ['/posts'];
yield ['/post/fixture-post-1'];
yield ['/blog/category/fixture-category'];
yield ['/archives'];
// ...
}
}

But you have to set each route yourself. But in our scenario, we have lot of tests and probably a short deadline. However, according to the documentation, we can import the Symfony Router object to test a route by its name.

// ...
private $router; // consider that this holds the Symfony router service

public function testBlogArchives()
{
$client = self::createClient();
$url = $this->router->generate('blog_archives');
$client->request('GET', $url);

// ...
}

Okay ! By we have to set the name of each route. This solution is clearly better because the test is always ok even if you change the URL of the route. But i can accept this solution for our scenario.

The solution ? Use the Router to get all routes of your project. Then you can apply a filter to keep what you want to test.

class UserTest extends WebTestCase {
....
/**
*
@var ContainerInterface
*/
protected static
$container;

/**
*
@var array Route
*/
protected static
$routes;


private function cleanUrl(Route $route, bool $ssl) : string {
$http = $ssl ? 'https://' : 'http://';
return $http . $route->getHost() . $this->replaceId($route->getPath());
}

private function routes() {
$router = self::$container->get('router');
$collectionRoute = $router->getRouteCollection();
$routes = $collectionRoute->all();

self::$routes = array();

foreach ($routes as $key => $route) {
$isForAdmin = preg_match('/^pathofexile/', $key);
$methods = $route->getMethods();
$isGet = empty($methods) || in_array("GET", $methods);

if ($isForAdmin && $isGet) {
self::$routes[$key] = [
$this->cleanUrl($route, false),
Response::HTTP_OK
];
}
}
}

public function urlProvider() {
self::bootKernel();
self::$container = self::$kernel->getContainer();
self::routes();
return self::$routes;
}

/**
*
@dataProvider urlProvider
*/
public function
testGet(string $url, int $expected) {
$response = self::$client->get($url);
$this->assertEquals($response->getStatusCode(), $expected);

}
}

I call the bootKernel method inside the provider because… see this.

Okay but you can have some problems with this solutions to test routes with specifics parameters and you have to cover each case for each route. Your application must never send back an error 500 (server side error) if user provide wrong parameters. Stay aware about this method to test your project.

Thx for reading this and see you soon.

Protocod

stupid dev blog

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store