php 如何在php中对curl调用进行单元测试
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7911535/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
how to unit test curl call in php
提问by Yader Hernandez
How would you go about unit testing a curl implementation?
您将如何对 curl 实现进行单元测试?
public function get() {
$ch = curl_init($this->request->getUrl());
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
curl_close($ch);
if (!strstr($type, 'application/json')) {
throw new HttpResponseException('JSON response not found');
}
return new HttpResponse($code, $result);
}
I need to test the content type returned so that it can throw an exception.
我需要测试返回的内容类型,以便它可以抛出异常。
回答by David Harkness
As thomasrutter suggested, create a class to abstract the usage of the cURL functions.
正如 thomasrutter 建议的那样,创建一个类来抽象 cURL 函数的用法。
interface HttpRequest
{
public function setOption($name, $value);
public function execute();
public function getInfo($name);
public function close();
}
class CurlRequest implements HttpRequest
{
private $handle = null;
public function __construct($url) {
$this->handle = curl_init($url);
}
public function setOption($name, $value) {
curl_setopt($this->handle, $name, $value);
}
public function execute() {
return curl_exec($this->handle);
}
public function getInfo($name) {
return curl_getinfo($this->handle, $name);
}
public function close() {
curl_close($this->handle);
}
}
Now you can test using a mock of the HttpRequest
interface without invoking any of the cURL functions.
现在,您可以使用HttpRequest
界面的模拟进行测试,而无需调用任何 cURL 函数。
public function testGetThrowsWhenContentTypeIsNotJson() {
$http = $this->getMock('HttpRequest');
$http->expects($this->any())
->method('getInfo')
->will($this->returnValue('not JSON'));
$this->setExpectedException('HttpResponseException');
// create class under test using $http instead of a real CurlRequest
$fixture = new ClassUnderTest($http);
$fixture->get();
}
EditFixed simple PHP parse error.
编辑修复了简单的 PHP 解析错误。
回答by cweiske
Do not use curl directly but through a wrapper like PEAR's HTTP_Request2. With it, you have the ability to exchange the curl driver with a mock driver - ideal for unit tests.
不要直接使用 curl,而是通过 PEAR 的HTTP_Request2 之类的包装器。有了它,您就可以将 curl 驱动程序与模拟驱动程序交换 - 非常适合单元测试。
回答by Markus Malkusch
You might use a function mock library. I made one for you: php-mock-phpunit
您可能会使用函数模拟库。我为你做了一个:php-mock-phpunit
namespace foo;
use phpmock\phpunit\PHPMock;
class BuiltinTest extends \PHPUnit_Framework_TestCase
{
use PHPMock;
public function testCurl()
{
$curl_exec = $this->getFunctionMock(__NAMESPACE__, "curl_exec");
$curl_exec->expects($this->once())->willReturn("body");
$ch = curl_init();
$this->assertEquals("body", curl_exec($ch));
}
}
回答by Martin Ender
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.
当我自己尝试使用 cURL 测试一个类时,我偶然发现了这个问题。我将 David Harkness 的建议牢记在心,并为 cURL 创建了一个界面。但是,PHPUnit 提供的存根/模拟功能在我的情况下是不够的,所以我添加了我自己的接口存根实现并将其全部放在 GitHub 上。因为这个问题在搜索这个问题时很早就出现在谷歌上,我想我会把它贴在这里,所以其他人可能会省力。
The repository's wikicontains a rather detailed documentation of the stub's capabilities, but here they are in short.
该仓库的wiki包含的存根的能力作一个比较详细的文档,但在这里,他们都在做空。
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
.
该接口是 PHP 的 cURL 函数的 1:1 映射,因此可以非常容易地开始使用该接口(只需传递ClassUnderTest
一个实例实现SAI_CurlInterface
,然后像以前一样调用所有 cURL 函数,但作为该实例上的方法)。该类SAI_Curl
通过简单地委托给 cURL 来实现这个接口。现在如果你想测试ClassUnderTest
你可以给它一个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).
存根主要缓解了 PHPUnit 的模拟和存根无法根据以前的函数调用返回虚拟数据的问题(但这就是 cURL 实际工作的方式 - 您设置选项,响应、错误代码和 cURL-info 取决于这些选项)。所以这是一个简短的例子,展示了这些响应能力(对于错误代码和 cURL-info,请参阅 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.
您可以根据任何 cURL 选项的任意组合做出响应。当然,您可以更进一步。举例来说,您ClassUnderTest
从服务器获取一些 XML 数据并对其进行解析(嗯,您应该有两个单独的类来执行这些任务,但让我们在我们的示例中假设这一点),并且您想要测试该行为。您可以手动下载 XML 响应,并让您的测试从文件中读取数据并将其填充到响应中。然后你就可以确切地知道那里有什么数据,并且可以检查它是否被正确解析。或者,您可以立即实现SAI_CurlInterface
从文件系统加载所有响应,但现有的实现绝对是一个起点。
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.
在我写这个答案的时候,@SAI_CurlStub@ 还不支持 cURL 多库功能,但我计划在未来实现这一点。
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.
我希望这个存根对任何想要对 cURL 相关类进行单元测试的人都有帮助。随意查看和使用这些类,或者贡献,当然 - 毕竟它在 GitHub 上:)。此外,我对任何关于接口和存根的实现和使用的建设性批评持开放态度。
回答by thomasrutter
One approach to this involves replacing the interface you are using (in this case, the curl_ functions) with dummy versions of themselves which return certain values. If you were using an object-oriented library this would be easier because you could just substitute an dummy object which has the same method names (and indeed, frameworks like simpletest can set up dummy object methods easily). Otherwise, perhaps there is some other sorcery you can use to override built-in functions with dummies. This extension includes override_function()which looks like what you'd need, though that would add another dependency.
一种方法是将您使用的接口(在本例中为 curl_ 函数)替换为自己的虚拟版本,这些版本返回某些值。如果您使用的是面向对象的库,这会更容易,因为您可以只替换具有相同方法名称的虚拟对象(实际上,像 simpletest 这样的框架可以轻松设置虚拟对象方法)。否则,也许还有其他一些法术可以用来用假人覆盖内置函数。 这个扩展包括 override_function(),它看起来像你需要的,尽管这会增加另一个依赖。
If you want to test this without replacing the curl_ functions with dummy versions, it looks like you will need to set up a dummy server that will return a certain result, so that you can test the way your PHP, and its curl extension, handles that result. To fully test it, you'd need to access this over HTTP rather than, say, a local file, because your PHP depends on having an HTTP response code, etc. So your tests will need a functioning HTTP server.
如果你想测试这个而不用虚拟版本替换 curl_ 函数,看起来你需要设置一个虚拟服务器来返回某个结果,这样你就可以测试你的 PHP 及其 curl 扩展的处理方式那个结果。要对其进行全面测试,您需要通过 HTTP 而不是本地文件访问它,因为您的 PHP 依赖于具有 HTTP 响应代码等。因此您的测试将需要一个正常运行的 HTTP 服务器。
Incidentally, PHP 5.4 will actually include its own web serverwhich would come in handy for this purpose. Otherwise, you could put a test script on a known server which you control, or distribute a simple server config with your tests.
顺便说一句,PHP 5.4 实际上将包含自己的 Web 服务器,这将派上用场。否则,您可以将测试脚本放在您控制的已知服务器上,或者随测试一起分发简单的服务器配置。
If you were to actually use the live server for your testing, this would become less of a unittest and more of an integration test, because you be testing both your PHP and the server, and the integration between the two. You would also miss out on being able to test on demand how your code handles certain failures.
如果您实际使用实时服务器进行测试,这将不再是单元测试,而是集成测试,因为您要同时测试 PHP 和服务器,以及两者之间的集成。您还将错过能够按需测试您的代码如何处理某些故障的机会。
回答by Robert Martin
In your unit test, have request->getUrl()
return the URI of a local file that you know will throw the exception.
在您的单元测试中,request->getUrl()
返回您知道会抛出异常的本地文件的 URI。