PHPUnit中的Mock Composer实际上正在运行

时间:2015-10-06 16:33:37

标签: php mocking phpunit composer-php

我正在构建一个需要直接访问Composer的PHP应用程序。但是,为了测试应用程序,我实际上并不想运行作曲家,所以我试图模仿它。

<?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未加载var data = []; for ( var x=0; x< 1000 ; x+=3*r) { for ( var y=0; y< 1000 ; y+=3*r) { data.push([x, y]); } } svg.selectAll("circle").data(data).enter() .append("circle") .attr("cx", function(d) { return d[0]; }) .attr("cy", function(d) { return d[1]; }) .attr("r", r) .attr("opacity", 0) .transition() .duration(100) .delay(function(d, i) { return i * 100; }) .attr("opacity", 1); ,尽管该对象之前已正确实例化。

2 个答案:

答案 0 :(得分:1)

我有3个解决方案:

1。自己添加事件调度程序

添加&#34; symfony / event-dispatcher&#34;到你自己的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。使用预言

使用预言与PHPUnit一起模拟控制台。

"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感到不安。

3。使用Mockery

模拟系统的另一个选择。

"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中使用可变参数的错误记录使我很伤心。

4。 (奖励)修复Symfony控制台

似乎不太可能,但如果有人可以解决如何修复#8200,那么这意味着没有人必须使用多个模拟框架(如果你已经使用过PHPUnit)或添加脏黑客他们的需求 - 开发一次破坏测试。

答案 1 :(得分:0)

我还不能发表评论,抱歉。 :/

TL; DR:getMock变体适合我。检查你的环境。

通过composer获取作曲家(在composer.json中需要&#34;作曲家/作曲家&#34;:&#34; @ dev&#34;)让测试(使用getMock)为我传递。

检查您正在使用的symfony版本(我的是2.7.4)以及您是否正在使用作曲家的phar版本(您可能不应该这样做)。