Installation:
composer require --dev contao/test-case
Add to your composer.json under require-dev if not using globally.
Basic Test Class:
use Contao\TestCase\ContaoTestCase;
class MyFirstTest extends ContaoTestCase {}
First Use Case: Test a Contao model interaction:
public function testPageModel()
{
$page = $this->createClassWithPropertiesStub(Contao\PageModel::class, ['id' => 1, 'title' => 'Test Page']);
$this->assertEquals('Test Page', $page->title);
}
getContainerWithContaoConfiguration() for dependency injection testing.createContaoFrameworkStub() for core Contao interactions.createAdapterStub() for testing Contao models (e.g., FilesModel).Workflow:
// Stub a PageModel with predefined properties
$page = $this->createClassWithPropertiesStub(Contao\PageModel::class, [
'id' => 42,
'title' => 'About Us',
'published' => true
]);
// Assert behavior
$this->assertTrue($page->published);
$this->assertEquals('About Us', $page->title);
Integration Tip:
Combine with createAdapterStub() for database interactions:
$adapter = $this->createAdapterStub(['findById' => $page]);
$framework = $this->createContaoFrameworkStub([Contao\PageModel::class => $adapter]);
Pattern:
public function testContainerConfiguration()
{
$container = $this->getContainerWithContaoConfiguration();
$this->assertEquals(
'files',
$container->getParameter('contao.upload_path')
);
}
Workflow for Custom Projects:
$container = $this->getContainerWithContaoConfiguration(__DIR__.'/../../');
$this->assertEquals(
__DIR__.'/../../var/cache',
$container->getParameter('kernel.cache_dir')
);
Use Case: Test backend/front-end user logic:
public function testBackendUserToken()
{
$tokenStorage = $this->createTokenStorageStub(Contao\BackendUser::class);
$user = $tokenStorage->getToken()->getUser();
$this->assertInstanceOf(Contao\BackendUser::class, $user);
}
Integration:
Use with createClassWithPropertiesStub for predefined user data:
$user = $this->createClassWithPropertiesStub(Contao\BackendUser::class, [
'username' => 'admin',
'id' => 1
]);
$tokenStorage = $this->createTokenStorageStub($user);
Pattern:
public function testFileUpload()
{
$tempDir = $this->getTempDir();
$fs = new \Symfony\Component\Filesystem\Filesystem();
$fs->mkdir($tempDir.'/uploads');
// Test file operations here...
$this->assertFileExists($tempDir.'/uploads');
}
Critical Step:
Always call parent::tearDownAfterClass() to auto-cleanup:
public static function tearDownAfterClass(): void
{
parent::tearDownAfterClass(); // Auto-deletes temp dir
}
Temp Directory Cleanup:
parent::tearDownAfterClass() causes lingering test directories.tearDownAfterClass() in your test class.Adapter Overrides:
Config adapter in createContaoFrameworkStub() may conflict with custom configs.$framework = $this->createContaoFrameworkStub([
Contao\Config::class => $customConfigAdapter
]);
Magic Properties:
createClassWithPropertiesStub() fails if the class uses __get()/__set() with non-standard logic.createAdapterStub() for complex property handling.Container Dumping:
$container = $this->getContainerWithContaoConfiguration();
dump($container->getParameterBag()->all()); // Inspect all params
Framework Mock Verification:
$framework = $this->createContaoFrameworkMock();
$framework->expects($this->once())->method('initialize');
// If test fails, check if `initialize()` was called.
Adapter Stub Validation:
->shouldReceive() for explicit method checks:
$adapter = $this->createAdapterStub(['findById']);
$adapter->shouldReceive('findById')->once()->andReturn($model);
Custom Adapters:
Extend ContaoTestCase to add project-specific adapters:
class CustomTestCase extends ContaoTestCase
{
protected function createCustomAdapterStub(array $methods)
{
return $this->createAdapterStub($methods);
}
}
Token Storage Extensions:
Override createTokenStorageStub() for custom token logic:
protected function createTokenStorageStub($user = null)
{
$token = $this->createMock(Contao\CoreBundle\Security\Token\ContaoUserToken::class);
$token->method('getUser')->willReturn($user);
$storage = $this->createMock(Contao\CoreBundle\Security\TokenStorage::class);
$storage->method('getToken')->willReturn($token);
return $storage;
}
Temp Directory Customization:
Override getTempDir() for multi-project testing:
protected function getTempDir(): string
{
return sys_get_temp_dir().'/myproject_'.static::class;
}
Combine with Laravel:
Use getContainerWithContaoConfiguration() to test Contao integrations in Laravel:
$container = $this->getContainerWithContaoConfiguration();
$this->assertTrue($container->has('contao.routing.router'));
Performance: Reuse stubs/adapters across tests to avoid recreation overhead:
private $pageStub;
protected function setUp(): void
{
$this->pageStub = $this->createClassWithPropertiesStub(Contao\PageModel::class, ['id' => 1]);
}
How can I help you explore Laravel packages today?