Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Testies Laravel Package

mindplay/testies

Testies is a lightweight PHP testing framework with a clean, expressive API for writing and organizing tests. It offers test suites, assertions, mocking support, and a simple runner, making it a compact alternative to heavier tools while fitting easily into existing PHP projects.

View on GitHub
Deep Wiki
Context7

Getting Started

Install as a dev dependency only:

composer require --dev mindplay/testies

There is no service provider, no config file, no Laravel integration layer. It is a flat set of namespaced functions (test(), run(), and a bag of assertions like eq(), ok(), throws()) that get imported with use function. That's the whole mental model — treat it as a script runner, not a framework.

Where to look first: the package README, then vendor/mindplay/testies/src/ — it's small enough to read in one sitting, and you'll want to know exactly which assertion functions exist because there's no IDE-friendly fluent API to discover (expect(...)->toBe(...) does not exist here).

First use case — don't start with the app. The right first fit in a Laravel codebase is a self-contained unit: a value object, a pure helper, a string/number formatter, or an internal packages/ library you haven't wired into the framework yet. Write one file, import functions, define cases, call run():

<?php
require __DIR__ . '/../vendor/autoload.php';

use function mindplay\testies\{test, run, eq, ok, throws};

test('Money normalizes to cents', function () {
    eq(Money::fromString('12.30')->cents(), 1230);
});

test('Money rejects negative values', function () {
    throws(fn() => Money::fromString('-1.00'), InvalidArgumentException::class);
});

run();

Execute with php tests/money.php. If it exits non-zero on failure (it does), it already drops into CI without ceremony.

Implementation Patterns

Pattern 1 — Standalone package tests. For an internal composer package (packages/billing, packages/geo), commit a tests/run.php and wire it up:

"scripts": {
    "test:geo": "php packages/geo/tests/run.php"
}

This gives you fast feedback without booting PHPUnit's bootstrap or Laravel's kernel. Good for pure logic where the framework would only add latency.

Pattern 2 — Bootstrapping Laravel manually in a script. You can still exercise container-resolved services from a plain testies script; you just do the bootstrapping yourself:

$app = require __DIR__ . '/../bootstrap/app.php';
$app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();

test('ReportBuilder totals line items', function () use ($app) {
    $builder = $app->make(ReportBuilder::class);
    eq($builder->total([100, 250, 50]), 400);
});

Use this only for narrow service-level checks. The moment you need RefreshDatabase, HTTP assertions, actingAs(), or queue/event fakes, you're in PHPUnit/Pest territory — don't fight it.

Pattern 3 — Smoke tests for CLI scripts and one-off commands. artisan commands that are hard to test through the standard TestCase (long-running loops, filesystem side effects) can be shelled out and verified with ok() / eq() on their output. This is often faster to write than mocking the console kernel.

Pattern 4 — Grouping by file, not by suite. Without describe() or data providers, the idiom is one test file per unit, many test() calls inside, run() at the bottom. If you need shared setup, do it in a closure above the test() calls — there's no setUp()/tearDown() lifecycle to lean on.

Pattern 5 — Mixing with PHPUnit deliberately. You can keep PHPUnit as the default composer test and add a composer test:units script for testies-run micro-suites. Splitting by kind of test (framework-coupled vs. pure) is cleaner than trying to force one runner everywhere.

Gotchas and Tips

  • Namespace collisions. All assertions are global functions imported via use function. assert() in particular collides with PHP's built-in (and PHPUnit's), and is(), eq(), ok() are generic enough to clash in big codebases. Always import explicitly (use function mindplay\testies\{test, run, eq};) rather than relying on loose globals, and never require two assertion libraries into the same file.
  • No lifecycle, no mocking, no data providers. There is no setUp, no mock(), no dataset(). If you catch yourself writing a hand-rolled for loop to feed cases into a test(), you've outgrown the tool for that file — move it to PHPUnit where data providers exist.
  • run() must be called explicitly, and it's the thing that emits results and sets the exit code. Forgetting it means a green build that tested nothing. Guard against this by having a single run() per entry-point file and nothing else at the top level.
  • Bootstrap order matters. Because you're in a bare script, any config your code needs (env(), config cache, service bindings) must exist before the assertions run. Load .env and bootstrap the kernel at the top of the file, before test() definitions, or you'll get confusing "value is null" failures that are really bootstrap failures.
  • No coverage integration. There's no --coverage flag. If you need coverage numbers, wrap the invocation: XDEBUG_MODE=coverage php -d ... tests/run.php and feed the output to your normal coverage tooling, or just accept that these micro-suites are for correctness, not metrics.
  • Failure output is terse. Assertion failures throw with a message comparing values, but there's no diff rendering for arrays/objects. For complex structures, serialize to JSON in the message yourself (eq(json_encode($actual), json_encode($expected))) so CI logs are actually readable.
  • Maintenance risk. Seven stars, zero dependents, last tagged release April 2024. It's a single-maintainer utility. Pin the version in composer.json and be prepared to vendor it or replace it — the surface area is small enough that a self-written eq()/throws() helper is a realistic fallback.
  • Where to extend: the extension point is the file itself — helper functions you define above run() are the idiomatic way to add custom assertions. Keep them in a shared tests/support.php required by each script so the "framework" you build stays consistent across suites.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource