
# Testing

Queuety provides a `QueueFake` class that replaces the real queue with an in-memory store. Dispatched jobs are recorded instead of being written to the database, and `create_batch()` records batch metadata in memory as well. That lets you assert dispatch behavior without running workers or writing to the database. Dispatching by handler name still resolves the handler through the registry, so call `Queuety::init()` first for string-handler dispatches; `Contracts\Job` instances, `chain()`, `create_batch()`, and `batch()` work without it.

## Setting up the fake

Call `Queuety::fake()` at the start of your test. It returns a `QueueFake` instance that records all subsequent dispatches.

```php
use Queuety\Queuety;

class SendEmailTest extends \PHPUnit\Framework\TestCase {
    protected function setUp(): void {
        $this->fake = Queuety::fake();
    }

    protected function tearDown(): void {
        Queuety::reset();
    }
}
```

`Queuety::fake()` creates a new `QueueFake` and wires it into the `Queuety` facade. `Queuety::dispatch()` and `Dispatchable::dispatch()` record pushed jobs. `Queuety::chain()` records each chained job as a normal push in dispatch order. `Queuety::create_batch()` records both the pushed jobs and the batch metadata used by `assert_batched()`. `Queuety::batch()` records the pushed jobs, but it does not create fake batch metadata.

Call `Queuety::reset()` in `tearDown()` to clear the fake between tests. It clears all facade state, so call `Queuety::init()` again if a later test needs the real queue.

## Asserting jobs were pushed

### `assert_pushed()`

Assert that a specific job class was dispatched at least once.

```php
public function test_welcome_email_is_sent(): void {
    $this->fake = Queuety::fake();

    // Code under test...
    register_new_user( 'user@example.com' );

    $this->fake->assert_pushed( SendWelcomeEmailJob::class );
}
```

Pass an optional callback to filter by payload or other attributes. The callback receives the recorded job data array with keys `handler`, `payload`, `queue`, and `instance`. `instance` is `null` for jobs dispatched through the facade or the `Dispatchable` trait, so match on `handler` and `payload`.

```php
$this->fake->assert_pushed( SendWelcomeEmailJob::class, function ( array $job ) {
    return $job['payload']['to'] === 'user@example.com';
} );
```

### `assert_pushed_times()`

Assert that a job was pushed an exact number of times.

```php
$this->fake->assert_pushed_times( SendEmailJob::class, 3 );
```

### `assert_not_pushed()`

Assert that a job was never dispatched.

```php
$this->fake->assert_not_pushed( DeleteAccountJob::class );
```

### `assert_nothing_pushed()`

Assert that no jobs were dispatched at all.

```php
public function test_no_jobs_on_invalid_input(): void {
    $this->fake = Queuety::fake();

    process_input( '' ); // invalid, should not dispatch anything

    $this->fake->assert_nothing_pushed();
}
```

## Asserting batches

### `assert_batched()`

Assert that a batch was dispatched. Optionally pass a callback to filter by batch contents.

```php
$this->fake->assert_batched();

$this->fake->assert_batched( function ( array $batch ) {
    return count( $batch['jobs'] ) === 3;
} );
```

The callback receives an array with `jobs` and `options` keys.

## Inspecting pushed jobs

Use `pushed()` to retrieve all recorded dispatches for a given class, and `batches()` to retrieve all recorded batches.

```php
$emails = $this->fake->pushed( SendEmailJob::class );
// [ ['handler' => '...', 'payload' => [...], 'queue' => 'default', 'instance' => null], ... ]

$batches = $this->fake->batches();
// [ ['jobs' => [...], 'options' => [...]], ... ]
```

## Resetting between tests

Call `reset()` on the fake to clear all recorded jobs and batches without creating a new instance.

```php
$this->fake->reset();
```

Or call `Queuety::reset()` to fully tear down the fake and all internal state.

## Full example

```php
use PHPUnit\Framework\TestCase;
use Queuety\Queuety;

class OrderWorkflowTest extends TestCase {
    private \Queuety\Testing\QueueFake $fake;

    protected function setUp(): void {
        $this->fake = Queuety::fake();
    }

    protected function tearDown(): void {
        Queuety::reset();
    }

    public function test_order_dispatches_payment_job(): void {
        $order = create_order( [ 'total' => 99.99 ] );

        $this->fake->assert_pushed( ProcessPaymentJob::class, function ( array $job ) use ( $order ) {
            return $job['payload']['order_id'] === $order->id;
        } );
        $this->fake->assert_not_pushed( RefundJob::class );
    }

    public function test_bulk_import_dispatches_batch(): void {
        start_bulk_import( $rows );

        $this->fake->assert_batched( function ( array $batch ) use ( $rows ) {
            return count( $batch['jobs'] ) === count( $rows );
        } );
    }

    public function test_nothing_dispatched_without_data(): void {
        process_empty_queue();

        $this->fake->assert_nothing_pushed();
    }
}
```
