I stumbled upon this question when I was trying to test a class using cURL myself. I took David Harkness's advice to heart and created an interface for cURL. However, the stub/mock functionality provided by PHPUnit was not sufficient in my case, so I added my own stub implementation of the interface and put it all on GitHub. And because this question shows up rather early on Google when searching this issue, I thought I would post it here, so the others might be able to save the effort.
Here it is.
The repository's wiki contains a rather detailed documentation of the stub's capabilities, but here they are in short.
The interface is a 1:1 mapping of PHP's cURL functions, so as to make it very easy to start using the interface (simply hand your ClassUnderTest
an instance implementing SAI_CurlInterface
and then call all cURL functions as before, but as methods on that instance). The class SAI_Curl
implements this interface by simply delegating to cURL. Now if you want to test the ClassUnderTest
you can give it an instance of SAI_CurlStub
.
The stub mainly alleviates the problem that PHPUnit's mocks and stubs cannot returned dummy data depending on former function calls (but this is how cURL actually works - you set up your options and the response, error code and cURL-info depend on those options). So here is a short example, showing those capabilities for responses (for error codes and cURL-info, see the wiki).
public function testGetData()
{
$curl = new SAI_CurlStub();
// Set up the CurlStub
$defaultOptions = array(
CURLOPT_URL => 'http://www.myserver.com'
);
$chromeOptions = array(
CURLOPT_URL => 'http://www.myserver.com',
CURLOPT_USERAGENT => 'Chrome/22.0.1207.1'
);
$safariOptions = array(
CURLOPT_URL => 'http://www.myserver.com',
CURLOPT_USERAGENT => 'Safari/537.1'
);
$curl->setResponse('fallback response');
$curl->setResponse('default response from myserver.com'
$defaultOptions);
$curl->setResponse('response for Chrome from myserver.com',
$chromeOptions);
$curl->setResponse('response for Safari from myserver.com',
$safariOptions);
$cut = new ClassUnderTest($curl);
// Insert assertions to check whether $cut handles the
// different responses correctly
...
}
You can make your response dependent on any combination of any cURL-options. Of course, you can take this even further. Say for example, your ClassUnderTest
takes some XML data from a server and parses it (well, you should have two separate classes for those tasks, but let's assume this for our example), and you want to test that behavior. You could download the XML response manually, and have your test read the data from the file and stuff it into the response. Then you know exactly what data is there, and can check whether it's parsed correctly. Alternatively, you could implement the SAI_CurlInterface
loading all responses from your file system right away, but the existing implementation is definitely a point to start.
At the time that I I am writing this answer, @SAI_CurlStub@ does not support cURL multi-lib features yet, but I plan to implement this, too, in the future.
I hope this stub is of help to anyone who wants to unit test cURL-dependent classes. Feel free to check out and use the classes, or contribute, of course - it's on GitHub after all :). Also, I am open to any constructive criticism regarding implementation and usage of interface and stub.