ttt - TYPO3 Testing Terrarium. Declarative test sandboxing via PHP attributes: TYPO3_CONF_VARS, application context, environment and more - applied before the test, guaranteed to be restored afterwards.

Maintainers

Package info

github.com/konradmichalik/ttt

pkg:composer/konradmichalik/ttt

Transparency log

Statistics

Installs: 3 004

Dependents: 10

Suggesters: 0

Stars: 1

Open Issues: 0

0.4.0 2026-07-30 09:47 UTC

README

ttt

Coverage CGL Tests Supported PHP Versions

ttt (TYPO3 Testing Terrarium) is a PHPUnit testing toolbox for TYPO3 extension development. At its core is declarative test sandboxing: TYPO3_CONF_VARS, environment variables, application context & more, put in place via PHP attributes and guaranteed to be cleaned up afterwards, whether the test passes, fails or errors. A Request kit, an Assertion kit, a Contract kit and a Fixture kit round out the rest of everyday TYPO3 test writing.

See Documentation index for the complete list of attributes, kits and concepts.

Before:

final class HandlerTest extends TestCase
{
    protected function setUp(): void
    {
        $this->backup = $GLOBALS['TYPO3_CONF_VARS'] ?? [];
        $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['my_ext']['configuration'] = [];
        putenv('MY_EXT_FEATURE=1');
    }

    protected function tearDown(): void
    {
        $GLOBALS['TYPO3_CONF_VARS'] = $this->backup; // skipped on hard errors!
        putenv('MY_EXT_FEATURE');
    }

    public function testResolvesConfiguration(): void { /* ... */ }
}

After:

#[WithTypo3ConfVars(['EXTCONF' => ['my_ext' => ['configuration' => []]]])]
final class HandlerTest extends TestCase
{
    #[Test]
    #[WithEnvVar('MY_EXT_FEATURE', '1')]
    public function resolvesConfiguration(): void { /* just Arrange–Act–Assert */ }
}

See docs/why-not-backupglobals.md for how this differs from PHPUnit's built-in #[BackupGlobals].

🔥 Installation

Packagist Packagist Downloads

composer require --dev konradmichalik/ttt

⚡ Usage

Register the extension once in your phpunit.xml:

<extensions>
    <bootstrap class="KonradMichalik\Ttt\TttExtension"/>
</extensions>

That's it: all ttt attributes now work in every test. Attributes can be placed on classes and methods (class level is applied first, method level merges on top) and are repeatable.

Available attributes

Attribute Purpose
#[WithTypo3ConfVars] Deep-merges configuration into $GLOBALS['TYPO3_CONF_VARS'], full restore afterwards
#[WithTca] Deep-merges configuration into $GLOBALS['TCA'][$table], full restore afterwards
#[WithGlobal] Sets an arbitrary $GLOBALS entry, full restore afterwards (incl. previously unset keys)
#[WithEnvVar] Sets an environment variable (putenv(), $_ENV, $_SERVER), restores all three channels
#[WithEnvironment] Bootstraps Environment::initialize() in a temporary project directory incl. cleanup
#[InApplicationContext] Switches the TYPO3 application context for one test
#[WithSingleton] Registers a singleton via GeneralUtility, restores the previous singleton map
#[WithBackendUser] Provides a lightweight $GLOBALS['BE_USER'] stub and the matching Context backend.user aspect
#[WithFrontendUser] Provides a lightweight $GLOBALS['FE_USER'] stub and the matching Context frontend.user aspect
#[FreezeTime] Pins the Context date aspect and EXEC_TIME globals
#[InTimeZone] Sets the default timezone (date_default_timezone_set())
#[InLocale] Sets the locale (setlocale()) for a given category
#[WithStaticProperty] Generic escape hatch: overwrites any static property via reflection, full restore afterwards
#[WithInstance] Queues a fake via GeneralUtility::addInstance() for the next makeInstance() call

Kits

Kit Purpose
Request kit Fluent builder for TYPO3 ServerRequest objects
Assertion kit JSON path assertions with descriptive failure messages
Contract kit Generates violation-case tests from a validateConfiguration()-style contract
Fixture kit Disposable test fixtures (images, log files)

See docs/lifecycle.md for how attribute application and restoration are wired into PHPUnit's event lifecycle, docs/why-extension.md for why that's an extension instead of tearDown(), and docs/without-extension.md for the imperative alternative.

🧩 Extending

Custom attributes are two small classes: a DTO implementing TttAttribute and an AttributeHandler (a public API with a backward-compatibility promise) that applies the state and returns a restorer closure. Handlers must be stateless: all captured state belongs into the closure.

Register custom handlers via a comma-separated handlers parameter on the bootstrap extension; there's no need to replace TttExtension:

<extensions>
    <bootstrap class="KonradMichalik\Ttt\TttExtension">
        <parameter name="handlers" value="Vendor\Ext\Tests\Sandbox\MyHandler,Vendor\Ext\Tests\Sandbox\OtherHandler" />
    </bootstrap>
</extensions>

Custom handlers run after the built-in ones. A missing class or one that doesn't implement AttributeHandler fails fast with an actionable error naming the handlers parameter.

See docs/non-goals.md for what's deliberately out of scope, and why.

🧑‍💻 Contributing

Please have a look at CONTRIBUTING.md.

⭐ License

This project is licensed under GNU General Public License 3.0 (or later).