shouldReceive('getIdentifier')->andReturn('test-module'); $module->shouldReceive('upgrades')->andReturn([ '1.1.0' => function () { /* no-op */ }, '1.2.0' => function () { /* no-op */ }, ]); $capturedVersions = []; $onStep = function (string $version) use (&$capturedVersions): void { $capturedVersions[] = $version; }; $reflection = new \ReflectionClass($moduleManager); $method = $reflection->getMethod('runUpgradeSteps'); $method->setAccessible(true); // $fromVersion=1.0.0, $toVersion=1.2.0 → 1.1.0, 1.2.0 실행 $method->invoke($moduleManager, $module, '1.0.0', '1.2.0', false, $onStep); $this->assertEquals(['1.1.0', '1.2.0'], $capturedVersions, '각 step 버전이 콜백에 전달되어야 함'); } /** * onStep 이 null 이어도 정상 동작 (하위 호환). */ public function test_run_upgrade_steps_tolerates_null_on_step(): void { $moduleManager = app(ModuleManager::class); $module = Mockery::mock(\App\Contracts\Extension\ModuleInterface::class); $module->shouldReceive('getIdentifier')->andReturn('test-module'); $module->shouldReceive('upgrades')->andReturn([ '1.1.0' => function () { /* no-op */ }, ]); $reflection = new \ReflectionClass($moduleManager); $method = $reflection->getMethod('runUpgradeSteps'); $method->setAccessible(true); $this->expectNotToPerformAssertions(); $method->invoke($moduleManager, $module, '1.0.0', '1.1.0', false, null); } }