Dan*_*Dan 21
已经有几个现有的解决方案已经解决了这个问题,但我见过的所有解决方案都有类似的弱点:他们依赖->expects($this->at(n))
.PHPUnit中的'expected at'函数有一些奇怪的行为,因为计数器用于模拟的每个方法调用.这意味着如果您在直接foreach之外对迭代器进行方法调用,则必须调整迭代器模拟.
解决方法是创建一个包含基本迭代器数据(源数组和位置)的对象,并将其传递给returnCallback
闭包.因为它是通过引用传递的,所以对象在调用之间保持最新,所以我们可以模拟每个方法来模拟一个简单的迭代器.现在我们可以正常使用迭代器模拟,而不必担心严格的调用顺序.
下面的示例方法,您可以使用它来设置迭代器模拟:
/**
* Setup methods required to mock an iterator
*
* @param PHPUnit_Framework_MockObject_MockObject $iteratorMock The mock to attach the iterator methods to
* @param array $items The mock data we're going to use with the iterator
* @return PHPUnit_Framework_MockObject_MockObject The iterator mock
*/
public function mockIterator(PHPUnit_Framework_MockObject_MockObject $iteratorMock, array $items)
{
$iteratorData = new \stdClass();
$iteratorData->array = $items;
$iteratorData->position = 0;
$iteratorMock->expects($this->any())
->method('rewind')
->will(
$this->returnCallback(
function() use ($iteratorData) {
$iteratorData->position = 0;
}
)
);
$iteratorMock->expects($this->any())
->method('current')
->will(
$this->returnCallback(
function() use ($iteratorData) {
return $iteratorData->array[$iteratorData->position];
}
)
);
$iteratorMock->expects($this->any())
->method('key')
->will(
$this->returnCallback(
function() use ($iteratorData) {
return $iteratorData->position;
}
)
);
$iteratorMock->expects($this->any())
->method('next')
->will(
$this->returnCallback(
function() use ($iteratorData) {
$iteratorData->position++;
}
)
);
$iteratorMock->expects($this->any())
->method('valid')
->will(
$this->returnCallback(
function() use ($iteratorData) {
return isset($iteratorData->array[$iteratorData->position]);
}
)
);
$iteratorMock->expects($this->any())
->method('count')
->will(
$this->returnCallback(
function() use ($iteratorData) {
return sizeof($iteratorData->array);
}
)
);
return $iteratorMock;
}
Run Code Online (Sandbox Code Playgroud)
这是一个结合了两全其美的解决方案,使用ArrayIterator
内部:
phpunit/phpunit
模拟设施/**
* @return \PHPUnit_Framework_MockObject_MockObject|SomeIterator
*/
private function createSomeIteratorDouble(array $items = []): SomeIterator
{
$someIterator = $this->createMock(SomeIterator::class);
$iterator = new \ArrayIterator($items);
$someIterator
->method('rewind')
->willReturnCallback(function () use ($iterator): void {
$iterator->rewind();
});
$someIterator
->method('current')
->willReturnCallback(function () use ($iterator) {
return $iterator->current();
});
$someIterator
->method('key')
->willReturnCallback(function () use ($iterator) {
return $iterator->key();
});
$someIterator
->method('next')
->willReturnCallback(function () use ($iterator): void {
$iterator->next();
});
$someIterator
->method('valid')
->willReturnCallback(function () use ($iterator): bool {
return $iterator->valid();
});
return $someIterator;
}
Run Code Online (Sandbox Code Playgroud)
phpspec/prophecy
/**
* @return \PHPUnit_Framework_MockObject_MockObject|SomeIterator
*/
private function createSomeIteratorDouble(array $items = []): SomeIterator
{
$someIterator = $this->prophesize(\ArrayIterator::class);
$iterator = new \ArrayIterator($items);
$someIterator
->rewind()
->will(function () use ($iterator): void {
$iterator->rewind();
});
$someIterator
->current()
->will(function () use ($iterator) {
return $iterator->current();
});
$someIterator
->key()
->will(function () use ($iterator) {
return $iterator->key();
});
$someIterator
->next()
->will(function () use ($iterator): void {
$iterator->next();
});
$someIterator
->valid()
->will(function () use ($iterator): bool {
return $iterator->valid();
});
return $someIterator->reveal();
}
Run Code Online (Sandbox Code Playgroud)
如果你只需要针对泛型迭代器进行测试,那么PHP(在SPL扩展中 - 在PHP> 5.3中无法关闭)内置了数组包装器,它们实现了Iterable:SPL Iterators.例如
$mock_iterator = new \ArrayIterator($items);
$test_class->methodExpectingGenericIterator($mock_iterator);
$resulting_data = $mock_iterator->getArrayCopy();
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
5264 次 |
最近记录: |