PHPackages                             rwslinkman/metronome - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Testing &amp; Quality](/categories/testing)
4. /
5. rwslinkman/metronome

Abandoned → [phpunit/phpunit](/?search=phpunit%2Fphpunit)ArchivedLibrary[Testing &amp; Quality](/categories/testing)

rwslinkman/metronome
====================

Test utility for Symfony 3 (PHP) applications

3.1.3(3y ago)11.1k[1 issues](https://github.com/rwslinkman/metronome/issues)Apache-2.0PHPPHP &gt;=8.1

Since Sep 14Pushed 3y ago1 watchersCompare

[ Source](https://github.com/rwslinkman/metronome)[ Packagist](https://packagist.org/packages/rwslinkman/metronome)[ RSS](/packages/rwslinkman-metronome/feed)WikiDiscussions master Synced 2w ago

READMEChangelogDependencies (15)Versions (19)Used By (0)

Metronome [![](https://camo.githubusercontent.com/5a5682f451ca48cb1fdf0e32b096c9e2eb540cf6c1991023936395616e532020/68747470733a2f2f7472617669732d63692e636f6d2f7277736c696e6b6d616e2f6d6574726f6e6f6d652e7376673f6272616e63683d6d6173746572)](https://camo.githubusercontent.com/5a5682f451ca48cb1fdf0e32b096c9e2eb540cf6c1991023936395616e532020/68747470733a2f2f7472617669732d63692e636f6d2f7277736c696e6b6d616e2f6d6574726f6e6f6d652e7376673f6272616e63683d6d6173746572)
===========================================================================================================================================================================================================================================================================================================================================================================================================================================================================

[](#metronome-)

**This project is deprecated and should no longer be used.**
**Please read the DEPRECATION NOTICE for further information.**

> Metronome is a lightweight test utility for Symfony 3 and Symfony 4 (PHP) applications.
> It provides a steady base for easy mocking and injection of the Symfony Container.
> Let Metronome help you orchestrate the Symfony in your hands!

Metronome aims to make functional testing easier for Symfony projects.
It creates a custom `Kernel` and `Container` and injects everything you need for a fully set-up Symfony environment.
You application is automatically loaded using Symfony's `WebTestCase` and `KernelBrowser` client.

Metronome provides several builders to aid in your tests:

- `MetronomeBuilder`
- `MetronomeDoctrineMockBuilder`
- `MetronomeFileSystemBuilder`

Using the Metronome you can:

- Build a `MetronomeEnvironment` that sends GET and POST requests to test your `Controller` classes
- Build a mocked `EntityManager` to test classes that have database interactions
- Build a mocked `ReferenceRepository` to test your `Fixture` classes
- Build a mocked `ManagerRegistry` to test your `EntityRepository` classes
- Inject `MetronomeLoginData` to bypass your `GuardAuthenticator` protection
- Mock `Symfony Forms` using the `MetronomeFormDataBuilder` and `MetronomeEntityFormDataBuilder`
- Verify the contents of the Symfony `FlashBag`

DEPRECATION NOTICE
------------------

[](#deprecation-notice)

**This project is no longer in active development.**
**Initially, the project was created in a time when Symfony did not fully support dependency injection for testing.**
**Also, it was difficult to create mocks that could be used in the Symfony kernel.**
**Testing your Symfony Controllers was pretty difficult at the time. This is no longer the case.**

**Over the years, both Symfony and PHPUnit have seen great improvements in these parts.**
**Metronome is therefor becoming more and more obsolete.**
**It becomes more of a burden than a benefit for projects.**
**Using the improved dependency injection system, testing Symfony Controllers can be done well without Metronome.**

**In case you want to migrate away from Metronome, take a look at mocking in PHPUnit.**
**These mocks can easily be passed in Controllers' constructors when testing.**

Installation
------------

[](#installation)

You can install Metronome using `composer` to get the package from Packagist.

```
composer require-dev rwslinkman/metronome

```

Metronome is not needed in the production environment, since it is a test utility.
The latest version is `3.0.0`.
Older versions may work for Symfony 3 projects, but this may prove difficult and is not supported.
For bleeding edge development, point to the `dev-develop` or `dev-master` version.

Usage
-----

[](#usage)

Metronome is used in combination with PHPUnit and the Symfony `WebTestCase`, which extends PHPUnit's `TestCase`.

### MetronomeBuilder

[](#metronomebuilder)

With the `MetronomeBuilder`, you can create a fully set-up environment of your application.
The connection to the database, `EntityManager`, is automatically mocked.
You can inject your own Kernel, or use the ready-made MetronomeKernel.
After setting this up, you inject services, repositories and other objects you need. Calling the `build` function returns a `MetronomeEnvironment` that allows to fire `GET`, `POST`, and `PUT` to your application.

Creating an environment for your application with no mocks (Doctrine excluded):

```
$clientBuilder = new MetronomeTestClientBuilder();
$builder = new MetronomeBuilder($clientBuilder->build());
$environment = $builder->build();

$response = $environment->get("/");
$this->assertEquals(200, $response->getStatusCode());
```

### Testing a Controller

[](#testing-a-controller)

Setup a `MetronomeEnvironment` using the `MetronomeBuilder`.
The most basic test returns a `Response` from `Symfony\HttpFoundation`.
Make sure your test extents `WebTestCase` to make use of Symfony's `Client` for HTTP requests.

```
class IndexControllerTest extends WebTestCase
{
    /** @var MetronomeBuilder */
    private $testEnvBuilder;

    public function setUp() {
        $clientBuilder = new MetronomeTestClientBuilder();
        $clientBulder->controller(IndexController::class);
        $this->testEnvBuilder = new MetronomeBuilder($clientBuilder->build());
        $this->testEnvBuilder->setupController(IndexController::class);
    }

    public function test_givenApp_whenGetIndex_thenShouldReturnOK() {
        /** @var MetronomeEnvironment */
        $environment = $builder->build();

        /** @var Response */
        $response = $environment->get("/");

        $this->assertEquals(200, $response->getStatusCode());
    }
}
```

If you have custom services configured in Symfony's `services.yaml`, then you can mock those during tests.
This is convenient when testing `Controllers`. You can make use of the `MetronomeDynamicMockBuilder` that easily mocks classes.

```
class IndexControllerTest extends WebTestCase
{
    /** @var MetronomeBuilder */
    private $testEnvBuilder;

    public function setUp() {
        $clientBuilder = new MetronomeTestClientBuilder();
        $clientBulder->controller(IndexController::class);
        $this->testEnvBuilder = new MetronomeBuilder($clientBuilder->build());
        $this->testEnvBuilder->setupController(IndexController::class);
    }

    public function test_givenApp_whenGetIndex_andEmptyList_thenShouldReturnOK() {
        $mockBuilder = new MetronomeDynamicMockBuilder(UserService::class);
        $mockBuilder->method("getUsers", array());
        $this->testEnvBuilder->injectObject("myapp.user_service", $mockBuilder->build());

        /** @var MetronomeEnvironment */
        $testEnv = $this->testEnvBuilder->build();

        /** @var Response */
        $result = $testEnv->get("/");

        $this->assertEquals(200, $result->getStatusCode());
    }
}
```

For more static components that you inject as services, you can use `ServiceInjector` to return basic values.
Optionally, add some setters to set your desired outcome for the mocked function.
`MetronomeBuilder` has a special `injectService()` function to accept these type of injections.

```
class ProductServiceInjector implements ServiceInjector {
    private $loadAllProducts;

    /**
     * @return array Key => Value array of methods to mock and their respective results
     */
    public function inject()
    {
        return array(
            "loadAllProducts" => $this->loadAddProducts,
        );
    }

    /**
     * @return string The service name as defined in config.yml
     */
    public function serviceName()
    {
        return "rwslinkman.products";
    }

    /**
     * @return string Full namespace for the service to mock
     */
    public function serviceClass()
    {
        return '\rwslinkman\Service\ProductService';
    }
}
```

### Arguments and Definitions

[](#arguments-and-definitions)

When testing `Controllers`, you will need to deal with constructor arguments that are injected by Symfony.
Metronome provides the `MetronomeArgument` that injects a mock directory into the Controller contructor when Symfony needs it.
Provide the parameter name and the service you want to inject and call `setupController` on your `MetronomeBuilder`. Metronome provides a small catalog with premade Arguments.

```
$clientBuilder = new MetronomeTestClientBuilder();
$clientBuilder->controller(ProjectController::class);
$this->builder = new MetronomeBuilder($clientBuilder->build());
$this->builder->setupController(ProjectController::class, array(
    new MetronomeServiceArgument("projectsService", "my_app.projects_service"),
    new MetronomeFormFactoryArgument("formFactory"),
    new MetronomeSessionArgument("session")
));
```

Note: the services must be injected in the Container when building a `MetronomeEnvironment`.

Symfony might want to load system services earlier than calling the Controller.
In that case, add a `MetronomeDefinition` to the test client:

```
$clientBuilder = new MetronomeTestClientBuilder();
$clientBuilder->controller(ProjectController::class);
$clientBuilder->addDefinition(new MetronomeDefinition(TokenStorage::class, TokenStorageInterface::class));
$clientBuilder->addDefinition(new MetronomeDefinition(AuthenticationUtils::class));

```

### Mocking the database and EntityManager

[](#mocking-the-database-and-entitymanager)

Classes that use the `Doctrine EntityManager` can be tested using Metronome.
These are usually the services in your Symfony application.
You can inject `RepoInjector` classes to mock your `EntityRepository`.

```
$metronomeBuilder = new MetronomeDoctrineMockBuilder();
$metronomeBuilder->injectRepo(new ProductRepoInjector());
$entityManagerMock = $metronomeBuilder->buildEntityManager(ProductRepository::class);

$service = new ProductService($this->entityManager);
```

The `RepoInjector` is very similar to the `ServiceInjector`.
It wraps around a single Doctrine `Entity`.

```
class ProductRepoInjector implements RepoInjector {
    private $findAll;

    /**
     * @return array Key => Value array of methods to mock and their respective results
     */
    public function inject()
    {
        return array(
            "findAll" => $this->findAll,
        );
    }

    /**
     * @return mixed Acts as an identifier for the repository
     */
    public function repositoryName()
    {
        return ProductRepository::class;
    }

    /**
     * @return string Full namespace for the repository to mock
     */
    public function repositoryClass()
    {
        return '\rwslinkman\Repository\ProductRepository';
    }
}
```

### Testing Fixtures

[](#testing-fixtures)

Fixtures are part of the `Doctrine FixtureBundle`.
Metronome can be used to verify some of the `Fixture` behaviour.

```
use PHPUnit\Framework\TestCase;

class MyFixtureTest extends TestCase
{
    public function test_givenFixture_whenLoad_shouldAlwaysPersist() {
        $envBuilder = new MetronomeDoctrineMockBuilder();
        $mockEm = $envBuilder->buildEntityManager();

        $fixture = new MyFixture();
        $fixture->load($mockEm);

        $mockEm->shouldHaveReceived("flush");
    }
}
```

### Bypassing the Guard authentication system

[](#bypassing-the-guard-authentication-system)

When testing Controllers, you can bypass your `GuardAuthenticator` by injecting `MetronomeLoginData` into the environment builder.
According to the Symfony documentation, the authenticator is defined in your `services.yaml`.
This service identifier is used when creating `MetronomeLoginData`. Use this to bypass the firewall configured in `security.yaml` during tests.

Using the `requiresLogin` function is not mandatory. If you want to test your firewall, omit the call.

```
class AdminControllerTest extends WebTestCase
{
    /** @var MetronomeBuilder */
    private $testEnvBuilder;
    /** @var */ MetronomeLoginData */
    private $loginData;

    public function setUp() {
        $clientBuilder = new MetronomeTestClientBuilder();
        $clientBulder->controller(AdminController::class);
        $this->testEnvBuilder = new MetronomeBuilder($clientBuilder->build());
        $this->testEnvBuilder->setupController(AdminController::class);

        $myUser = new MyUser(); // implements UserInterface
        $this->loginData = new MetronomeLoginData($myUser, "rwslinkman.my_authenticator");
    }

    public function test_givenApp_whenGetIndex_andEmptyProductList_thenShouldReturnOK() {
        // Add this line to actually use the login data.
        $this->testEnvBulder->requiresLogin($loginData);

        /** @var MetronomeEnvironment */
        $testEnv = $this->testEnvBuilder->build();

        /** @var Response */
        $result = $testEnv->get("/admin"); // A route that is protected by the firewall in security.yaml

        $this->assertEquals(200, $result->getStatusCode());
    }
}
```

### Testing forms in your Controllers

[](#testing-forms-in-your-controllers)

Most Symfony websites have forms in their Controllers, which can easily be tested with Metronome.
Metronome provides 2 builders, that build a `MetronomeFormData` object.
This data can be injected into the `MetronomeBuilder` to mock a form.

If you provide an instance of the object being modified to the Symfony FormBuilder, it is directly updated when submitting a valid form.
You can mock this updated object by using the `MetronomeEntityFormDataBuilder`.

```
$entity = new MyEntity();
$entityFormBuilder = new MetronomeEntityFormDataBuilder();
$entityFormBuilder
    ->formData($doctrineEntity)
    ->isValid(true);
$formData = $$entityFormBuilder->build();
```

If you have simpler forms, where you directly use the input data, you can make use of the `MetronomeFormDataBuilder`.
It allows to directly inject values into the form fields.

```
// Simple forms
$formBuilder = new MetronomeFormDataBuilder();
$formBuilder
    ->isValid(true)
    ->formData("form_field_address", "some address")
    ->formData("form_field_zipcode", "123456");
$formData = $formBuilder->build();
```

Using the built form data is done easily by injecting it into the `MetronomeBuilder`.

```
$clientBuilder = new MetronomeTestClientBuilder();
$testEnvBuilder = new MetronomeBuilder($clientBuilder->build());
$envBuilder->injectForm($formData);

$testEnv = $this->envBuilder->build();
$testEnv->post("/register");
```

You can use `injectForm` multiple times.
The `FormFactory` mock will return the forms in the order they were injected.
There are cases where you want to write a test specified to the second form. To skip the first form, you can inject `MetronomeNonSubmittedForm` or `MetronomeInvalidForm` before your actual `MetronomeFormData`.

Please note that this example uses a CSS selector, which requires the `symfony/css-selector` dependency.

### Verifying FlashBag data

[](#verifying-flashbag-data)

The `FlashBag` in the user's session can be a convenient tool for your website.
Metronome allows you to access the `FlashBag` through the `MetronomeEnvironment`.
This can help you verify the outcome of your GET or POST request even better.

In the example below, assume that the page has no log messages to show and reports this in a flash message.

```
$clientBuilder = new MetronomeTestClientBuilder();
$testEnvBuilder = new MetronomeBuilder($clientBuilder->build());
$testEnv = $testEnvBuilder->build();

$testEnv->get("/admin/logs");

$flash = $testEnv->getFlashBag();
$this->assertNotEmpty($flash);
```

The `getFlashBag` function returns an associative array, with an entry for every key.
Each key entry is also an array, containing the flash messsages associated to that key.

###  Health Score

30

—

LowBetter than 62% of packages

Maintenance0

Infrequent updates — may be unmaintained

Popularity16

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity80

Battle-tested with a long release history

 Bus Factor1

Top contributor holds 97.7% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~109 days

Recently: every ~2 days

Total

17

Last Release

1457d ago

Major Versions

1.5.0 → 2.0.02019-10-07

2.0.4 → 3.0.02022-06-26

PHP version history (3 changes)1.0PHP &gt;=5.3.0

2.0.2PHP &gt;=7.3

3.0.0PHP &gt;=8.1

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/3406313?v=4)[Rick Slinkman](/maintainers/rwslinkman)[@rwslinkman](https://github.com/rwslinkman)

---

Top Contributors

[![rwslinkman](https://avatars.githubusercontent.com/u/3406313?v=4)](https://github.com/rwslinkman "rwslinkman (125 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (3 commits)")

---

Tags

cidoctrinephpphpunitsymfonytest-automationtestingtesting-tools

###  Code Quality

TestsPHPUnit

Code StylePHP\_CodeSniffer

### Embed Badge

![Health badge](/badges/rwslinkman-metronome/health.svg)

```
[![Health](https://phpackages.com/badges/rwslinkman-metronome/health.svg)](https://phpackages.com/packages/rwslinkman-metronome)
```

###  Alternatives

[rcsofttech/audit-trail-bundle

Enterprise-grade, high-performance Symfony audit trail bundle. Automatically track Doctrine entity changes with split-phase architecture, multiple transports (HTTP, Queue, Doctrine), and sensitive data masking.

1175.2k](/packages/rcsofttech-audit-trail-bundle)[easycorp/easyadmin-bundle

Admin generator for Symfony applications

4.3k17.5M378](/packages/easycorp-easyadmin-bundle)[sulu/sulu

Core framework that implements the functionality of the Sulu content management system

1.3k1.4M196](/packages/sulu-sulu)[prestashop/prestashop

PrestaShop is an Open Source e-commerce platform, committed to providing the best shopping cart experience for both merchants and customers.

9.1k16.8k](/packages/prestashop-prestashop)[2lenet/crudit-bundle

The easy like Crud'it Bundle.

1615.6k12](/packages/2lenet-crudit-bundle)[pimcore/pimcore

Content &amp; Product Management Framework (CMS/PIM/E-Commerce)

3.8k3.8M464](/packages/pimcore-pimcore)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
