【问题标题】:Mock Composer in PHPUnit is actually runningPHPUnit 中的 Mock Composer 实际上正在运行
【发布时间】:2016-01-03 16:10:59
【问题描述】:

我正在构建一个需要直接访问 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

我尝试了-&gt;getMock(完全失败)和-&gt;getMockBuilder 的各种组合,但它似乎总是实际使用-&gt;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,尽管该对象之前已正确实例化。

【问题讨论】:

  • 作曲家Application 类不是抽象类。那你为什么用getMockForAbstractClass()呢?
  • 好问题,我会更新上面的信息说明原因。 :)
  • 当我这样做时它对我有用: $composer = $this->getMock('\Composer\Console\Application');显示 Application::class 时会得到什么?
  • 当我回显Application::class 时,我确实得到了Composer\Console\Application。按照建议尝试 $composer = $this-&gt;getMock('\Composer\Console\Application'); 会导致与上述相同的错误。
  • @Bang 你用的是什么版本的包?

标签: php mocking phpunit composer-php


【解决方案1】:

我有 3 个解决方案:

1。自己添加事件调度器

将“symfony/event-dispatcher”添加到您自己的需求开发中

"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。使用嘲讽

模拟系统的另一种选择。

"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 中错误记录的可变参数使用让我非常难过。 p>

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

似乎不太可能,但如果有人能解决#8200 的问题,那就意味着没有人必须使用多个模拟框架(如果你已经在使用 PHPUnit)或在他们的 require-dev 中添加肮脏的技巧对于单个损坏的测试。

【讨论】:

  • 旁注:我选择了选项 1
【解决方案2】:

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

TL;DR:getMock 变体对我有用。检查您的环境。

通过 composer 获取 composer(在 composer.json 中需要 "composer/composer": "@dev")使测试(使用 getMock)为我通过。

检查您使用的是哪个版本的 symfony(我的是 2.7.4)以及您是否使用的是 phar 版本的 composer(您可能不应该这样做)。

【讨论】:

  • 我尝试过使用 dev-master 和 alpha10,但都不起作用。我没有直接使用 Symfony,尽管我使用的一些工具需要一些包。
  • 好吧,就像 Sven 说的,你应该尝试在你安装的依赖项中搜索这个接口。或者您可以将 symfony 添加为直接依赖项,看看它是否有任何不同。
猜你喜欢
  • 1970-01-01
  • 2015-04-04
  • 1970-01-01
  • 2021-09-11
  • 1970-01-01
  • 2016-04-07
  • 1970-01-01
  • 1970-01-01
  • 2020-04-10
相关资源
最近更新 更多