Testing utilities for Marko---reusable fakes with built-in assertions that eliminate test boilerplate.
marko/testing provides a set of in-memory fakes that replace real infrastructure dependencies during tests. Instead of mocking interfaces by hand or spinning up real services, you drop in a fake, run your code, and call assertion methods directly on the fake. This removes hundreds of lines of boilerplate and keeps tests focused on behavior.
composer require marko/testing --devFakeEventDispatcher, FakeMailer, FakeQueue, FakeSession, FakeCookieJar, FakeLogger, FakeConfigRepository, FakeAuthenticatable, FakeUserProvider, FakeGuard
use Marko\Testing\Fake\FakeEventDispatcher;
$dispatcher = new FakeEventDispatcher();
$dispatcher->dispatch(new OrderPlaced($order));
$dispatcher->assertDispatched(OrderPlaced::class);
$dispatcher->assertDispatchedCount(OrderPlaced::class, 1);
$dispatcher->assertNotDispatched(OrderShipped::class);use Marko\Testing\Fake\FakeMailer;
$mailer = new FakeMailer();
$mailer->send($message);
$mailer->assertSent(WelcomeEmail::class);
$mailer->assertSentCount(WelcomeEmail::class, 1);
$mailer->assertNothingSent();use Marko\Testing\Fake\FakeQueue;
$queue = new FakeQueue();
$queue->push(new ProcessOrder($order));
$queue->assertPushed(ProcessOrder::class);
$queue->assertPushedCount(ProcessOrder::class, 1);
$queue->assertNotPushed(SendInvoice::class);
$queue->assertNothingPushed();use Marko\Testing\Fake\FakeSession;
$session = new FakeSession();
$session->put('user_id', 42);
$value = $session->get('user_id'); // 42
$session->forget('user_id');use Marko\Testing\Fake\FakeCookieJar;
$cookies = new FakeCookieJar();
$cookies->set('token', 'abc123');
$value = $cookies->get('token'); // 'abc123'use Marko\Testing\Fake\FakeLogger;
$logger = new FakeLogger();
$logger->info('User logged in');
$logger->error('Something failed');
$logger->assertLogged('User logged in');
$logger->assertNothingLogged();use Marko\Testing\Fake\FakeConfigRepository;
$config = new FakeConfigRepository([
'auth.defaults.guard' => 'web',
'app.name' => 'Marko',
]);
$value = $config->get('auth.defaults.guard'); // 'web'use Marko\Testing\Fake\FakeAuthenticatable;
$user = new FakeAuthenticatable(id: 1, password: 'hashed-secret');
$user->getAuthIdentifier(); // 1
$user->getAuthPassword(); // 'hashed-secret'use Marko\Testing\Fake\FakeUserProvider;
use Marko\Testing\Fake\FakeAuthenticatable;
$user = new FakeAuthenticatable(id: 1);
$provider = new FakeUserProvider($user);
$found = $provider->retrieveById(1); // returns $useruse Marko\Testing\Fake\FakeGuard;
use Marko\Testing\Fake\FakeAuthenticatable;
$guard = new FakeGuard(name: 'web', attemptResult: true);
// Set a user directly
$user = new FakeAuthenticatable(id: 1);
$guard->setUser($user);
$guard->assertAuthenticated();
// Test login attempt
$guard->attempt(['email' => 'user@example.com', 'password' => 'secret']);
$guard->assertAttempted();
// Assert no user logged in
$guard->logout();
$guard->assertGuest();
$guard->assertLoggedOut();dispatch($event): void— Record a dispatched eventdispatched(string $class): array— Return all dispatched events of a classassertDispatched(string $class): void— Assert an event was dispatchedassertNotDispatched(string $class): void— Assert an event was not dispatchedassertDispatchedCount(string $class, int $count): void— Assert exact dispatch count
send($message): void— Record a sent messageassertSent(string $class): void— Assert a message was sentassertNothingSent(): void— Assert no messages were sentassertSentCount(string $class, int $count): void— Assert exact sent count
push($job): void— Record a pushed jobassertPushed(string $class): void— Assert a job was pushedassertNotPushed(string $class): void— Assert a job was not pushedassertPushedCount(string $class, int $count): void— Assert exact pushed countassertNothingPushed(): void— Assert no jobs were pushed
info(string $message): void,error(),warning(), etc. — Record log entriesassertLogged(string $message): void— Assert a message was loggedassertNothingLogged(): void— Assert nothing was logged
new FakeGuard(string $name, bool $attemptResult)— Create guard;$attemptResultcontrols whatattempt()returnssetUser(?AuthenticatableInterface $user): void— Set the current authenticated userattempt(array $credentials): bool— Record credentials attempt and return configured resultlogin(AuthenticatableInterface $user): void— Log in a user directlylogout(): void— Clear current user and record logoutassertAuthenticated(): void— Assert a user is currently authenticatedassertGuest(): void— Assert no user is authenticatedassertAttempted(?callable $callback = null): void— Assert attempt() was called, optionally matching credentials via callbackassertNotAttempted(): void— Assert attempt() was never calledassertLoggedOut(): void— Assert logout() was called
marko/testing ships Pest custom expectations that are auto-loaded via autoload.files.
use Marko\Testing\Fake\FakeEventDispatcher;
use Marko\Testing\Fake\FakeGuard;
use Marko\Testing\Fake\FakeMailer;
use Marko\Testing\Fake\FakeQueue;
use Marko\Testing\Fake\FakeLogger;
// FakeEventDispatcher
expect($dispatcher)->toHaveDispatched(OrderPlaced::class);
// FakeMailer
expect($mailer)->toHaveSent();
expect($mailer)->toHaveSent(fn ($msg) => $msg instanceof WelcomeEmail);
// FakeQueue
expect($queue)->toHavePushed(ProcessOrder::class);
// FakeLogger
expect($logger)->toHaveLogged('User logged in');
// FakeGuard
expect($guard)->toHaveAttempted();
expect($guard)->toHaveAttempted(fn ($creds) => $creds['email'] === 'user@example.com');
expect($guard)->toBeAuthenticated();Full usage, API reference, and examples: marko/testing