liip/functional-test-bundle
Symfony bundle providing base classes and helpers for functional tests, plus a DI-aware mock builder for unit tests. Includes tools for command testing, logged clients, query counting, examples, and parallel test runs.
Installation:
composer require --dev liip/functional-test-bundle
Add the bundle to config/bundles.php under test environment:
Liip\FunctionalTestBundle\LiipFunctionalTestBundle::class => ['test' => true],
Extend Base Class:
Replace Symfony\Bundle\FrameworkBundle\Test\WebTestCase with:
use Liip\FunctionalTestBundle\Test\WebTestCase;
class MyTest extends WebTestCase
First Test Case:
public function testHomepage()
{
$client = $this->makeClient();
$client->request('GET', '/');
$this->assertStatusCode(200, $client);
}
makeClient(['username' => 'test', 'password' => 'test']) or configure in config/packages/test/liip_functional_test.yaml.getServiceMockBuilder().QueryCounter (see Query Counter docs).// Option 1: Config-based (config/packages/test/liip_functional_test.yaml)
liip_functional_test:
authentication:
username: test@example.com
password: secret
// Option 2: Inline credentials
$client = $this->makeClient(['username' => 'test', 'password' => 'secret']);
// Option 3: Fixture-based login
$client = $this->makeClient();
$this->loginClient($client, $this->getReference('user'), 'main');
public function testMockedService()
{
$mock = $this->getServiceMockBuilder(MyService::class)
->disableOriginalConstructor()
->getMock();
$mock->method('doWork')->willReturn('mocked');
$this->setServiceMock($this->getContainer(), MyService::class, $mock);
$controller = new MyController($mock);
$response = $controller->indexAction();
$this->assertEquals('mocked', $response);
}
public function testCommand()
{
$client = $this->makeClient();
$output = $this->runCommand($client, 'app:command', ['--option' => 'value']);
$this->assertStringContainsString('Expected output', $output);
}
public function testQueryCount()
{
$client = $this->makeClient();
$counter = new QueryCounter($client->getContainer()->get('doctrine')->getConnection());
$client->request('GET', '/');
$this->assertEquals(3, $counter->getCount());
}
static::ensureKernelShutdown() before mocking services if they’re already loaded.config/packages/test/liip_functional_test.yaml:
liip_functional_test:
parallel:
enabled: true
workers: 4
config/packages/test/security.yaml:
security:
firewalls:
main:
http_basic: ~
Session Storage Errors:
Missing session.storage.options#name.config/packages/test/framework.yaml:
framework:
session:
name: MOCKSESSID
Service Mocking Quirks:
static::ensureKernelShutdown()) or set reuseKernel: true in runCommand().Fixture References:
getReference() fails if fixtures aren’t loaded.$fixtures = $this->loadFixtures([UserFixture::class])->getReferenceRepository();
Parallel Tests:
static::bootKernel() and static::ensureKernelShutdown() explicitly.$counter = new QueryCounter($connection, true); // Enable logging
$this->dumpClient($client); // Shows headers, content, and status
$this->assertServiceMockCalled(MyService::class, 'doWork');
Custom Assertions:
Extend WebTestCase to add domain-specific assertions:
class CustomTestCase extends WebTestCase
{
protected function assertFlashMessageContains($message)
{
$this->assertStringContainsString($message, $this->getSession()->getFlashBag()->get('notice'));
}
}
Query Counter Extensions:
Subclass QueryCounter to track specific queries:
class CustomQueryCounter extends QueryCounter
{
public function trackSelectsOnly()
{
$this->trackQueries('SELECT');
}
}
Authentication Providers:
Extend AuthenticationProvider for custom auth logic:
class ApiTokenAuthProvider implements AuthenticationProviderInterface
{
public function authenticate(array $credentials)
{
// Custom logic
}
}
config/packages/test/ overrides are not loaded in production.
Use when@test in services.yaml:
services:
_defaults:
autowire: true
autoconfigure: true
public: true # Critical for mocking!
How can I help you explore Laravel packages today?