›
›
›
  1. docs
  2. ›
  3. byrcsc/laravel-customer-health
1.x
Browse documentationOpenClose

Getting started

  • Introduction
  • Installation and setup
  • Quick start

Core concepts

  • Product events
  • Features and milestones
  • Onboarding
  • Health scores
  • Summaries and tenancy

Operations

  • Querying customer health
  • Queueing events
  • Recomputing scores
  • Retention and erasure
  • Production operations

Reference

  • Configuration
  • Public API
  • Events
  • Console commands
  • Database storage
  • Testing
  • Troubleshooting

Getting started

  • Introduction
  • Installation and setup
  • Quick start

Core concepts

  • Product events
  • Features and milestones
  • Onboarding
  • Health scores
  • Summaries and tenancy

Operations

  • Querying customer health
  • Queueing events
  • Recomputing scores
  • Retention and erasure
  • Production operations

Reference

  • Configuration
  • Public API
  • Events
  • Console commands
  • Database storage
  • Testing
  • Troubleshooting

byrcsc/laravel-customer-health · 1.x

Testing.

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.

Test an inline product event

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.

Test milestone uniqueness

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.

Test queued tracking

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.

Test onboarding

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.

Test a health score

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.

Test UTC windows

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.

Test the package itself

The package repository runs without an external account or API service:

composer install
composer test
composer analyse
vendor/bin/pint --test

Its main suite uses SQLite. CI also exercises database-specific concurrency, indexes, JSON columns, and uniqueness on MySQL and PostgreSQL.

What to read next

  • Events for event properties and dispatch timing.
  • Database storage for model casts and table columns.
  • Troubleshooting for exceptions that tests can assert.
PreviousDatabase storageNextTroubleshooting
View source

On this page

  1. Test an inline product event
  2. Test milestone uniqueness
  3. Test queued tracking
  4. Test onboarding
  5. Test a health score
  6. Test UTC windows
  7. Test the package itself
  8. What to read next