masterjus / behat-hypervel-extension
Behat extension for testing Hypervel applications: boots the app, injects it into contexts and provides ready-made steps for HTTP, JSON, database, config and fixtures.
Package info
github.com/masterjus/behat-hypervel-extension
pkg:composer/masterjus/behat-hypervel-extension
Requires
- php: ^8.4
- ext-json: *
- ext-swoole: *
- behat/behat: ^3.10
- hyperf/testing: ^3.1.0
- hypervel/coroutine: ^0.3
- hypervel/devtool: ^0.3
- hypervel/foundation: ^0.3
- hypervel/laminas-mime: ^0.1.0
- mockery/mockery: ^1.3
- phpunit/phpunit: ^10.0
Requires (Dev)
- laravel/pint: ^1.24
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-11-05 12:33:03 UTC
README
Behat extension for testing Hypervel applications.
It boots your application once per test run, injects it into your contexts and ships ready-made steps for HTTP requests, JSON responses, database assertions, config overrides and database fixtures.
Requirements
- PHP 8.2+ with the Swoole extension
- Hypervel 0.3
- Behat 3.10+
Installation
1. Install the package
composer require --dev masterjus/behat-hypervel-extension
2. Create the testing environment file
Tests run with their own .env file, tests/.env by default. Before booting the application the extension
replaces all the values loaded from the root .env with the values from it, so the tests never touch the database
from your main .env.
Create tests/.env from your main .env. The values are not merged: everything the application needs must be
in tests/.env. At least:
APP_ENV=testing APP_URL=http://localhost DB_CONNECTION=pgsql DB_HOST=127.0.0.1 DB_PORT=5432 DB_DATABASE=app_testing DB_USERNAME=app DB_PASSWORD=secret
APP_URL is sent as the Host header of every request made by the HTTP steps.
The database from tests/.env is recreated with migrate:fresh at the beginning of every run
(unless refresh_database is false), so never point it at a database with data you need.
If tests/.env is missing, the run stops with an error instead of falling back to the root .env.
Keep tests/.env out of git and commit templates instead, for example tests/.env.example for developers and
tests/.env.ci for CI, which copies it before running the tests:
cp tests/.env.ci tests/.env vendor/bin/behat
3. Enable the extension
# behat.yml default: suites: default: paths: [ "%paths.base%/tests/Feature" ] contexts: - Tests\Behat\FeatureContext extensions: BehatHypervel: ~
4. Autoload the contexts
Behat contexts are regular classes, so add their directory to autoload-dev in your composer.json
and run composer dump-autoload:
"autoload-dev": { "psr-4": { "Tests\\": "tests/", "Tests\\Behat\\": "tests/Behat/" } }
A project layout with the defaults:
project/
├── bootstrap/app.php
├── tests/
│ ├── Behat/FeatureContext.php
│ ├── Feature/users.feature
│ ├── fixtures/users.fixture
│ ├── .env (not in git)
│ └── .env.example
├── behat.yml
└── composer.json
Configuration
default: extensions: BehatHypervel: bootstrap_path: bootstrap/app.php env_path: tests refresh_database: true
| Option | Default | Description |
|---|---|---|
bootstrap_path |
bootstrap/app.php |
File that returns the application, relative to the project root. |
env_path |
tests |
Directory with the .env file for tests. ~ keeps the .env from the project root. |
refresh_database |
true |
Run migrate:fresh once at the beginning of the test run. |
Before requiring bootstrap_path the extension does what artisan does: defines BASE_PATH and
SWOOLE_HOOK_FLAGS and initializes Hypervel\Foundation\ClassLoader. So bootstrap/app.php from a fresh Hypervel
project works as is, no separate bootstrap file for Behat is needed.
The application is not bound to the testing database connection resolver of Hypervel: it keeps one connection for all coroutines, while the application under test may run queries in parallel ones. Connections come from the regular pool.
Context
A context receives the application when it implements BehatHypervel\Context\HypervelAwareContext.
Add the traits with the steps you need:
<?php namespace Tests\Behat; use BehatHypervel\Context\Concerns\InteractsWithDatabase; use BehatHypervel\Context\Concerns\LoadsFixtures; use BehatHypervel\Context\Concerns\MakesHttpRequests; use BehatHypervel\Context\Concerns\WorksWithAppConfig; use BehatHypervel\Context\HypervelAwareContext; use Hypervel\Foundation\Contracts\Application; class FeatureContext implements HypervelAwareContext { use InteractsWithDatabase; use LoadsFixtures; use MakesHttpRequests; use WorksWithAppConfig; protected Application $app; public function setApp(Application $app): void { $this->app = $app; } }
The application is created once and shared between scenarios. Config values changed with the steps below are restored after each scenario.
Steps
HTTP
Given the "Authorization" header is "Bearer token" Given the "Accept-Language" header is absent When I send GET request to "/api/users" When I send POST request to "/api/users" with JSON body: """ {"name": "John"} """ Then status code must be 200 Then the JSON response must be exact: """ {"id": 1, "name": "John"} """ Then the JSON response should be similar: Then the JSON response should be: """ {"id": 1, "name": "John"} """ Then the JSON response property "data.0.id" should be "1" Then the JSON response should have fragment: Then the JSON response should miss fragment:
Supported methods: GET, POST, DELETE, HEAD, OPTIONS.
Headers set with the header is steps apply to the next request only and are cleared after it.
How the JSON steps compare the response:
| Step | Keys order | Lists order | Types | Extra data in the response |
|---|---|---|---|---|
must be exact |
ignored | matters | strict | fails |
should be similar |
ignored | ignored | strict | fails |
should be |
ignored | matters | loose, 1 equals "1" |
fails |
should have fragment |
— | — | strict | allowed |
property ... should be takes a dot path and casts the expected
value to the type of the actual one: "1" matches 1 and 1.0, "true", "false" and "null" match
the corresponding values.
Database
Then the table "users" should have record with: """ {"email": "john@example.com"} """ Then the table "users" should not have record with: Given I execute SQL statement: """ UPDATE users SET deleted_at = NOW() WHERE id = 1 """
Config
Given I set config "app.debug" to value "true" Given I set config "services.mailer" to empty value Given I replace config "services.payments" with: """ {"driver": "fake"} """
Fixtures
Given data from the "users" fixture is loaded
Fixtures are loaded from tests/fixtures/<name>.fixture. The directory can be changed with setFixtureDir().
A fixture uses Gherkin syntax:
Fixture: Users
Optional description
Table: users
Data:
| name | email | deleted_at |
| John | john@example.com | |
Materialized View: users_summary
Empty cells are inserted as NULL. After each scenario the tables from the loaded fixtures are truncated
and the materialized views are reset.
LoadsFixtures needs InteractsWithDatabase in the same context.
| Database | Tables cleanup | Materialized views |
|---|---|---|
| PostgreSQL | TRUNCATE ... RESTART IDENTITY CASCADE |
Supported |
| MySQL | TRUNCATE for each table with FOREIGN_KEY_CHECKS disabled |
Not supported |
Other drivers can be added by implementing BehatHypervel\Fixtures\Database\DatabaseAdapter
and overriding getFixtureDatabaseAdapter() in your context.
Running
vendor/bin/behat
License
MIT