I must say that your class is not intended for isolated unit tests due to the hard-coded curl_* methods. To make it better, you have at least 2 options:
1) Extract curl_* function calls to another class and pass this class as a parameter
class CurlCaller { public function call($url) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $result = curl_exec($ch); curl_close($ch); return $result; } } class Youtube { public function __construct($apiKey, CurlCaller $caller) { $this->apiKey = $apiKey; $this->caller = $caller; } }
Now you can easily make fun of the CurlCaller class. There are many ready-made solutions that abstract the network. For example, Guzzle is excellent
2) Another option is to extract the curl_* calls of the protected method and mock this method. Here is a working example:
// Firstly change your class: class Youtube { // ... public function searchYoutube($channel, $query) { $url = 'https://www.googleapis.com/youtube/v3/search?order=date' . '&part=snippet' . '&channelId=' . urlencode($channel) . '&type=video' . '&maxResults=25' . '&key=' . urlencode($this->apiKey) . '&q=' . urlencode($query); $result = $this->callUrl($url); $result = json_decode($result, true); if ( is_array($result) && count($result) ) { return $this->extractVideo($result); } return $result; } // This method will be overriden in test. protected function callUrl($url) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $result = curl_exec($ch); curl_close($ch); return $result; } }
Now you can mock the callUrl method. But first, let's assume the api response to fixtures/youtube-response-stub.json file.
class YoutubeTest extends PHPUnit_Framework_TestCase { public function testYoutube() { $apiKey = 'StubApiKey'; // Here we create instance of Youtube class and tell phpunit that we want to override method 'callUrl' $youtube = $this->getMockBuilder(Youtube::class) ->setMethods(['callUrl']) ->setConstructorArgs([$apiKey]) ->getMock(); // This is what we expect from youtube api but get from file $fakeResponse = $this->getResponseStub(); // Here we tell phpunit how to override method and our expectations about calling it $youtube->expects($this->once()) ->method('callUrl') ->willReturn($fakeResponse); // Get results $list = $youtube->searchYoutube('UCSZ3kvee8aHyGkMtShH6lmw', 'php'); $expected = ['items' => [[ 'videoId' => 'video-id-stub', 'title' => 'title-stub', 'description' => 'description-stub', 'thumbnail' => 'https://i.ytimg.com/vi/stub/thimbnail-stub.jpg', ]]]; // Finally assert result with what we expect $this->assertEquals($expected, $list); } public function getResponseStub() { $response = file_get_contents(__DIR__ . '/fixtures/youtube-response-stub.json'); return $response; } }
Run the test and ... OMG FAILURE !! 1 You have typos in the extractVideo method, there should be $item instead of $items . Let's fix it
$videoId = $item['id']['videoId']; $title = $item['snippet']['title']; $description = $item['snippet']['description']; $thumbnail = $item['snippet']['thumbnails']['default']['url'];
OK, now he is passing.
If you want to test your class when calling the Youtube API, you just need to create a normal Youtube class.
By the way, there is php-youtube-api lib that has providers for laravel 4 and laravel 5, there are also tests