deozza/philarmony-api-tester-bundle
Installation
composer require deozza/philarmony-api-tester-bundle
Add the bundle to config/bundles.php:
return [
// ...
Deozza\PhilarmonyApiTesterBundle\PhilarmonyApiTesterBundle::class => ['all' => true],
];
Configure Database
Update .env.test (or .env for local testing) with a dedicated test database:
DATABASE_URL="mysql://user:pass@127.0.0.1:3306/your_test_db"
Or for SQLite:
DATABASE_URL="sqlite:///%kernel.project_dir%/var/test.db"
First Test Case
Create a test class extending PhilarmonyApiTesterBundle\Test\ApiTestCase:
namespace App\Tests;
use PhilarmonyApiTesterBundle\Test\ApiTestCase;
class UserApiTest extends ApiTestCase
{
public function testGetUsers()
{
$response = $this->get('/api/users');
$this->assertEquals(200, $response->getStatusCode());
}
}
Run Tests
php bin/phpunit
PhilarmonyApiTesterBundle\Test\ApiTestCase for built-in assertions and helpers.doctrine/doctrine-fixtures-bundle for test data setup (see example).Test Setup
setUp() to load fixtures:
protected function setUp(): void
{
$this->loadFixtures([
UserFixtures::class,
RoleFixtures::class,
]);
parent::setUp();
}
$this->authenticateAsUser($user); // If supported by the bundle.
API Testing Patterns
$response = $this->get('/api/users');
$response = $this->post('/api/users', ['name' => 'John']);
$response = $this->put('/api/users/1', ['name' => 'Updated']);
$response = $this->delete('/api/users/1');
$this->assertStatusCode(201, $response);
$this->assertJson($response->getContent());
$this->assertEquals(['id' => 1, 'name' => 'John'], $response->toArray());
Scenario Testing
public function testUserCreationWorkflow()
{
// Create user.
$createResponse = $this->post('/api/users', ['name' => 'Alice']);
$this->assertStatusCode(201, $createResponse);
// Fetch user.
$userId = $createResponse->toArray()['id'];
$fetchResponse = $this->get("/api/users/{$userId}");
$this->assertStatusCode(200, $fetchResponse);
}
Integration with Fixtures
doctrine/doctrine-fixtures-bundle to preload test data:
# config/packages/test/doctrine.yaml
imports:
- { resource: ../fixtures/test.yaml }
Example fixture:
// src/DataFixtures/UserFixtures.php
namespace App\DataFixtures;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Persistence\ObjectManager;
use App\Entity\User;
class UserFixtures extends Fixture
{
public function load(ObjectManager $manager)
{
$user = new User();
$user->setName('Test User');
$manager->persist($user);
$manager->flush();
}
}
Mocking External Services
createClient() to mock HTTP clients (e.g., Guzzle):
protected function createClient(array $options = [], array $server = [])
{
$client = parent::createClient($options, $server);
$client->getContainer()->set('test.guzzle.client', $this->createMockClient());
return $client;
}
Database Reset Overhead
Fixture Loading Order
// src/DataFixtures/AppFixtures.php
namespace App\DataFixtures;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Persistence\ObjectManager;
class AppFixtures extends Fixture
{
public function load(ObjectManager $manager)
{
$this->addReference('role_admin', $this->createRole('Admin'));
}
private function createRole(string $name): Role
{
$role = new Role();
$role->setName($name);
$manager->persist($role);
return $role;
}
}
Then reference them in other fixtures:
$user->setRole($this->getReference('role_admin'));
Authentication Quirks
$response = $this->get('/api/protected', [], [
'HTTP_Authorization' => 'Bearer ' . $this->getToken(),
]);
Assertion Limitations
use PhilarmonyApiTesterBundle\Test\ApiTestCase;
class CustomApiTest extends ApiTestCase
{
protected function assertNestedJson(array $expected, $response)
{
$actual = $response->toArray();
$this->assertArraySubset($expected, $actual);
}
}
Environment Configuration
.env.test is loaded during tests. Add this to phpunit.xml:
<php>
<env name="APP_ENV" value="test"/>
<env name="APP_DEBUG" value="true"/>
</php>
Enable Debug Mode
Set APP_DEBUG=1 in .env.test to see detailed error logs.
Inspect Responses Dump raw responses for debugging:
$response = $this->get('/api/debug');
file_put_contents('debug_response.json', $response->getContent());
Database State
Use a tool like Laravel Debugbar (if Symfony-compatible) or doctrine:schema:validate to check schema integrity:
php bin/console doctrine:schema:validate
Slow Tests
Profile test execution with Xdebug or --stop-on-failure:
php bin/phpunit --stop-on-failure
Custom Assertions Extend the base test case to add reusable assertions:
class ExtendedApiTest extends ApiTestCase
{
protected function assertApiError($response, string $field, string $message)
{
$data = $response->toArray();
$this->assertArrayHasKey('errors', $data);
$this->assertArrayHasKey($field, $data['errors']);
$this->assertEquals($message, $data['errors'][$field][0]);
}
}
Hooks for Pre/Post-Test Actions
Override setUp() and tearDown():
protected function setUp(): void
{
parent::setUp();
$this->enableFeatureFlags(['new_ui']);
}
protected function tearDown(): void
{
$this->disableFeatureFlags(['new_ui']);
parent::tearDown();
}
Custom Fixture Loaders Implement a custom loader
How can I help you explore Laravel packages today?