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

Monkey Laravel Package

brain/monkey

Brain Monkey is a PHP testing library for mocking and spying on global functions and class methods, with seamless PHPUnit integration and no dependencies—ideal for testing legacy code, WordPress plugins, and function-heavy PHP applications.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Release delta (2.7.0 vs. 2.6.2). Minor, non-breaking, no deprecations. Five changes matter to us, one of them directly:

  1. has_filter() / has_action() mocks now accept $priority (tracking WordPress 6.9). Previously we could not express "a callback was registered at priority 20" — a common, real hook pattern. This closes a genuine assertion gap rather than adding a convenience.
  2. Mock defaults aligned with WP Core for doing_action(), doing_filter(), do_action_deprecated(), apply_filters_deprecated(), has_filter(), has_action(). This is the one change that can turn our own tests from green to red: any test written against the old, incorrect defaults (e.g. omitting a parameter that Core allows to be omitted) may now need editing. The change itself is a correctness fix that shrinks the "false green" surface.
  3. PHP 8.4 and 8.5 compatibility confirmed. Directly retires part of the CI-matrix uncertainty flagged in the original spike question 2.
  4. Mockery constraints tightened. Relevant to our "one Mockery major in the vendor tree" rule — must be verified at pin time, not assumed.
  5. Dist no longer ships dev-only files. Small reduction in the artifact/supply-chain surface for a package that rewrites include; welcome, not decisive.

What this actually is. brain/monkey is not a Laravel package and has no Laravel-specific surface. It is a unit-testing utility for WordPress-oriented PHP: it wraps Mockery to let you (a) replace global/namespaced PHP function calls with controlled doubles and (b) assert against the WordPress hook system (add_action, add_filter, do_action, apply_filters) without loading WordPress. It is the modern successor to the 10up/wp_mock pattern and is the de-facto standard in the WP plugin ecosystem (WP-CLI, Yoast, and most serious plugin vendors use it or its conventions).

Architecture fit. Fit is binary, not gradient — and 2.7.0 does not change this:

  • Fit = yes if we own any WordPress plugin/theme code, or any procedural PHP that calls global functions we cannot inject (e.g., wp_remote_get, wp_schedule_event, get_option, time, file_get_contents). It lets that code be unit-tested in milliseconds without a WP runtime.
  • Fit = no for Laravel application code. Laravel's container, facades, Http::fake(), Queue::fake(), and plain Mockery already cover every case this solves, with better ergonomics and no global-state hacks. The new $priority assertion support is WordPress-hook semantics specifically; it is not a reason to introduce hook mocking into a Laravel service.

Integration feasibility. Unchanged and still low effort, high constraint density. Install is composer require --dev brain/monkey; the API is small (Monkey\setUp()/tearDown(), Functions\expect/when, Actions\expectAdded, Filters\expectApplied, Hooks\did_action). The real cost remains bootstrap ordering: the 2.x line intercepts function calls by rewriting files at include time, so the library must be initialized before the code under test is loaded, and functions in already-loaded files cannot be redefined. That is a "read the docs once, then never again" tax — but it silently produces confusing failures when violated. 2.7.0 is a drop-in for 2.6.x; adopt it as a patch-level bump, not a migration.

Technical risk.

  • Medium — stream-wrapper/opcache interaction. Code-coverage drivers, opcache.preload, and some CI container images are known friction points; this must be proven in our CI, not assumed. PHP 8.4/8.5 confirmation reduces the version-support unknown but does not substitute for the opcache/coverage proof.
  • Medium → Low-Medium — false greens. The default-alignment fix (item 2) removes a class of divergence for the six affected functions, and $priority support removes a "can't assert this, so assert nothing" gap. The residual risk is unchanged, though: Functions\when('x')->justReturn(null) and unstubbed-expectation defaults can still make a test pass while production behaviour diverges. This remains a convention problem, not a library problem.
  • Low-Medium — global state leakage. Hooks registered in one test persist into the next unless tearDown() runs; PHPUnit processIsolation/Paratest sidestep it, in-process parallelism does not.
  • Low-Medium — upgrade-induced test churn. The corrected mock defaults may invalidate tests we wrote against the old behaviour. Scope is limited and the fix direction is right, but it must be an explicit upgrade step, not a surprise.
  • Low — bus factor. Tiny maintainer pool (Brain-WP / G. Mazzapica), MIT licensed, low churn — and 2.7.0 shows two contributors including a reviewer with WordPress Core context, which is mild positive evidence of continued stewardship. Abandonment risk is real but mitigation (fork, ~1k LOC of glue) is cheap.
  • Data-quality caveat: the "0 dependents" figure in the supplied metrics is clearly an artifact — the package is widely used transitively. Do not let that number drive the decision; the 345 stars and longevity are the meaningful signals.

Key questions for the spike (timebox: 1 day).

  1. Which of our repositories, if any, actually call WP or global functions? If zero, reject immediately.
  2. Does our CI (PHP version matrix, xdebug/pcov coverage, opcache settings) tolerate include-time function rewriting? Extend the matrix to PHP 8.4/8.5 now that upstream claims support.
  3. New: do the tightened Mockery constraints in 2.7.0 conflict with the Mockery version we pin elsewhere? Resolve at composer update time on a real branch.
  4. New: do we have tests that assert has_filter/has_action or rely on the previously-wrong default arguments? Those are the upgrade blast radius — identify them before bumping.
  5. Do we already run a real-WordPress integration suite (wp-env, wp-browser)? If yes, this is a complement, not a replacement — we must define the unit/integration boundary explicitly.
  6. Supply-chain review: are we comfortable with a dev-only dependency that rewrites include? Still yes-leaning — it is dev-only, never ships, and 2.7.0's dist slimming further reduces the shipped-surface question.
  7. PHPUnit 10/11 compatibility at the version we'd pin (unchanged; not addressed by this release).

Integration Approach

Stack fit and placement. Do not put this in the Laravel application's test suite. Put it in a separate Composer package — either a path repository in a monorepo or a standalone plugin repo — with its own phpunit.xml, its own autoload root, and its own composer.json dev dependencies. The Laravel app then consumes the plugin package as a normal vendor dependency (or via HTTP, if the plugin only talks to our API). This keeps the stream-wrapper bootstrap out of the app's test lifecycle entirely, which is the single most important architectural decision here.

Sequencing.

  1. Boundary decision — inventory which classes/functions call WP globals; classify each as "pure" (testable now, no monkey business), "WP-coupled" (Brain Monkey), or "integration" (real WP).
  2. Pilot — one WP-coupled class, 5–10 tests, in a scratch branch, run in our real CI image on ^2.7. Success criterion: green build with coverage enabled, no opcache complaints, and zero Mockery constraint conflicts.
  3. Bootstrap contract — a tests/bootstrap.php that calls Monkey\setUp() in a PHPUnit @before/Pest beforeEach hook and asserts tearDown() runs; wrap in a base TestCase so no test can forget.
  4. Convention doc — a short ADR: "Functions\when(...)->justReturn(...) is for collaborator stubs; Functions\expect(...)->once() is required for anything with a side effect. Always pass $priority explicitly when asserting has_filter/has_action." The second sentence is new with 2.7.0 and is where the added capability actually converts into quality.
  5. Expand to the rest of the WP-coupled surface; keep an integration suite for anything that touches the DB, HTTP, or hook ordering.

Migration path. From 10up/wp_mock the translation is mechanical: WP_Mock::userFunction('x', ['return' => 1]) → Functions\when('x')->justReturn(1); WP_Mock::expectActionAdded(...) → Actions\expectAdded(...); WP_Mock::wpFunction(...)->times(1) → Functions\expect(...)->once(). Doing it file-by-file alongside other work (no dedicated migration sprint) is realistic for a normal-sized plugin. From "no tests at all," start with the pure classes and only reach for this where globals actually block testability.

Upgrading from 2.6.x. Treat as a routine patch bump with one verification pass: composer update brain/monkey, then run the full suite and inspect any test touching doing_action, doing_filter, do_action_deprecated, apply_filters_deprecated, has_filter, or has_action. Failures there are expected and indicate tests that encoded the pre-2.7 (incorrect) defaults. Budget well under a day; no dedicated project.

Compatibility notes. PHPUnit-agnostic and Pest-compatible; requires Mockery. Pin ^2.7 (not ^2.6, to get the corrected defaults and $priority support) and lock Mockery to the major we already use elsewhere to avoid two Mockery majors in one vendor tree — but validate that against 2.7.0's tightened constraints before committing to the pin. PHP 8.4/8.5 are now upstream-supported; add them to the CI matrix.

Operational Impact

Maintenance. Near-zero steady state. Upstream churn is low, semantic versioning is respected, and the API is small enough that a breaking change is cheap to absorb. 2.7.0 is consistent with that pattern: additive, correctness-oriented, contributor-diverse. The genuine maintenance cost is our own convention discipline: the library is only as good as the stub contracts we write around it. Budget a standing PR-review rule rather than ongoing engineering time.

Support. Community-only — GitHub issues, no vendor, no SLA. Acceptable for a dev-only test dependency. Mitigate bus factor by pinning exactly, vendoring the fork path in our notes (MIT permits it; the glue is small), and keeping the pilot reproducible so a future maintainer can re-derive decisions from the ADR.

Scaling. Negligible per-test cost; the include-time rewriting adds a small constant to file loading, invisible at plugin-code scale. Concurrency is fine under process-per-worker runners (Paratest, PHPUnit --process-isolation); it is not safe for in-process parallel or long-lived test workers, so don't reach for it if we ever move to a persistent-process test runner. Scaling the number of tests is cheap; scaling the number of teams using it is where the convention doc earns its keep.

Failure modes to instrument against.

Mode Symptom Mitigation
Bootstrap ordering "Call to undefined function" or real WP function used Base TestCase enforces setUp() first; fail loudly
Unstubbed expectation Test green, production diverges Lint rule / review checklist: side-effecting calls need expect()
Hook leakage Order-dependent flaky tests tearDown() in base class; run suite with --order-by=random in CI
Rewriting incompatibility Coverage driver errors, silent non-interception Prove in CI image during pilot; gate on it
Namespaced function mismatch Stub never invoked Convention: always stub fully-qualified names
Mock-default drift on upgrade Tests fail (or silently pass) after a mock-behaviour fix like 2.7.0's Run full suite on the bump; review any test touching the six affected functions; assert $priority explicitly

Ramp-up. One to two days for a developer already fluent in Mockery/PHPUnit; the only genuinely new concepts are bootstrap ordering and the hook assertion API. Plan a single brown-bag session and a 30-line example test as the onboarding artifact — that is sufficient. Do not schedule formal training.

Recommendation. Adopt conditionally and narrowly: approve only if we own WordPress-side code, and only as a dev dependency inside a separately-packaged plugin test suite. Reject outright for Laravel application code. Pin ^2.7 — it is a low-risk correctness upgrade that retires part of the CI-matrix unknown, removes a false-green class, and enables priority-aware hook assertions we previously could not express. Gate adoption on the one-day CI spike that proves coverage-driver and opcache compatibility; that remains the only finding that can invalidate the whole evaluation.

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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin