VOOZH about

URL: https://deepwiki.com/hypervel/testbench/2.3-coroutine-context-and-hyperf-integration

⇱ Coroutine Context and Hyperf Integration | hypervel/testbench | DeepWiki


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

Coroutine Context and Hyperf Integration

Purpose and Scope

This document explains how TestCase manages Hyperf's coroutine-based architecture to enable testing of asynchronous code in a synchronous PHPUnit environment. It covers the runInCoroutine wrapper mechanism, Swoole timer management, coordinator lifecycle integration, and ApplicationContext setup.

For information about the overall test lifecycle, see Test Lifecycle and Hooks. For application container setup, see Application Creation and Container Setup.


The Coroutine Context Challenge

Hyperf is built on Swoole's coroutine runtime, which enables high-performance asynchronous operations. However, PHPUnit executes tests in a traditional synchronous PHP environment. This creates a fundamental incompatibility: Hyperf services expect to run within an active coroutine context, but PHPUnit test methods execute outside of any coroutine.

The TestCase class bridges this gap by strategically wrapping key test lifecycle methods in coroutine contexts, ensuring that Hyperf services function correctly during tests while maintaining PHPUnit's standard test execution flow.

Key Challenges Addressed:

ChallengeSolution
PHPUnit runs synchronouslyWrap setup/teardown in runInCoroutine()
Hyperf services require coroutine contextExecute test environment setup within coroutines
Swoole timers persist between testsClear all timers in setUp()
Worker lifecycle needs managementResume coordinator in setUp()
Services need container accessSet ApplicationContext during creation

Sources: src/TestCase.php1-111


Test Lifecycle Coroutine Integration

The following diagram shows how coroutine context is applied throughout the test lifecycle:

Diagram: Coroutine Context Flow in Test Execution


Key Implementation Points:

  1. Setup Coroutine Wrapping src/TestCase.php57: The setUpTheTestEnvironmentUsingTestCase() method executes within a coroutine context, ensuring that trait setup methods and BeforeEach attributes have access to Hyperf services.

  2. Teardown Coroutine Wrapping src/TestCase.php83: Similarly, tearDownTheTestEnvironmentUsingTestCase() runs in a coroutine to properly clean up resources that require coroutine context.

  3. Parent Chain Execution: The parent::setUp() and parent::tearDown() calls execute outside the coroutine wrapper, maintaining compatibility with PHPUnit's lifecycle expectations.

Sources: src/TestCase.php38-58 src/TestCase.php80-88


ApplicationContext Integration

The ApplicationContext is Hyperf's global container accessor, required by many Hyperf components to retrieve service instances. The TestCase establishes this context during application creation:

Diagram: ApplicationContext Setup Flow


Implementation Details:

The createApplication() method src/TestCase.php69-78 performs the following operations:

  1. Application Instantiation: Creates a new Hypervel\Foundation\Application instance
  2. Contract Binding: Binds KernelContract to ConsoleKernel and ExceptionHandlerContract to the workbench exception handler
  3. Global Context Setup src/TestCase.php75: Calls ApplicationContext::setContainer($app) to make the container globally accessible
  4. Return: Returns the configured application instance

This context setup is critical because Hyperf's static service accessors (like Redis::get() or DB::table()) rely on ApplicationContext to retrieve the container and resolve dependencies.

Sources: src/TestCase.php69-78


Swoole Timer Management

Swoole's timer system allows scheduling of asynchronous tasks, but timers persist across test runs if not explicitly cleared. This can cause tests to interfere with each other through lingering timer callbacks.

Timer Clearing Strategy

The TestCase clears all active Swoole timers during the afterApplicationCreated callback src/TestCase.php46:

Timer::clearAll()

Why This Matters:

ScenarioWithout Timer ClearingWith Timer Clearing
Test A schedules a timerTimer persists into Test BTimer is cleared before Test B
Test B expects clean stateUnexpected timer fires in Test BTest B starts with no active timers
Timer references Test A resourcesPotential crashes or errorsClean slate for each test

The timer clearing occurs after the application is created but before routes are set up, ensuring a clean timer state for each test execution.

Sources: src/TestCase.php45-51


Coordinator Integration

Hyperf uses coordinators to manage worker process lifecycle events. The CoordinatorManager provides synchronization points for startup, shutdown, and other lifecycle phases.

Coordinator Resume in setUp

The TestCase resumes the WORKER_EXIT coordinator during setup src/TestCase.php47:

CoordinatorManager::until(Constants::WORKER_EXIT)->resume()

Diagram: Coordinator Lifecycle Management


Purpose of Coordinator Resume:

The Constants::WORKER_EXIT coordinator is used by Hyperf components to wait for a signal that the worker process is shutting down. By resuming this coordinator in setUp(), the TestCase ensures that:

  1. Components waiting on WORKER_EXIT do not block test execution
  2. Async cleanup operations can proceed normally
  3. Each test starts with coordinators in a known state

This is particularly important for components like connection pools, which may wait on coordinators before releasing resources.

Sources: src/TestCase.php8-9 src/TestCase.php47


Complete Integration Flow

The following diagram shows how all integration components work together:

Diagram: Complete Coroutine Integration Architecture


Flow Summary:

  1. Bootstrap Phase: First test triggers Bootstrapper::bootstrap() src/TestCase.php40-43
  2. Callback Registration: afterApplicationCreated callback is registered src/TestCase.php45-51
  3. Application Creation: parent::setUp() triggers createApplication() which sets ApplicationContext src/TestCase.php75
  4. Cleanup Operations: Callback executes Timer::clearAll() and coordinator resume src/TestCase.php46-47
  5. Coroutine Setup: runInCoroutine() wraps environment setup src/TestCase.php57
  6. Test Execution: Test method runs with full Hyperf service access
  7. Coroutine Teardown: runInCoroutine() wraps environment teardown src/TestCase.php83
  8. Queue Reset: Payload callbacks are cleared src/TestCase.php87

Sources: src/TestCase.php38-88


Practical Implications for Test Authors

When writing tests that extend TestCase, developers benefit from automatic coroutine integration without needing to explicitly manage coroutine contexts:

What Works Automatically

OperationCoroutine ContextExample
Database queries in setup✓ AutomaticUser::create() in trait setUp()
Redis operations in setup✓ AutomaticRedis::set() in BeforeEach attribute
Cache access in teardown✓ AutomaticCache::flush() in trait tearDown()
Queue job dispatching✓ AutomaticJob::dispatch() in test method

What Requires Manual Wrapping

If custom test methods need to execute Hyperf operations that are not already within a test method (which runs in the main execution context), they should be aware that:

  1. Test Methods: Run outside coroutine context by default. If async operations are needed, use Hyperf's async utilities directly.
  2. Custom Setup/Teardown Traits: If creating custom traits with setUp or tearDown methods, they will execute within the coroutine context established by TestCase.
  3. Static Methods: Class-level setup (setUpBeforeClass) runs outside coroutine context and should avoid Hyperf service calls.

Example: Trait Setup in Coroutine Context


The DB::table() call works because TestCase::setUp() wraps trait setup execution in runInCoroutine() src/TestCase.php57

Sources: src/TestCase.php38-58 src/TestCase.php80-88


Summary of Integration Components

ComponentLocationPurpose
runInCoroutine()src/TestCase.php57-83Wraps setup/teardown in coroutine context
ApplicationContext::setContainer()src/TestCase.php75Makes container globally accessible to Hyperf
Timer::clearAll()src/TestCase.php46Clears Swoole timers between tests
CoordinatorManager::until()->resume()src/TestCase.php47Manages worker lifecycle coordination
afterApplicationCreated callbacksrc/TestCase.php45-51Executes cleanup after app initialization
Queue::createPayloadUsing(null)src/TestCase.php87Resets queue payload customization

The integration architecture ensures that tests written for Hypervel/Hyperf applications work seamlessly with Hyperf's coroutine-based services while maintaining compatibility with PHPUnit's synchronous test execution model.

Sources: src/TestCase.php1-111