Laminas-test单元测试报错:ModuleManager服务创建失败
Laminas单元测试报错:ModuleManager无法创建的问题
错误信息
执行vendor/bin/phpunit时出现以下错误:
1) CompanyTest\Controller\ManageControllerTest::testDirectoryAction Laminas\ServiceManager\Exception\ServiceNotCreatedException: Service with name "ModuleManager" could not be created. Reason: Parameter to Laminas\ModuleManager\ModuleManager's Laminas\ModuleManager\ModuleManager::setModules method must be an array or implement the Traversable interface /var/lxp/sites/cammslearning/production/application/vendor/laminas/laminas-servicemanager/src/ServiceManager.php:652 /var/lxp/sites/cammslearning/production/application/vendor/laminas/laminas-servicemanager/src/ServiceManager.php:218 /var/lxp/sites/cammslearning/production/application/vendor/laminas/laminas-mvc/src/Application.php:263 /var/lxp/sites/cammslearning/production/application/vendor/laminas/laminas-test/src/PHPUnit/Controller/AbstractControllerTestCase.php:230 /var/lxp/sites/cammslearning/production/application/vendor/laminas/laminas-test/src/PHPUnit/Controller/AbstractControllerTestCase.php:255 /var/lxp/sites/cammslearning/production/application/vendor/laminas/laminas-test/src/PHPUnit/Controller/AbstractControllerTestCase.php:344 /var/lxp/sites/cammslearning/production/application/module/Company/test/Controller/ManageControllerTest.php:29 Caused by Laminas\ModuleManager\Exception\InvalidArgumentException: Parameter to Laminas\ModuleManager\ModuleManager's Laminas\ModuleManager\ModuleManager::setModules method must be an array or implement the Traversable interface /var/lxp/sites/cammslearning/production/application/vendor/laminas/laminas-modulemanager/src/ModuleManager.php:262 /var/lxp/sites/cammslearning/production/application/vendor/laminas/laminas-modulemanager/src/ModuleManager.php:68 /var/lxp/sites/cammslearning/production/application/vendor/laminas/laminas-mvc/src/Service/ModuleManagerFactory.php:82 /var/lxp/sites/cammslearning/production/application/vendor/laminas/laminas-servicemanager/src/ServiceManager.php:645 /var/lxp/sites/cammslearning/production/application/vendor/laminas/laminas-servicemanager/src/ServiceManager.php:218 /var/lxp/sites/cammslearning/production/application/vendor/laminas/laminas-mvc/src/Application.php:263 /var/lxp/sites/cammslearning/production/application/vendor/laminas/laminas-test/src/PHPUnit/Controller/AbstractControllerTestCase.php:230 /var/lxp/sites/cammslearning/production/application/vendor/laminas/laminas-test/src/PHPUnit/Controller/AbstractControllerTestCase.php:255 /var/lxp/sites/cammslearning/production/application/vendor/laminas/laminas-test/src/PHPUnit/Controller/AbstractControllerTestCase.php:344 /var/lxp/sites/cammslearning/production/application/module/Company/test/Controller/ManageControllerTest.php:29
测试代码
<?php namespace CompanyTest\Controller; use Company\Controller\ManageController; use Laminas\Stdlib\ArrayUtils; use Laminas\Test\PHPUnit\Controller\AbstractHttpControllerTestCase; class ManageControllerTest extends AbstractHttpControllerTestCase { protected $traceError = true; protected function setUp(): void { $configOverrides = []; $configs = [ include __DIR__ . '/../../../../config/application.config.global.php', include __DIR__ . '/../../../../config/autoload/testing.global.php', ]; $mergedConfig = ArrayUtils::merge( $configs, $configOverrides ); $this->setApplicationConfig($mergedConfig); parent::setUp(); } public function testDirectoryAction () { $this->dispatch('/company/directory'); $this->assertResponseStatusCode(200); $this->assertModuleName('Company'); $this->assertControllerName(ManageController::class); $this->assertControllerClass('ManageController'); $this->assertMatchedRouteName('company/directory'); } }
相关配置
composer.json的autoload-dev配置
"autoload-dev": { "psr-4": { "ApplicationTest\\": "module/Application/Test/", "CompanyTest\\": "module/Company/test/" } },
phpunit.xml
<?xml version="1.0" encoding="UTF-8"?> <phpunit colors="true" bootstrap="./vendor/autoload.php" > <testsuites> <testsuite name="Laminas MVC Application Test Suite"> <directory>./module/Application/Test</directory> </testsuite> <testsuite name="Login"> <directory>./module/Learner/Authentication/test</directory> </testsuite> <testsuite name="Company"> <directory>./module/Company/test</directory> </testsuite> </testsuites> </phpunit>
目录结构
. └── application/ ├── config/ │ ├── application.config.global.php │ └── autoload/ │ └── testing.global.php ├── module/ │ └── Company/ │ ├── Controller/ │ │ └── ManageController.php │ └── test/ │ └── Controller/ │ └── ManageControllerTest.php ├── composer.json └── phpunit.xml
错误原因分析
- 配置合并逻辑错误:
ArrayUtils::merge的参数顺序用反了,你把多个配置组成的数组作为第一个参数传入,导致合并后的配置结构异常,modules字段可能变成非数组类型。 - 配置文件返回值异常:某个配置文件(
application.config.global.php或testing.global.php)没有正确返回数组,比如语法错误、输出了额外内容,导致$configs混入非数组值,最终合并后modules字段不符合要求。 - 测试配置覆盖错误:
testing.global.php可能错误地将modules配置覆盖为非数组类型,或者没有正确继承基础配置的模块列表。
解决方法
1. 修正配置合并逻辑
调整ArrayUtils::merge的使用方式,确保正确合并多个配置文件:
protected function setUp(): void { $configOverrides = []; // 先合并基础配置,再叠加测试配置,最后应用自定义覆盖 $mergedConfig = include __DIR__ . '/../../../../config/application.config.global.php'; $mergedConfig = ArrayUtils::merge($mergedConfig, include __DIR__ . '/../../../../config/autoload/testing.global.php'); $mergedConfig = ArrayUtils::merge($mergedConfig, $configOverrides); $this->setApplicationConfig($mergedConfig); parent::setUp(); }
如果你的Laminas版本支持,也可以用可变参数形式一次性合并:
$mergedConfig = ArrayUtils::merge( include __DIR__ . '/../../../../config/application.config.global.php', include __DIR__ . '/../../../../config/autoload/testing.global.php', $configOverrides );
2. 检查配置文件的返回值
确保所有配置文件的最后一行都是return $config;,且$config数组中包含'modules'键,值为数组类型:
// application.config.global.php示例 return [ 'modules' => [ 'Laminas\Mvc\Plugin\FlashMessenger', 'Company', // 其他模块... ], // 其他配置项 ];
如果testing.global.php需要修改模块列表,要用数组形式叠加,不要替换成非数组值:
// testing.global.php示例 $baseConfig = include __DIR__ . '/../application.config.global.php'; return [ 'modules' => array_merge($baseConfig['modules'], [ // 测试环境额外模块 ]), // 其他测试配置 ];
3. 验证合并后的配置
在setApplicationConfig之前,打印合并后的配置,检查modules字段是否为数组:
var_dump($mergedConfig['modules']); exit;
如果输出不是数组,定位对应的配置文件修正即可。
4. 刷新自动加载规则
执行composer dump-autoload,确保新增的autoload-dev配置生效,避免类加载异常。
内容的提问来源于stack exchange,提问作者Tharisha Perera
相关产品推荐
相关产品推荐

