我正在构建一个需要直接访问composer的php应用程序。但是,为了测试应用程序,我实际上不想运行composer,所以我试图模拟它。

<?php

namespace MyNamespace;

use Composer\Console\Application;
use Symfony\Component\Console\Input\ArrayInput;

class MyTest extends \PHPUnit_Framework_TestCase {

    public function testComposer()
    {
        /** @var Application|\PHPUnit_Framework_MockObject_MockObject $composer */
        $composer = $this->getMockForAbstractClass(Application::class);
        $composer
            ->expects($this->any())
            ->method('run')
            ->will($this->returnValue(true));

        $this->assertTrue(
            $composer->run(new ArrayInput(['command' => 'install']))
        );
    }

}

这实际上运行代码:
$ bin/phpunit -c phpunit-fast.xml tests/MyTest.php
PHPUnit 4.8.10 by Sebastian Bergmann and contributors.

Runtime:        PHP 5.6.13-1+deb.sury.org~trusty+3 with Xdebug 2.3.2
Configuration:  phpunit-fast.xml

Loading composer repositories with package information
Installing dependencies (including require-dev) from lock file
Nothing to install or update
Generating autoload files

我尝试过各种->getMock(完全失败)和->getMockBuilder的组合,但它似乎总是实际使用->run方法,而不是存根。
我想它是在某种程度上取代了内部的这些方法,但如果是这样的话,我如何防止它呢?
更新
有人问我为什么要使用getMockForAbstractClass而不仅仅是getMock。当使用getMock时,我得到以下输出:
PHPUnit 4.8.10 by Sebastian Bergmann and contributors.

Runtime:        PHP 5.6.13-1+deb.sury.org~trusty+3 with Xdebug 2.3.2
Configuration:  phpunit.xml.dist

E

Time: 1.19 minutes, Memory: 4.50Mb

There was 1 error:

1) MyNamespace\MyTest::testComposer
PHPUnit_Framework_MockObject_RuntimeException: Cannot mock Symfony\Component\Console\Application::setDispatcher() because a class or interface used in the signature is not loaded

tests/MyTest.php:22

Caused by
ReflectionException: Class Symfony\Component\EventDispatcher\EventDispatcherInterface does not exist

tests/MyTest.php:22

FAILURES!
Tests: 1, Assertions: 0, Errors: 1.

尽管使用$composer = new Application();效果良好。事实上,如果我在测试上面加上那一行,它仍然声明一个class or interface没有加载,尽管对象在前面被正确地实例化了。

最佳答案

我有3个解决方案:
一。添加事件调度器
将“symfony/event dispatcher”添加到您自己的require dev

"require-dev" : {
    ...
    "symfony/event-dispatcher" : "^2.1"
}

通过校正试验:
<?php
/**
 * MyTest.php
 */

namespace MyNamespace;

use Composer\Console\Application;
use Symfony\Component\Console\Input\ArrayInput;

class MyTest extends \PHPUnit_Framework_TestCase {

    public function testComposer()
    {
        /** @var Application|\PHPUnit_Framework_MockObject_MockObject $composer */
        $composer = $this->getMock(Application::class);
        $composer
            ->expects($this->any())
            ->method('run')
            ->will($this->returnValue(true));

        $this->assertTrue(
            $composer->run(new ArrayInput(['command' => 'install']))
        );
    }
}

个人注意:这感觉像是一个肮脏的黑客,但迄今为止是最简单的解决方案
2.使用预言
与PHPUnt一起使用预言来模拟控制台。
"require-dev" : {
    ...
    "phpspec/prophecy": "~1.0"
}

现在测试如下:
<?php
/**
 * MyTest.php
 */

namespace MyNamespace;

use Composer\Console\Application;
use Prophecy\Prophet;
use Prophecy\Prophecy\ObjectProphecy;
use Symfony\Component\Console\Input\ArrayInput;

class MyTest extends \PHPUnit_Framework_TestCase {

    public function testComposer()
    {
        $prophet = new Prophet();
        $composerProphecy = $prophet->prophesize(Application::class);
        $composerProphecy
            ->run(new ArrayInput(['command' => 'install']))
            ->willReturn(true);

        /** @var Application $composer */
        $composer = $composerProphecy->reveal();

        $this->assertTrue(
            $composer->run(new ArrayInput(['command' => 'install']))
        );
    }
}

个人注:我不喜欢告诉预言什么方法将被称为使用魔术方法,因为这些扰乱了我的IDE。
三。使用嘲弄
模拟系统的另一个选项。
"require-dev" : {
    ...
    "mockery/mockery": "^0.9.4"
}

还有测试:
<?php
/**
 * MyTest.php
 */

namespace MyNamespace;

use Composer\Console\Application;
use Symfony\Component\Console\Input\ArrayInput;

class MyTest extends \PHPUnit_Framework_TestCase {

    public function testComposer()
    {
        /** @var Application|\Mockery\MockInterface $composer */
        $composer = \Mockery::mock(Application::class);
        $composer
            ->shouldReceive('run')
            ->with(ArrayInput::class)
            ->andReturn(true);


        $this->assertTrue(
            $composer->run(new ArrayInput(['command' => 'install']))
        );

        \Mockery::close();
    }
}

个人注意:静态使用,必须记住清理,以及错误记录使用的变量在shouldReceive使我非常难过。
四。(奖励)修复symfony控制台
似乎不太可能,但如果任何人都能解决如何修复#8200,那么这意味着没有人将不得不使用多个模拟框架(如果你已经使用PHPUnt无论如何)或添加脏黑客到他们需要的DEV为一个单独的测试。

关于php - PHPUnit中的Mock Composer实际上正在运行,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/32975108/

10-12 02:14