›
byrcsc/laravel-customer-health · 1.x
Test event tracking, milestones, queues, onboarding, and scores with Laravel's standard fakes.
Use Laravel database assertions and event or queue fakes to test customer health behavior without replacing the package manager.
Your test database needs the four package migrations and a persisted
Trackable subject.
use App\CustomerHealth\Events\WorkflowCreated;
use ByRcsc\LaravelCustomerHealth\Events\MilestoneReached;
use ByRcsc\LaravelCustomerHealth\Events\ProductEventRecorded;
use ByRcsc\LaravelCustomerHealth\Facades\CustomerHealth;
use ByRcsc\LaravelCustomerHealth\Models\Milestone;
use ByRcsc\LaravelCustomerHealth\Models\ProductEventRecord;
use Illuminate\Support\Facades\Event;
it('records first workflow adoption', function () {
Event::fake([
ProductEventRecorded::class,
MilestoneReached::class,
]);
CustomerHealth::track(new WorkflowCreated(
subject: $this->team,
actor: $this->user,
properties: ['template' => 'approval'],
));
$event = ProductEventRecord::query()->sole();
$milestone = Milestone::query()->sole();
expect($event->properties)->toBe(['template' => 'approval'])
->and($event->subject->is($this->team))->toBeTrue()
->and($event->actor?->is($this->user))->toBeTrue()
->and($milestone->name)->toBe('workflow_created');
Event::assertDispatched(ProductEventRecorded::class);
Event::assertDispatched(MilestoneReached::class);
});Register the test event class in customer-health.events before the manager or
event registry resolves.
CustomerHealth::track(new WorkflowCreated($this->team));
CustomerHealth::track(new WorkflowCreated($this->team));
expect(ProductEventRecord::query()->count())->toBe(2)
->and(Milestone::query()->count())->toBe(1);This verifies the intended difference between repeatable raw events and the first milestone.
use ByRcsc\LaravelCustomerHealth\Jobs\RecordProductEvent;
use Illuminate\Support\Facades\Queue;
config()->set('customer-health.queue', true);
config()->set('customer-health.queue_connection', 'redis');
config()->set('customer-health.queue_name', 'customer-health');
Queue::fake();
CustomerHealth::track(new WorkflowCreated($this->team));
Queue::assertPushed(
RecordProductEvent::class,
fn (RecordProductEvent $job): bool =>
$job->connection === 'redis'
&& $job->queue === 'customer-health',
);No event or milestone row is written while the queue is faked. Test the job or worker integration separately when the stored result matters.
CustomerHealth::track(new WorkflowCreated($this->team));
$progress = CustomerHealth::onboarding(
$this->team,
App\CustomerHealth\Onboarding::class,
);
expect($progress->completedSteps())->toBe(1)
->and($progress->percent())->toBe(50)
->and($progress->currentStep())
->toBe(App\CustomerHealth\Events\TeammateInvited::class);Use CarbonImmutable::setTestNow() and an explicit occurredAt when testing
stall or day-window boundaries.
use ByRcsc\LaravelCustomerHealth\Events\HealthScoreComputed;
use ByRcsc\LaravelCustomerHealth\Events\HealthStateChanged;
Event::fake([
HealthScoreComputed::class,
HealthStateChanged::class,
]);
$result = CustomerHealth::compute(
$this->team,
'customer_health',
);
expect($result->value)->toBeGreaterThanOrEqual(0)
->and($result->value)->toBeLessThanOrEqual(100)
->and($result->breakdown)->not->toBeEmpty()
->and(CustomerHealth::score($this->team, 'customer_health'))
->toEqual($result);
Event::assertDispatched(HealthScoreComputed::class);
Event::assertDispatched(HealthStateChanged::class);For a deterministic assertion, replace application dependencies used by a custom signal or define a test score with fixed signals.
use Carbon\CarbonImmutable;
CarbonImmutable::setTestNow('2026-08-08 00:00:00 UTC');
CustomerHealth::track(new WorkflowCreated(
subject: $this->team,
occurredAt: CarbonImmutable::now('UTC')->subDays(30),
));
$usage = CustomerHealth::featureUsage('workflows')->for($this->team);
expect($usage->eventCount(days: 30))->toBe(1)
->and($usage->eventCount(days: 29))->toBe(0);Reset the test clock after tests when the framework does not already do so.
The package repository runs without an external account or API service:
composer install
composer test
composer analyse
vendor/bin/pint --testIts main suite uses SQLite. CI also exercises database-specific concurrency, indexes, JSON columns, and uniqueness on MySQL and PostgreSQL.