VOOZH about

URL: https://deepwiki.com/hypervel/testbench/2.1-test-lifecycle-and-hooks

⇱ Test Lifecycle and Hooks | hypervel/testbench | DeepWiki


Loading...
Last indexed: 7 February 2026 (93289f)
Menu

Test Lifecycle and Hooks

This document explains the complete test execution lifecycle in the Hypervel Testbench, including the order of hook methods, one-time bootstrapping, per-test setup and teardown phases, and available extension points. Understanding this lifecycle is essential for writing effective tests and customizing test behavior.

For details on how the application instance is created during setup, see Application Creation and Container Setup. For information on how coroutines are managed during test execution, see Coroutine Context and Hyperf Integration. For environment management and reloading patterns, see Reloading and Environment Management.


Lifecycle Overview

The testbench implements a sophisticated lifecycle that distinguishes between one-time initialization (performed once per test run) and per-test setup/teardown (performed for each individual test method). This design optimizes performance while maintaining test isolation.

Complete Lifecycle Flow


Sources: src/TestCase.php36-110 src/Bootstrapper.php21-40


One-Time Bootstrapping

The testbench uses a static flag $hasBootstrappedTestbench to ensure that bootstrapping occurs exactly once per PHPUnit test run, regardless of how many test classes or test methods execute.

Bootstrapping Guard Mechanism

The guard is implemented in the setUp() method:


This check occurs at src/TestCase.php40-43

Bootstrapper Operations

When Bootstrapper::bootstrap() executes, it performs several critical initialization tasks:

OperationPurposeFile Reference
Load testbench.yamlParse configuration file to get environment settingssrc/Bootstrapper.php23-25
Define BASE_PATH constantSet application base directory (defaults to workbench/)src/Bootstrapper.php27-32
Define SWOOLE_HOOK_FLAGSConfigure Swoole coroutine hookssrc/Bootstrapper.php33
Generate .env fileCreate environment file from YAML env keysrc/Bootstrapper.php98-108
Generate composer.lockCreate mock composer.lock with provider configurationsrc/Bootstrapper.php56-78
Register purge filesSchedule cleanup of temporary files on shutdownsrc/Bootstrapper.php119-145
Initialize ClassLoaderSetup class scanning with TestScanHandlersrc/Bootstrapper.php39

The bootstrapping only loads configuration and defines constants—it does not create the application instance. Application creation occurs separately during each test's setUp() phase.

Sources: src/Bootstrapper.php21-40 src/TestCase.php40-43


Per-Test Setup Phase

Each test method executes within a fresh application context. The setup phase creates a new application instance, registers providers, configures routes, and prepares the database.

Setup Execution Flow


Sources: src/TestCase.php38-58

Setup Method Breakdown

The setUp() method performs the following operations in order:

  1. Bootstrap Check src/TestCase.php40-43: Conditionally calls Bootstrapper::bootstrap() on first test execution.

  2. Register Callback src/TestCase.php45-51: Registers an afterApplicationCreated callback that will execute after provider booting:

    
    
  3. Parent Setup src/TestCase.php53: Calls parent::setUp() which triggers:

    • Application creation via createApplication()
    • Environment definition via defineEnvironment()
    • Service provider booting
    • Execution of registered callbacks
  4. Coroutine Setup src/TestCase.php57: Executes attribute-based setup inside coroutine context:

    
    

Application Creation Callbacks

The afterApplicationCreated callback performs three critical operations:

  • Timer::clearAll(): Clears all Swoole timers to prevent interference between tests
  • CoordinatorManager::until(Constants::WORKER_EXIT)->resume(): Resumes the worker exit coordinator
  • $this->setUpApplicationRoutes($this->app): Registers routes defined in defineRoutes() and defineWebRoutes()

These operations ensure that each test starts with a clean Swoole timer state and properly configured routing.

Sources: src/TestCase.php38-58


Test Execution Phase

After the setup phase completes, PHPUnit invokes the test method. At this point:

  • The application instance is fully initialized and stored in $this->app
  • All service providers are registered and booted
  • Routes are defined and middleware is applied
  • Database migrations are executed (if using HandlesDatabases trait)
  • The test runs inside a Hyperf coroutine context

Test methods can access the application container, make HTTP requests, query the database, and use any registered services. The application lifecycle is managed transparently by the TestCase base class.


Per-Test Teardown Phase

The teardown phase cleans up after each test, ensuring that subsequent tests start with a fresh state.

Teardown Execution Flow


Sources: src/TestCase.php80-88

Teardown Method Operations

The tearDown() method src/TestCase.php80-88 performs cleanup in this order:

  1. Coroutine Teardown src/TestCase.php83: Executes attribute-based teardown inside coroutine context:

    
    
  2. Parent Teardown src/TestCase.php85: Calls parent::tearDown() which:

    • Destroys database migrations via destroyDatabaseMigrations() (if using HandlesDatabases)
    • Cleans up application resources
    • Resets any test doubles or mocks
  3. Queue Reset src/TestCase.php87: Resets the Queue payload callback:

    
    

This ensures that any custom queue payload creators registered during a test do not affect subsequent tests.

Sources: src/TestCase.php80-88


Class-Level Lifecycle Hooks

In addition to per-test hooks, PHPUnit provides class-level hooks that execute once per test class.

setUpBeforeClass Hook

The setUpBeforeClass() static method src/TestCase.php99-103 executes once before any test methods in the class run:


This hook:

  • Calls the parent class implementation
  • Invokes setUpBeforeClassUsingTestCase() from the InteractsWithTestCase trait
  • Processes BeforeClass attributes if defined on the test class

tearDownAfterClass Hook

The tearDownAfterClass() static method src/TestCase.php105-109 executes once after all test methods in the class complete:


This hook:

  • Invokes tearDownAfterClassUsingTestCase() from the InteractsWithTestCase trait
  • Processes AfterClass attributes if defined on the test class
  • Calls the parent class implementation

Note: Class-level hooks execute outside coroutine context and should not perform async operations.

Sources: src/TestCase.php99-109


Extension Points and Hooks

The testbench provides multiple extension points for customizing test behavior. These methods can be overridden in test classes to inject custom logic at specific lifecycle points.

Extension Points Map


Sources: src/TestCase.php38-110 src/Concerns/CreatesApplication.php src/Concerns/HandlesRoutes.php src/Concerns/HandlesDatabases.php

Override Methods Reference

MethodPhasePurposeDefault Behavior
defineEnvironment(ApplicationContract $app)SetupConfigure application before provider bootRegisters package providers and aliases
getPackageProviders(ApplicationContract $app)SetupDefine service providers to registerReturns empty array
getPackageAliases(ApplicationContract $app)SetupDefine class aliases to registerReturns empty array
defineRoutes(Router $router)SetupDefine test routes without middlewareEmpty implementation
defineWebRoutes(Router $router)SetupDefine routes with web middleware groupEmpty implementation
defineDatabaseMigrations()SetupRun database migrationsEmpty implementation
destroyDatabaseMigrations()TeardownRoll back database migrationsEmpty implementation
createApplication()SetupInstantiate application containerCreates Hypervel\Foundation\Application
reloadApplication()ManualRecreate application during testCalls tearDown() then setUp()

For detailed information on each extension point, see:

Sources: src/TestCase.php63-97


Coroutine Context Management

The testbench automatically wraps setup and teardown logic in Hyperf coroutines to ensure compatibility with async operations.

Coroutine-Wrapped Operations

Two lifecycle operations execute explicitly inside coroutine context:

  1. Setup Attributes src/TestCase.php57:

    
    

    This executes BeforeEach attributes inside a coroutine, matching the context where test methods run.

  2. Teardown Attributes src/TestCase.php83:

    
    

    This executes AfterEach attributes inside a coroutine, ensuring proper cleanup of async resources.

Why Coroutine Wrapping Matters

Hyperf's architecture requires that certain operations (database queries, HTTP requests, cache operations) execute within a coroutine context. The testbench ensures this by:

  • Wrapping attribute execution in coroutines
  • Running test methods in coroutines (handled by parent BaseTestCase)
  • Providing runInCoroutine() helper for explicit async operations

For a deep dive into coroutine integration, including coordinator management and Swoole timer handling, see Coroutine Context and Hyperf Integration.

Sources: src/TestCase.php57 src/TestCase.php83


Application Reloading

The reloadApplication() method src/TestCase.php93-97 allows tests to recreate the application instance mid-test:


This pattern is useful when:

  • Testing configuration changes that require application restart
  • Simulating multi-request scenarios
  • Resetting the application state without ending the test

Note: Reloading the application executes the full teardown and setup cycle, including database migration rollback and re-execution.

For more information on environment management and reloading patterns, see Reloading and Environment Management.

Sources: src/TestCase.php93-97


Summary

The testbench lifecycle implements a sophisticated initialization strategy:

  • One-time bootstrapping loads configuration and defines constants on the first test execution
  • Per-test setup creates a fresh application instance for each test method
  • Coroutine integration ensures async operations work correctly in tests
  • Extension points allow customization of providers, routes, and database state
  • Class-level hooks provide once-per-class setup and teardown capabilities

This architecture balances performance optimization (one-time bootstrap) with test isolation (per-test application creation), while maintaining compatibility with Hyperf's async runtime environment.

Sources: src/TestCase.php36-110 src/Bootstrapper.php21-40