php模拟不同手机型号访问,php-PHPUnit中的模拟-带有不同参数的同一方法的多个配置...

php-PHPUnit中的模拟-带有不同参数的同一方法的多个配置

是否可以通过这种方式配置PHPUnit模拟?

$context = $this->getMockBuilder('Context')

->getMock();

$context->expects($this->any())

->method('offsetGet')

->with('Matcher')

->will($this->returnValue(new Matcher()));

$context->expects($this->any())

->method('offsetGet')

->with('Logger')

->will($this->returnValue(new Logger()));

我使用PHPUnit 3.5.10,当我要求Matcher时,它会失败,因为它需要“ Logger”参数。就像第二个期望重写了第一个期望一样,但是当我转储模拟时,一切看起来都还不错。

7个解决方案

61 votes

遗憾的是,默认的PHPUnit Mock API无法做到这一点。

我可以看到两个选择,可以使您接近以下情况:

使用-> at($ x)

$context = $this->getMockBuilder('Context')

->getMock();

$context->expects($this->at(0))

->method('offsetGet')

->with('Matcher')

->will($this->returnValue(new Matcher()));

$context->expects($this->at(1))

->method('offsetGet')

->with('Logger')

->will($this->returnValue(new Logger()));

这可以正常工作,但是您要进行的测试超出了您应有的水平(主要是首先使用匹配器调用它,这是实现的详细信息)。

如果您对每个功能的调用不止一个,这也将失败!

接受两个参数并使用returnCallBack

这是更多的工作,但是效果更好,因为您不依赖于调用的顺序:

工作示例:

class FooTest extends PHPUnit_Framework_TestCase {

public function testX() {

$context = $this->getMockBuilder('Context')

->getMock();

$context->expects($this->exactly(2))

->method('offsetGet')

->with($this->logicalOr(

$this->equalTo('Matcher'),

$this->equalTo('Logger')

))

->will($this->returnCallback(

function($param) {

var_dump(func_get_args());

// The first arg will be Matcher or Logger

// so something like "return new $param" should work here

}

));

$context->offsetGet("Matcher");

$context->offsetGet("Logger");

}

}

class Context {

public function offsetGet() { echo "org"; }

}

这将输出:

/*

$ phpunit footest.php

PHPUnit 3.5.11 by Sebastian Bergmann.

array(1) {

[0]=>

string(7) "Matcher"

}

array(1) {

[0]=>

string(6) "Logger"

}

.

Time: 0 seconds, Memory: 3.00Mb

OK (1 test, 1 assertion)

我在匹配器中使用了$this->exactly(2),以证明这在计算调用次数时也有效。 如果您不需要,将其换成$this->any()当然可以。

edorian answered 2020-02-07T10:34:17Z

30 votes

从PHPUnit 3.6开始,有$this->returnValueMap()可用于根据方法存根的给定参数返回不同的值。

leeb answered 2020-02-07T10:33:17Z

7 votes

您可以通过回调来实现:

class MockTest extends PHPUnit_Framework_TestCase

{

/**

* @dataProvider provideExpectedInstance

*/

public function testMockReturnsInstance($expectedInstance)

{

$context = $this->getMock('Context');

$context->expects($this->any())

->method('offsetGet')

// Accept any of "Matcher" or "Logger" for first argument

->with($this->logicalOr(

$this->equalTo('Matcher'),

$this->equalTo('Logger')

))

// Return what was passed to offsetGet as a new instance

->will($this->returnCallback(

function($arg1) {

return new $arg1;

}

));

$this->assertInstanceOf(

$expectedInstance,

$context->offsetGet($expectedInstance)

);

}

public function provideExpectedInstance()

{

return array_chunk(array('Matcher', 'Logger'), 1);

}

}

应该传递给传递给Context Mock的273693388456172020320方法的任何“ Logger”或“ Matcher”参数:

F:\Work\code\gordon\sandbox>phpunit NewFileTest.php

PHPUnit 3.5.13 by Sebastian Bergmann.

..

Time: 0 seconds, Memory: 3.25Mb

OK (2 tests, 4 assertions)

如您所见,PHPUnit运行了两个测试。 每个dataProvider值一个。 在每个测试中,它为with()声明了一个,并为273693388456172020321声明了一个,因此有四个声明。

Gordon answered 2020-02-07T10:34:45Z

5 votes

接下来是@edorian的回答和有关确保使用Matcher和Logger调用该方法的评论(@MarijnHuizendveld),而不是简单地使用Matcher或Logger调用两次,这里是一个示例。

$expectedArguments = array('Matcher', 'Logger');

$context->expects($this->exactly(2))

->method('offsetGet')

->with($this->logicalOr(

$this->equalTo('Matcher'),

$this->equalTo('Logger')

))

->will($this->returnCallback(

function($param) use (&$expectedArguments){

if(($key = array_search($param, $expectedArguments)) !== false) {

// remove called argument from list

unset($expectedArguments[$key]);

}

// The first arg will be Matcher or Logger

// so something like "return new $param" should work here

}

));

// perform actions...

// check all arguments removed

$this->assertEquals(array(), $expectedArguments, 'Method offsetGet not called with all required arguments');

这是PHPUnit 3.7。

如果您要测试的方法实际上没有返回任何内容,而您只需要测试使用正确的参数调用它,则可以使用相同的方法。 对于这种情况,我还尝试使用$ this-> callback的回调函数作为with的参数来执行此操作,而不是在will中使用returnCallback。 失败,因为内部phpunit在验证参数匹配器回调的过程中两次调用了该回调。 这意味着该方法失败,因为在第二次调用时该参数已从预期参数数组中删除。 我不知道为什么phpunit会两次调用它(似乎是不必要的浪费),我想您可以通过仅在第二次调用时将其删除来解决此问题,但是我对这是有目的的和一致的phpunit行为没有足够的信心 依靠这种发生。

crysallus answered 2020-02-07T10:35:16Z

3 votes

我对该主题的2美分:使用at($ x)时要注意:这意味着预期的方法调用将是模拟对象上的第($ x + 1)个方法调用; 这并不意味着将是预期方法的第($ x + 1)次调用。 这让我浪费了一些时间,所以我希望它不会与您在一起。 对大家致以诚挚的问候。

Alessandro Ronchi answered 2020-02-07T10:35:36Z

2 votes

我只是偶然发现了这个PHP扩展来模拟对象:[https://github.com/etsy/phpunit-extensions/wiki/Mock-Object]

powtac answered 2020-02-07T10:35:56Z

0 votes

这也是doublit库的一些解决方案:

解决方案1:使用Stubs::returnValueMap

/* Get a dummy double instance */

$double = Doublit::dummy_instance(Context::class);

/* Test the "offsetGet" method */

$double::_method('offsetGet')

// Test that the first argument is equal to "Matcher" or "Logger"

->args([Constraints::logicalOr('Matcher', 'Logger')])

// Return "new Matcher()" when first argument is "Matcher"

// Return "new Logger()" when first argument is "Logger"

->stub(Stubs::returnValueMap([['Matcher'], ['Logger']], [new Matcher(), new Logger()]));

解决方案2:使用回调

/* Get a dummy double instance */

$double = Doublit::dummy_instance(Context::class);

/* Test the "offsetGet" method */

$double::_method('offsetGet')

// Test that the first argument is equal to "Matcher" or "Logger"

->args([Constraints::logicalOr('Matcher', 'Logger')])

// Return "new Matcher()" when first argument $arg is "Matcher"

// Return "new Logger()" when first argument $arg is "Logger"

->stub(function($arg){

if($arg == 'Matcher'){

return new Matcher();

} else if($arg == 'Logger'){

return new Logger();

}

});

gealex answered 2020-02-07T10:36:25Z

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值