You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

错误原因分析

  1. 配置合并逻辑错误:ArrayUtils::merge的参数顺序用反了,你把多个配置组成的数组作为第一个参数传入,导致合并后的配置结构异常,modules字段可能变成非数组类型。
  2. 配置文件返回值异常:某个配置文件(application.config.global.php或testing.global.php)没有正确返回数组,比如语法错误、输出了额外内容,导致$configs混入非数组值,最终合并后modules字段不符合要求。
  3. 测试配置覆盖错误: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 07:20:57