PHPUnit 中的 Mock Composer 实际上是 运行
Mock Composer in PHPUnit is actually running
我正在构建一个需要直接访问 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
,尽管该对象之前已正确实例化。
抱歉,我还不能发表评论。 :/
TL;DR: getMock 变体对我有用。检查您的环境。
通过 composer 获取 composer(在 composer.json require "composer/composer": "@dev")使我通过了测试(使用 getMock)。
检查您使用的是哪个版本的 symfony(我的是 2.7.4)以及您是否使用了 composer 的 phar 版本(您可能不应该使用)。
我有 3 个解决方案:
1。自己添加事件调度程序
添加"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']))
);
}
}
个人说明:这感觉像是一个肮脏的 hack,但这是迄今为止最简单的解决方案
2。使用预言
将 prophecy 与 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
中可变参数的错误记录使用让我好难过
4。 (奖励)修复 Symfony 控制台
似乎不太可能,但如果有人能想出如何修复 #8200,那将意味着没有人必须使用多个模拟框架(如果你已经在使用 PHPUnit)或添加肮脏的 hacks他们的 require-dev 用于单个失败的测试。
我正在构建一个需要直接访问 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
,尽管该对象之前已正确实例化。
抱歉,我还不能发表评论。 :/
TL;DR: getMock 变体对我有用。检查您的环境。
通过 composer 获取 composer(在 composer.json require "composer/composer": "@dev")使我通过了测试(使用 getMock)。
检查您使用的是哪个版本的 symfony(我的是 2.7.4)以及您是否使用了 composer 的 phar 版本(您可能不应该使用)。
我有 3 个解决方案:
1。自己添加事件调度程序
添加"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']))
);
}
}
个人说明:这感觉像是一个肮脏的 hack,但这是迄今为止最简单的解决方案
2。使用预言
将 prophecy 与 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
中可变参数的错误记录使用让我好难过
4。 (奖励)修复 Symfony 控制台
似乎不太可能,但如果有人能想出如何修复 #8200,那将意味着没有人必须使用多个模拟框架(如果你已经在使用 PHPUnit)或添加肮脏的 hacks他们的 require-dev 用于单个失败的测试。