Running the Tests 

The extension ships two test suites, compatible with TYPO3 13.4 and 14:

Suite

Location

What it covers

Unit

Tests/Unit/

Manager, channels, traits, models, queue handler. No TYPO3 instance.

Functional

Tests/Functional/

A real TYPO3 instance: DI wiring, email delivery, database storage, queue handler, repositories, custom channels registered by another extension, and every public way of sending a notification.

The functional tests load a fixture extension (Tests/Functional/Fixtures/Extensions/notifications_test). It plays the role of a third-party integrator: it registers custom channels and captures sent emails in memory, so no mail is ever sent. By default they use SQLite, so no database server is needed.

Without DDEV 

Requires PHP 8.2+ (with pdo_sqlite) and Composer, run from the extension directory:

composer install

# Unit tests
.Build/bin/phpunit -c Build/phpunit-unit.xml
# or: composer test:php:unit

# Functional tests
.Build/bin/phpunit -c Build/phpunit-functional.xml
# or: composer test:php:functional
Copied!

With DDEV 

The extension directory contains a DDEV configuration:

ddev start
ddev composer install

# Unit tests
ddev exec .Build/bin/phpunit -c Build/phpunit-unit.xml

# Functional tests on SQLite
ddev exec .Build/bin/phpunit -c Build/phpunit-functional.xml

# Functional tests on the DDEV MySQL server
ddev exec typo3DatabaseDriver=mysqli typo3DatabaseHost=db \
    typo3DatabaseUsername=root typo3DatabasePassword=root typo3DatabaseName=func_test \
    .Build/bin/phpunit -c Build/phpunit-functional.xml
Copied!

The functional tests create one database per test class (prefixed with typo3DatabaseName), so the database user needs CREATE DATABASE privileges. That's why the example uses root.

Useful Options 

# Run a single test class or method
.Build/bin/phpunit -c Build/phpunit-functional.xml --filter PublicApiTest
.Build/bin/phpunit -c Build/phpunit-unit.xml --filter sendNowBypassesTheMessageBus

# Show deprecations (useful when preparing the next TYPO3 major)
.Build/bin/phpunit -c Build/phpunit-functional.xml --display-deprecations

# Test against a specific TYPO3 version
composer update -W --with "typo3/cms-core:^13.4"
composer update -W --with "typo3/cms-core:^14.3"
Copied!

Any other database can be used through the typo3DatabaseDriver, typo3DatabaseHost, typo3DatabasePort, typo3DatabaseUsername, typo3DatabasePassword and typo3DatabaseName environment variables.

Testing Your Own Notifications 

The fixture extension is also a working example of how to test code that sends notifications:

  • Classes/Mail/CapturingTransport.php — a mail transport that keeps sent emails in memory. Enable it in a functional test with $GLOBALS['TYPO3_CONF_VARS']['MAIL']['transport'] = CapturingTransport::class (or $configurationToUseInTestInstance), then assert on CapturingTransport::$messages.
  • Classes/Channel/AttributeTaggedChannel.php — a custom channel that records what it receives, registered with #[AutoconfigureTag].
  • Configuration/Services.yaml — registers YamlTaggedChannel with an explicit notifications.channel tag.

In unit tests, mock NotificationDispatcherInterface and register it with GeneralUtility::addInstance() to assert that notify() / notifyNow() are called, without delivering anything:

$dispatcher = $this->createMock(NotificationDispatcherInterface::class);
$dispatcher->expects(self::once())->method('send')->with($user, $notification);
GeneralUtility::addInstance(NotificationDispatcherInterface::class, $dispatcher);

$user->notify($notification);
Copied!