Download the PHP package ralder/phpunit-query-scaling without Composer

On this page you can find all versions of the php package ralder/phpunit-query-scaling. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package phpunit-query-scaling

ralder/phpunit-query-scaling

Controlled multi-scale query-count assertions for Laravel tests (PHPUnit and Pest).

Instead of asserting "this scenario runs at most N queries" — a number nobody can defend six months later — this package measures your scenario at several dataset scales inside real database transactions and asserts a contract on how the query count changes:

When a contract is violated, every SQL fingerprint that grew across the scales is reported, so the N+1 query (or worse) is found without digging through logs.

Installation

Requirements: PHP 8.3+, Laravel 12/13, PHPUnit 12/13 or Pest 4/5 (the CI matrix verifies Laravel ^12/^13 × PHP 8.3–8.5, plus a separate PHPUnit 13 / Pest 5 job — support claims reflect the verified CI matrix, including minimum dependency versions).

Usage

PHPUnit

Pest

The trait works with any Laravel-aware test case: Orchestra Testbench and Illuminate\Foundation\Testing\TestCase alike.

What happens when it detects growth

A failing assertion throws PHPUnit's ExpectationFailedException with a deterministic, CI-friendly report:

The trend line is a purely diagnostic least-squares fit over the measured checkpoints. No package can (and this one does not) prove an asymptotic complexity class from measurements: the assertion verifies the contract at the measured scale factors only.

Expectations

Factory Passes when
QueryGrowth::constant(int $tolerance = 0) max(queries) − min(queries) ≤ tolerance
QueryGrowth::atMostSlope(float $maxSlope, float $tolerance = 0) every consecutive segment satisfies queries(i+1) − queries(i) ≤ maxSlope × (scale(i+1) − scale(i)) + tolerance; decreasing segments always pass

API reference

assertQueryScaling() receives its tolerance through the expectation factory, for example QueryGrowth::constant(tolerance: 1) or QueryGrowth::atMostSlope(maxSlope: 1, tolerance: 1). Prefer named arguments when calling either assertion so optional parameters remain unambiguous.

Invalid scale lists (unsorted, duplicates, fewer than two factors, non-positive factors) are rejected with InvalidArgumentException before any database work happens.

How isolation works

For every scale factor the package:

  1. records the current transaction depth of each measured connection,
  2. begins one nested transaction (savepoint) inside it,
  3. runs populate($scale), optional warmup($scale) (unmeasured), then measures run(),
  4. restores the transaction depth — usually by rolling back — even if your callbacks throw.

If your run() callback committed or rolled back transactions outside its own scope (or issued implicit-commit DDL on MySQL), the restore step throws IsolationViolationException, explaining the observed depth and connection. The package fails loudly instead of pretending the environment is clean.

A callback that commits the isolation transaction and then opens a new one — ending at exactly the measured depth — is caught the same way: the package watches the connection's transaction lifecycle events and reports the breach instead of silently rolling back the replacement transaction. Watching those events requires an event dispatcher on the connection (the normal Laravel setup); without one, restore() falls back to transaction-depth checks, which cannot see a balanced commit-and-re-open.

Isolation is limited to the database connection. Redis, the filesystem, queues, HTTP calls are not isolated — keep your run() callback idempotent with respect to them. This limitation is contractual; it will not change silently with new isolation strategies.

populate() and warmup() run in the same isolation scope; they receive the scale factor and must be pure functions of it (only populate the requested amount, no external side effects).

SQLite note: in-memory test databases are perfectly fine. Real FOREIGN keys and savepoints work with the transaction driver.

Multi-connection measurement

Query fingerprints

A fingerprint is {connection, normalized SQL} where normalization is conservative: trim + whitespace collapse. Two statements count as the same fingerprint iff they are identical on the same connection. Parameter values never change the fingerprint.

Opt-in normalizeLiterals: true additionally folds string and numeric literals into ?. It is a heuristic (useful when Eloquent is bypassed and raw SQL interpolates values) and is off by default; report the SQL you actually saw before enabling it.

Custom isolation

Provide your own Ralder\QueryScaling\Core\IsolationStrategy implementation (e.g. schema dumps or read-only connections) and pass it as the isolation: argument. Throw Ralder\QueryScaling\Core\IsolationViolationException when your strategy detects a callback broke its invariants — the framework will surface it instead of the measurement result.

What this package is not

Compatibility

The supported combinations are the ones exercised by the repository's CI workflow:

PHP Laravel Testbench PHPUnit / Pest CI coverage
8.3, 8.4, 8.5 ^12 ^10 PHPUnit 12 / Pest 4 standard matrix; PHP 8.3 also uses prefer-lowest
8.3, 8.4, 8.5 ^13 ^11 PHPUnit 12 / Pest 4 standard matrix
8.5 ^13 ^11 PHPUnit 13 / Pest 5 dedicated compatibility job

Warmup

Pass warmup: fn (int $scale) => ... when a route, container, metadata cache, or other read-only application state should be initialized before measurement. Warmup runs once per scale after populate, with the collector disabled, and its queries are not counted. It runs inside the same isolation scope and must not mutate the dataset; use populate for all scale-dependent data setup.

Troubleshooting

Development

CI runs Laravel ^12/^13 × PHP 8.3/8.4/8.5 (including prefer-lowest) and a separate PHPUnit 13 / Pest 5 job on PHP 8.5.

License

MIT. See LICENSE.md.


All versions of phpunit-query-scaling with dependencies

PHP Build Version
Package Version
Requires php Version ^8.3
illuminate/contracts Version ^12.0 || ^13.0
illuminate/database Version ^12.0 || ^13.0
illuminate/events Version ^12.0 || ^13.0
illuminate/support Version ^12.0 || ^13.0
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package ralder/phpunit-query-scaling contains the following files

Loading the files please wait ...