Metrics::fake() swaps the manager — behind the facade and in the container, so injected managers get it too — for a recording RoundlyConsulting\Metrics\Testing\MetricsFake, and returns it. It keeps every metric you had registered, answers the keys you pass with canned results, and records what was resolved and invalidated — mirroring Laravel’s Http::fake(). The fake ships with the package and depends only on PHPUnit’s Assert:
use RoundlyConsulting\Metrics\Facades\Metrics;
$fake = Metrics::fake([
'active-users' => 1200, // a number becomes a value envelope
'revenue' => ['value' => 5000.0], // an array is used as the raw result
]);
// ...exercise the code under test...
Metrics::assertResolved('active-users');
$fake->assertResolvedTimes('active-users', 1);
$fake->assertNotResolved('revenue');
$fake->assertNothingResolved();
// forget() and flushCache() are recorded, and the cache is left alone
$fake->assertForgotten('revenue'); // by key, or by class for an unregistered metric
$fake->assertNotForgotten('active-users');
$fake->assertNothingForgotten();
$fake->assertCacheFlushed();
$fake->assertCacheNotFlushed();Canned values
- An int or float — wrapped in a value result.
- An array — used as the raw result.
- A RoundlyConsulting\Metrics\Types\Result — used as-is.
- A full Metric instance — used as a copy with caching switched off.
Canned results are never cached, so one test’s canned value can’t leak into the next. The fake keeps every metric registered before it — in a service provider, say — so keys without a canned value fall through to the real registered metric; keys(), has() and all() include the canned keys:
use RoundlyConsulting\Metrics\Facades\Metrics;
// AppServiceProvider::boot() registered: Metrics::register('users', RegisteredUsers::class);
$fake = Metrics::fake(['revenue' => 5000]);
Metrics::get('users'); // the real registered metric — kept by the fake, and recorded
Metrics::get('revenue'); // the canned result
$fake->assertResolved('users');
$fake->assertResolved('revenue');Assertions
Available on the returned fake and, proxied, on the Metrics facade:
| Assertion | Passes when |
|---|---|
assertResolved(string $key, ?int $times = null) | The key was resolved at least once (or exactly $times). |
assertResolvedTimes(string $key, int $times) | The key was resolved exactly $times. |
assertNotResolved(string $key) | The key was never resolved. |
assertNothingResolved() | No metric was resolved at all. |
assertForgotten(string $key) | forget() was called for the key — or, for an unregistered metric instance, its class. |
assertNotForgotten(string $key) | forget() was never called for it. |
assertNothingForgotten() | forget() was never called. |
assertCacheFlushed() | flushCache() was called. |
assertCacheNotFlushed() | flushCache() was never called. |
forget() and flushCache() are recorded and never touch the cache; forget() still throws UnknownMetricException for a key that is neither canned nor registered.
A feature test
The fake pairs well with the controller from the Registry section — every lookup through get(), including the ones a dashboard makes, is recorded:
use RoundlyConsulting\Metrics\Facades\Metrics;
it('serves a metric tile', function () {
Metrics::fake(['active-users' => 1200]);
// GET /metrics/{key} — the show() controller from the Registry section
$this->getJson('/metrics/active-users')
->assertOk()
->assertJsonPath('result.value', 1200.0);
Metrics::assertResolvedTimes('active-users', 1);
});Testing the numbers
To test the calculation itself, skip the fake: seed rows with your factories and assert on toArray() — metrics run against your test database like any other query:
use App\Metrics\RegisteredUsers;
use App\Models\User;
use RoundlyConsulting\Metrics\Enums\Period;
it('counts sign-ups today', function () {
User::factory()->count(3)->create();
$metric = RegisteredUsers::make()->range(Period::Today)->toArray();
expect($metric['result']['value'])->toBe(3.0);
});Show your open-source love
This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.
More ways to support, including cryptoBy donating, you agree to our donation terms.
Want this built into your product?
We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.