Jes*_*ord 71 api rest phpunit integration-testing unit-testing
单元测试是测试一小段代码的方法(主要是单一方法).
集成测试是测试多个代码区域之间交互的测试(希望它们已经有了自己的单元测试).有时,部分测试代码需要其他代码以特定方式执行操作.这就是Mocks&Stubs的用武之地.因此,我们非常具体地模拟/删除部分代码.这使我们的集成测试可以预测地运行而没有副作用.
所有测试都应该能够独立运行而无需数据共享.如果需要数据共享,这表明系统没有足够的分离.
当与外部API(特别是将使用POST请求修改实时数据的RESTful API)进行交互时,我理解我们可以(应该?)模拟与该API的交互(在此答案中更加雄辩地说明)进行集成测试.我也理解我们可以单元测试与该API交互的各个组件(构造请求,解析结果,抛出错误等).我没有得到的是如何实际解决这个问题.
如何测试与具有副作用的外部API的交互?
一个完美的例子是Google的Content API用于购物.为了能够执行手头的任务,它需要大量的准备工作,然后执行实际请求,然后分析返回值.其中一些没有任何"沙盒"环境.
执行此操作的代码通常具有相当多的抽象层,例如:
<?php
class Request
{
public function setUrl(..){ /* ... */ }
public function setData(..){ /* ... */ }
public function setHeaders(..){ /* ... */ }
public function execute(..){
// Do some CURL request or some-such
}
public function wasSuccessful(){
// some test to see if the CURL request was successful
}
}
class GoogleAPIRequest
{
private $request;
abstract protected function getUrl();
abstract protected function getData();
public function __construct() {
$this->request = new Request();
$this->request->setUrl($this->getUrl());
$this->request->setData($this->getData());
$this->request->setHeaders($this->getHeaders());
}
public function doRequest() {
$this->request->execute();
}
public function wasSuccessful() {
return ($this->request->wasSuccessful() && $this->parseResult());
}
private function parseResult() {
// return false when result can't be parsed
}
protected function getHeaders() {
// return some GoogleAPI specific headers
}
}
class CreateSubAccountRequest extends GoogleAPIRequest
{
private $dataObject;
public function __construct($dataObject) {
parent::__construct();
$this->dataObject = $dataObject;
}
protected function getUrl() {
return "http://...";
}
protected function getData() {
return $this->dataObject->getSomeValue();
}
}
class aTest
{
public function testTheRequest() {
$dataObject = getSomeDataObject(..);
$request = new CreateSubAccountRequest($dataObject);
$request->doRequest();
$this->assertTrue($request->wasSuccessful());
}
}
?>
Run Code Online (Sandbox Code Playgroud)
注意:这是一个PHP5/PHPUnit示例
鉴于这testTheRequest
是测试套件调用的方法,该示例将执行实时请求.
现在,这个实时请求(希望提供一切顺利)执行POST请求,其具有更改实时数据的副作用.
这可以接受吗?我有什么替代品?我看不到模拟测试的Request对象的方法.即使我这样做,也意味着为Google的API接受的每个可能的代码路径设置结果/入口点(在这种情况下必须通过反复试验找到),但是允许我使用灯具.
进一步扩展是指某些请求依赖于已经存在的某些数据.再次使用Google Content API作为示例,要将数据Feed添加到子帐户,子帐户必须已存在.
我能想到的一种方法是以下步骤;
testCreateAccount
testCreateDataFeed
依赖于testCreateAccount
没有任何错误
testCreateDataFeed
,创建一个新帐户这就提出了进一步的问题; 如何测试帐户/数据Feed的删除?testCreateDataFeed
对我来说很脏 - 如果创建数据源失败怎么办?测试失败,因此子帐户永远不会被删除...我无法在没有创建的情况下测试删除,所以在创建然后删除自己的帐户之前testDeleteAccount
,我是否编写了另一个依赖的test()testCreateAccount
(因为数据不应该在测试之间共享).
有关:
这是对已经给出的一个额外答案:
仔细查看代码,它class GoogleAPIRequest
具有硬编码的依赖关系class Request
.这可以防止您独立于请求类进行测试,因此您无法模拟请求.
您需要使请求可注入,因此您可以在测试时将其更改为模拟.完成后,没有发送真正的API HTTP请求,实时数据不会更改,您可以更快地测试.