ZF2 - Module.php

时间:2016-01-15 12:41:44

标签: php testing zend-framework2 phpunit

我正在尝试测试我的ZF2应用程序的控制器。假设此控制器在我的A模块中。

在模块onBootstrap Module.php的{​​{1}}方法中,我使用服务管理器检索另一个模块的服务,比如A,我是没有加载。

如何在服务管理器中设置所请求服务的模拟?请注意,我在测试中无法使用B来执行此操作,因为这已经调用了$this->getApplicationServiceLocator()模块的Module.onBootstrap方法。

要发布一些代码,这就是我现在正在做的事情

bootstrap.php中

A

我的实际考试类

namespace Application;

use Zend\Mvc\Service\ServiceManagerConfig;
use Zend\ServiceManager\ServiceManager;
use RuntimeException;

class Bootstrap
{
    protected static $serviceManager;

    public static function init()
    {
        $modulePath = static::findParentPath('module');
        $vendorPath = static::findParentPath('vendor');

        if (is_readable($vendorPath . '/autoload.php')) {
            $loader = include $vendorPath . '/autoload.php';
        } else {
            throw new RuntimeException('Cannot locate autoload.php');
        }

        $config = [
            'modules' => [
                'Application',
            ],
            'module_listener_options' => [
                'module_paths' => [
                    $modulePath,
                    $vendorPath
                ]
            ]
        ];

        $serviceManager = new ServiceManager(new ServiceManagerConfig());
        $serviceManager->setService('ApplicationConfig', $config);
        $serviceManager->get('ModuleManager')->loadModules();
        static::$serviceManager = $serviceManager;
    }

    protected static function findParentPath($path)
    {
        $dir = __DIR__;
        $previousDir = '.';
        while (!is_dir($dir . '/' . $path)) {
            $dir = dirname($dir);
            if ($previousDir === $dir) {
                return false;
            }
            $previousDir = $dir;
        }
        return $dir . '/' . $path;
    }

    public static function getServiceManager()
    {
        return static::$serviceManager;
    }
}

Bootstrap::init();

Module.php简化

namespace Application\Functional;

use Application\Bootstrap;

use Zend\Test\PHPUnit\Controller\AbstractHttpControllerTestCase;

class ValidateCustomerRegistrationTest extends AbstractHttpControllerTestCase
{
    public function setUp()
    {
        $serviceManager = Bootstrap::getServiceManager();
        $applicationConfig = $serviceManager->get('ApplicationConfig');

        $this->setApplicationConfig($applicationConfig);
        parent::setUp();
    }

    public function testRegisterValidUserWithOnlyEquomobiliData()
    {
        $this->getApplicationServiceLocator();
    }
}

3 个答案:

答案 0 :(得分:1)

这里没有足够的数据直接帮助您。为初学者提供函数ValidateCustomerRegistrationTest->getApplicationServiceLocator()会更有用。

我希望我可以间接帮助你。

重构可能会有所帮助

当我在编写单元测试时,我会先从一些个人规则开始。

仅测试正在测试的代码。嘲笑其他一切。不需要测试应该已经拥有它自己的测试的东西。

功能如何运作不应该很重要。只有输入/输出。即使功能核心发生剧烈变化,这也可以使您的测试保持可行。

/**
 * @param MvcEventInterface $event
 * @param Service\From\Other\ModuleInterface $service
 *
 * @return boolean
 */
public function onBootstrap(MvcEventInterface $event, Service\From\Other\ModuleInterface $service)
{
  return true;
}

然后在测试课程中:

public function testOnBootstrap(){
   $eventMock = $this->getMock(MvcEventInterface::class);
   $serviceMock = $this->getMock(ModuleInterface::class);
   $module = new Module();

   $result = $module->onBootstrap($eventMock, $serviceMock);
   $this->assertTrue($result);
}

*我显然不知道你要测试的是什么

当重构不是一个选项时

有两种模拟类型可以提供帮助,这对我来说就是蝙蝠,模拟和mockBuilder。看看PHPUnit documentation for Test Doubles

$serviceMock = $this->getMockBuilder(ServiceManager::class)
    ->disableOriginalConstructor()
    ->setMethods([
        '__construct',
        'get'
    ])
    ->getMock();

$serviceMock
    ->expects($this->any())
    ->method('get')
    ->will($this->returnValue(
        $this->getMock(ModuleManagerInterface::class)
    ));
祝你好运,我希望你能告诉我们我们是否可以更具体地帮助你。我还建议研究面向对象编程的SOLID原理。许多编程原则之一,应该使您的代码清洁,易于扩展,并且易于测试。

答案 1 :(得分:1)

实现您尝试执行的操作的一种方法可能是在将请求分派给测试控制器之前强制覆盖服务管理器中的服务。 Here's an example如何做到这一点(注意:因为您正在做的是在模块的引导过程中,该示例可能无法100%转换为您的情况。)

像其他提到的人一样:你可能仍然需要仔细检查是否可以重构一个更清洁的方法,但这是另一个故事。

答案 2 :(得分:1)

为了模拟服务管理器和使用它进行的调用,您可以使用mockery https://github.com/mockery/mockery。这个库是框架无关的,所以即使你使用PHPUnit或其他工具它也应该有效。

使用它的坏方法但它应该快速解决你的问题是使用mockery('overload:myclass')重载类,其中myclass是服务管理器的实例。

使用该库的最佳方法是使用依赖注入模式在应用程序升级测试时注入服务定位器的模拟。

一种简单的方法是为控制器创建一个工厂,将服务定位器注入其中。然后在测试中,使用服务定位器的模拟作为参数来初始化控制器。