testing php applications

Testing Queued Jobs, Mail, Events, And External APIs

Asynchronous jobs and external integrations need tests at more than one level. Unit tests can check message creation and service rules. Integration tests should prove adapters, serialisation, retries, and failure handling where those boundaries matter.

Separate Dispatch From Execution

One test can prove that an order requests a receipt email. Another can execute the mail builder. A queue integration test can prove serialisation and worker handling. Keeping those concerns separate makes failures easier to diagnose.

Control External Services

Use fake or stub HTTP responses for service-level tests, then keep a smaller set of adapter or sandbox checks for the real integration contract. Test timeouts, non-200 responses, malformed data, and idempotent retry behaviour.

PHP example
<?php

declare(strict_types=1);

function retryPlan(int $attempt, int $maxAttempts): string
{
    return $attempt < $maxAttempts ? 'retry later' : 'record failure';
}

echo retryPlan(1, 3) . PHP_EOL;
echo retryPlan(3, 3) . PHP_EOL;

// Prints:
// retry later
// record failure

Common Mistakes

  • Calling live third-party APIs from every unit test.
  • Testing dispatch but never testing job execution.
  • Ignoring retries and duplicate delivery.
  • Asserting implementation-specific event internals instead of outcomes.

What To Practise

  • Separate dispatch, execution, and adapter checks.
  • Test failures and retries.
  • Design idempotent queued work.

Practice

Practice: Plan Receipt Email Tests

Plan tests for a queued receipt email after a paid order.

Requirements

  • Check dispatch after payment.
  • Check email content separately.
  • Include retry behaviour.
  • Prevent duplicate receipt effects.
Show solution

Each check protects one boundary.

  • Prove that successful payment dispatches one receipt job.
  • Test the mail builder separately and assert the order number and total.
  • Run the job through a queue integration check and confirm it completes.
  • Retry the same job and prove it does not create a second receipt effect.

The exact fake queue or mail helper depends on the framework. The boundary split is transferable.