如何编写与外部API交互的集成testing?

首先,我的知识在于:

unit testing是testing一小段代码(主要是单一方法)的unit testing

集成testing是testing多个代码区域(希望已经有自己的unit testing)之间的交互的那些testing。 有时,被测代码的一部分需要其他代码以特定方式进行操作。 这就是Mocks&Stubs进来的地方。所以,我们嘲笑/去掉一部分代码来执行。 这使我们的集成testing可以运行,没有副作用。

所有的testing应该能够独立运行而不需要数据共享。 如果数据共享是必要的,这是一个标志系统不够脱钩。

接下来,我面对的情况是:

当与外部API交互(特别是使用POST请求修改实时数据的RESTful API)时,我知道我们可以(应该)模拟与该API的交互(在此答案中更加雄辩地陈述)集成testing。 我也明白,我们可以unit testing与该API交互的个别组件(构build请求,parsing结果,抛出错误等)。 我没有得到的是如何去实际去做这件事。

所以,最后:我的问题。

如何testing我与具有副作用的外部API的交互?

Google的购物内容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()); } } ?> 

注意:这是一个PHP5 / PHPUnit示例

考虑到testTheRequest是testing套件调用的方法,该示例将执行一个实时请求。

现在,这个实时请求会(希望提供的一切顺利)执行一个POST请求,这个请求会改变实时数据的副作用。

这可以接受吗? 我有什么替代方法? 我看不出一种方法来模拟testing的Request对象。 即使我这样做了,这也意味着要为Google API所接受的每个可能的代码path(在这种情况下必须通过反复试验find)设置结果/入口点,但是可以使用灯具。

进一步的扩展是当某些请求依赖某些已经存在的数据。 再次以Google Content API为例,要将数据馈送添加到子帐户,子帐户必须已经存在。

我能想到的一个方法是以下步骤。

  1. testCreateAccount
    1. 创build一个子帐户
    2. 断言子帐户已创build
    3. 删除子帐户
  2. testCreateDataFeed依赖于testCreateAccount没有任何错误
    1. testCreateDataFeed ,创build一个新的帐户
    2. 创build数据馈送
    3. 断言数据馈送已创build
    4. 删除数据Feed
    5. 删除子帐户

这就提出了进一步的问题。 我如何testing帐户/数据提要的删除? testCreateDataFeed对我来说感觉很脏 – 如果创build数据Feed失败了,该怎么办? testing失败,因此子帐户永远不会删除…我不能testing删除没有创build,所以我写另一个testing( testDeleteAccount )依赖testCreateAccount创build之前,然后删除自己的帐户(因为数据shouldntesting之间不共享)。

综上所述

  • 如何testing与影响实时数据的外部API交互?
  • 如何在集成testing中将对象隐藏在抽象层之后如何模拟/存根对象?
  • 当testing失败并且实时数据处于不一致的状态时,我该怎么办?
  • 如何在代码中我真的去做这一切?

有关:

  • 如何嘲笑外部服务改进unit testing?
  • 编写unit testingREST-ful API

如何testing与影响实时数据的外部API交互?

你没有。 你必须真正相信实际的API实际工作。

您可以 – 也应该 – 使用实时数据来运行API,以确保您了解它。

但是你不需要testing它。 如果API不起作用,只需停止使用它。 不要testing每个边缘和angular落的情况。

如何在集成testing中将对象隐藏在抽象层之后如何模拟/存根对象?

这才是重点。 testing抽象。 你必须相信这个实现是有效的。 你正在testing你的代码。 不是他们的代码。

当testing失败并且实时数据处于不一致的状态时,我该怎么办?

什么? 为什么要testing实时API以确保它们正常工作? 你不相信他们? 如果你不信任他们,不要testing。 find一个你可以信任的供应商。

你只testing你的代码。 你相信他们的代码。 你琐碎地嘲笑他们的代码,以确保你的代码工作。


你如何做到这一点。

  1. 玩这个API。 发送请求。 得到回应。

  2. 玩你的应用程序。 找出你要发送什么types的请求。

  3. 回到API。 发送已知的好请求。 得到回应。 保存这个响应 。 这是您的一个很好的要求的黄金标准的回应。 将这个标准化为一个testing用例。

  4. 现在你可以在你的应用程序上工作,知道你有一个真正的来自真实API的黄金标准的回应。 这应该足以开始处理答复。

  5. 在您完成了几个用例(好的请求,错误的请求)之后,您应该能够获得良好的响应以及来自API的一些典型的错误响应。 保存好的和错误信息。 这对于unit testing非常有用,以确保您正确处理某些types的响应。

这是已经给出的更多答案:

class GoogleAPIRequest代码, class GoogleAPIRequest具有class Request的硬编码依赖关系。 这可以防止你从请求类独立地进行testing,所以你不能模拟请求。

您需要使请求可注入,因此您可以在testing时将其更改为模拟。 这样做,没有真正的API HTTP请求发送,实时数据不会改变,你可以更快地testing。

我最近不得不更新一个库,因为它连接到的API已经更新。

我的知识还不够详细解释,但是我从代码中学到了很多东西。 https://github.com/gridiron-guru/FantasyDataAPI

您可以像往常一样向api提交请求,然后将该响应保存为json文件,然后可以将其用作模拟。

看看这个库中使用Guzzle连接到api的testing。

它嘲笑api的反应,文档中有很多关于如何进行testing的信息,它可能会给你一个如何去做的想法。

但基本上你可以手动调用api以及你需要的任何参数,并将响应保存为json文件。

当您为api调用编写testing时,请发送相同的参数,并将其加载到模拟中,而不是使用活动api,然后可以在您创build的模拟中testing包含期望值的数据。

我的更新版本的问题可以在这里find。 更新回购