Skip to content

Instantly share code, notes, and snippets.

@ukolka
Last active August 15, 2019 12:19
Show Gist options
  • Save ukolka/8448362 to your computer and use it in GitHub Desktop.
Save ukolka/8448362 to your computer and use it in GitHub Desktop.
PHPUnit extension for testing code that's using PHP's http:// stream wrapper context to make requests.
<?php
namespace tests\unittests\custom;
/**
* Whenever PHP HTTP stream is used you can
* inject a mock stream resource into an object that
* expect it.
*
* Usage example:
*
* ...
* $stream = getMockStream('{"foo": "bar"}');
* $dependency->expects($this->all())
* ->method('getStream')
* ->will($this->returnValue($stream));
* $dependent->setDependency($dependency);
* ...
*
* @link http://www.php.net/manual/en/book.stream.php
* @author Mykola Bespaliuk
*/
abstract class HttpStreamWrapperTestCase extends \PHPUnit_Framework_TestCase {
public function setUp() {
parent::setUp();
stream_wrapper_unregister('http');
stream_wrapper_register(
'http',
'tests\unittests\custom\MockHttpStreamWrapper'
) or die('Failed to register protocol');
}
/**
* Makes a mock stream that returns expected values.
* @param string $data response body
* @param string $code HTTP response code
*/
public function getMockStream($data, $code='HTTP/1.1 200 OK') {
MockHttpStreamWrapper::$mockBodyData = $data;
MockHttpStreamWrapper::$mockResponseCode = $code;
$context = stream_context_create(
array(
'http' => array(
'method' => 'GET'
)
)
);
$stream = fopen('http://example.com', 'r', false, $context);
return $stream;
}
public function tearDown() {
stream_wrapper_restore('http');
}
}
/**
* Class MockHttpStreamWrapper
* Stunt double of the PHP HTTP stream wrapper.
* @author Mykola Bespaliuk
*/
class MockHttpStreamWrapper implements
\IteratorAggregate, \ArrayAccess, \Countable{
/**
* Using static properties here because it's easy to set them up
* before running request and in stream_open method corresponding
* object properties are overridden with the
* contents of the static properties here.
*/
public static $mockBodyData = '';
public static $mockResponseCode = 'HTTP/1.1 200 OK';
public $context;
public $position = 0;
public $bodyData = 'test body data';
public $responseCode = '';
/**
* @var array $foo
* Example:
* array(
* 0 => 'HTTP/1.0 301 Moved Permantenly',
* 1 => 'Cache-Control: no-cache',
* 2 => 'Connection: close',
* 3 => 'Location: http://example.com/foo.jpg',
* 4 => 'HTTP/1.1 200 OK',
* ...
*/
protected $foo = array();
/* IteratorAggregate */
public function getIterator() {
return new \ArrayIterator($this->foo);
}
/* ArrayAccess */
public function offsetExists($offset) {
return array_key_exists($offset, $this->foo);
}
public function offsetGet($offset ) {
return $this->foo[$offset];
}
public function offsetSet($offset, $value) {
$this->foo[$offset] = $value;
}
public function offsetUnset($offset) {
unset($this->foo[$offset]);
}
/* Countable */
public function count() {
return count($this->foo);
}
/* StreamWrapper */
public function stream_open($path, $mode, $options, &$opened_path) {
$this->bodyData = self::$mockBodyData;
$this->responseCode = self::$mockResponseCode;
array_push($this->foo, self::$mockResponseCode);
return true;
}
public function stream_read($count) {
if ($this->position > strlen($this->bodyData)) {
return false;
}
$result = substr($this->bodyData, $this->position, $count);
$this->position += $count;
return $result;
}
public function stream_eof() {
return $this->position >= strlen($this->bodyData);
}
public function stream_stat() {
return array('wrapper_data' => array('test'));
}
public function stream_tell() {
return $this->position;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment