如何集成测试与外部API进行交互写的? [英] How are integration tests written for interacting with external API?

查看:153
本文介绍了如何集成测试与外部API进行交互写的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

一起来,我的知识是:

单元测试的是那些测试的一小块code(单方法,主要是)。

集成测试的是那些测试code的多个领域(这希望已经有了自己的单元测试)之间的相互作用。有时,被测code部分要求其他code在一个特定的方式行事。这是嘲弄和放大器;存根进来。所以,我们模拟/存根出code的一部分非常具体执行。这使得我们的整合测试,以无副作用pdictably运行$ P $。

所有测试应该能够没有数据共享运行单机。如果数据共享是必要的,这是不脱钩系统足够的标志。

接下来,我面临的处境:

当与外部API(具体而言,一个RESTful API将修改实时数据与POST请求),我明白,我们可以(应该?)模拟出与API的互动交互(以<更雄辩地说明href=\"http://stackoverflow.com/questions/559092/how-can-mocking-external-services-improve-unit-tests/559177#559177\">this答案)为集成测试。我也明白,我们可以单元测试与API交互(建设要求,解析结果,引发错误等)的各个组件。我不明白的是如何真正去了解这一点。

所以,最后:我的问题(S)

如何测试使用具有副作用外部API我的互动?

一个很好的例子是谷歌的购物。为了能够在手执行任务,它需要preP工作体面的数额,然后进行实际的请求,然后分析返回值。有些这是<一个href=\"https://groups.google.com/group/google-content-api-for-shopping/browse_thread/thread/ad6d1f482a7cae4d#msg_d4ab56ab47877fba\">without任何沙盒环境

在code要做到这一点一般具有抽象不少层,是这样的:

&LT; PHP
一流的请求
{
    公共职能setUrl(..){/ * ... * /}
    公共功能使用setData(..){/ * ... * /}
    公共职能setHeaders(..){/ * ... * /}
    公共职能执行(..){
        //做一些卷曲请求或一些,这样
    }
    公共职能wasSuccessful(){
        //一些测试,以查看是否卷曲请求成功
    }
}类GoogleAPIRequest
{
    私人$请求;
    抽象保护功能的getURL();
    抽象保护功能的getData();    公共职能__construct(){
        $这个 - &GT;请求=新申请();
        $这个 - &GT;请求 - &GT; setUrl($这个 - &GT;的getURL());
        $这个 - &GT;请求 - &GT;使用setData($这个 - &GT;的getData());
        $这 - &GT;请求 - &GT; setHeaders($这 - &GT; getHeaders());
    }    公共职能doRequest(){
        $这个 - &GT;请求 - &GT;执行();
    }
    公共职能wasSuccessful(){
        返回($这 - &GT;请求 - &GT; wasSuccessful()及&放大器; $这 - &GT; parseResult());
    }
    私有函数parseResult(){
        //返回false,如果结果不能被解析
    }    保护的函数getHeaders(){
        //返回一些GoogleAPI特定的头文件
    }
}类CreateSubAccountRequest扩展GoogleAPIRequest
{
    私人$ dataObject时;    公共职能__construct($ dataObject时){
        父:: __构造();
        $这个 - &GT; dataObject时= $ dataObject时;
    }
    保护功能的getURL(){
        返回的http:// ...;
    }
    保护功能的getData(){
        返回$这个 - &GT; dataObject-&GT; getSomeValue();
    }
}类ATEST
{
    公共职能testTheRequest(){
        $ dataObject时= getSomeDataObject(..);
        $ =请求新CreateSubAccountRequest($ dataObject时);
        $请求 - &GT; doRequest();
        $这 - &GT; assertTrue($请求 - &GT; wasSuccessful());
    }
}
?&GT;

注:这是一个PHP5 / PHPUnit的例子

由于 testTheRequest 是测试套件调用的方法,该示例将执行现场要求。

现在,这个活请求将(有希望,只要一切顺利)做了改变实时数据的副作用的POST请求。

这是可以接受的?我有什么选择呢?我看不到的方式来模拟出用于测试的Request对象。即使我做到了,这将意味着设立结果/切入点,谷歌的API接受(在这种情况下就必须通过试验和错误中找到)每一个可能的code路径,但会允许我使用灯具。

一个进一步扩展是当某些请求依赖于特定的数据是直播了。再次使用谷歌API的内容作为一个例子,一个数据源添加到一个子账户,分账户必须已经存在。

一种方法我能想到的是下面的步骤;


  1. testCreateAccount

    1. 创建一个子账户

    2. 断言子帐户的创建

    3. 删除子账户


  2. testCreateDataFeed 取决于 testCreateAccount 没有任何错误

    1. testCreateDataFeed ,创建一个新帐户

    2. 创建数据馈送

    3. 断言数据馈送创建

    4. 删除数据馈送

    5. 删除子账户


这则提出了进一步的问题;我怎么测试帐户,删除/数据输入? testCreateDataFeed 脏的感觉对我来说 - 如果创建数据馈送失败怎么办?测试失败,因此子帐户不会被删除......没有创造我无法测试的缺失,所以做我写另外一个测试( testDeleteAccount )依靠 testCreateAccount 创建然后删除了自己的帐户(因为数据不应测试之间共享)。

在摘要


  • 如何测试与效果的实时数据的外部API交互?

  • 我如何可以模拟在集成测试/存根对象时,他们隐藏的抽象?背后的层层

  • 什么时候做一个测试失败,并且实时数据被留在不一致的状态怎么办?

  • 如何的在code 的我是不是真的去这样做呢?


相关阅读:


解决方案

  

如何测试与效果的实时数据的外部API交互?


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

您可以 - 也应该 - 运动与实时数据的API,以确保你了解它。

但你并不需要对其进行测试。如果API不起作用,干脆停止使用。不要测试每个边角情况。


  

我如何可以模拟在集成测试/存根对象时,他们隐藏的抽象层,后面?


这是问题的关键。测试抽象。你要相信,实施工作。您正在测试的 code。不是他们的code。


  

我该怎么办时,测试失败,并且实时数据被留在不一致的状态?


什么?你为什么要测试现场的API,以确保他们的工作?你不信任他们吗?如果你不信任他们,不测试。找到可以信任的供应商。

您只测试 code。你信任的 code。你假装平凡的不足code,以确保您的code ++工程。


你怎么做到这一点。


  1. 玩的API。发送请求。获得响应。


  2. 玩弄你的应用程序。搞清楚什么样的,你要发送的请求。


  3. 返回到API。发送已知良好的请求。获得响应。 保存这个响应。这是你的金标准响应良好的请求。推崇到一个测试用例这一点。


  4. 现在你可以在你的应用程序工作,知道你有一个黄金标准响应thatreally离真正的API来了。这应该是足够的上手操作的响应。


  5. 在您通过一些用例的工作(好的要求,错误的请求),你应该能够获得良好的反应,从API一些典型的错误响应。保存好和错误消息。这些都是有用的单元测试,以确保您正在处理的一些种恰当反应的。


First up, where my knowledge is at:

Unit Tests are those which test a small piece of code (single methods, mostly).

Integration Tests are those which test the interaction between multiple areas of code (which hopefully already have their own Unit Tests). Sometimes, parts of the code under test requires other code to act in a particular way. This is where Mocks & Stubs come in. So, we mock/stub out a part of the code to perform very specifically. This allows our Integration Test to run predictably without side effects.

All tests should be able to be run stand-alone without data sharing. If data sharing is necessary, this is a sign the system isn't decoupled enough.

Next up, the situation I am facing:

When interacting with an external API (specifically, a RESTful API that will modify live data with a POST request), I understand we can (should?) mock out the interaction with that API (more eloquently stated in this answer) for an Integration Test. I also understand we can Unit Test the individual components of interacting with that API (constructing the request, parsing the result, throwing errors, etc). What I don't get is how to actually go about this.

So, finally: My question(s).

How do I test my interaction with an external API that has side effects?

A perfect example is Google's Content API for shopping. To be able to perform the task at hand, it requires a decent amount of prep work, then performing the actual request, then analysing the return value. Some of this is without any 'sandbox' environment.

The code to do this generally has quite a few layers of abstraction, something like:

<?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());
    }
}
?>

Note: This is a PHP5 / PHPUnit example

Given that testTheRequest is the method called by the test suite, the example will execute a live request.

Now, this live request will (hopefully, provided everything went well) do a POST request that has the side effect of altering live data.

Is this acceptable? What alternatives do I have? I can't see a way to mock out the Request object for the test. And even if I did, it would mean setting up results / entry points for every possible code path that Google's API accepts (which in this case would have to be found by trial and error), but would allow me the use of fixtures.

A further extension is when certain requests rely on certain data being Live already. Using the Google Content API as an example again, to add a Data Feed to a Sub Account, the Sub Account must already exist.

One approach I can think of is the following steps;

  1. In testCreateAccount

    1. Create a sub-account
    2. Assert the sub-account was created
    3. Delete the sub-account

  2. Have testCreateDataFeed depend on testCreateAccount not having any errors

    1. In testCreateDataFeed, create a new account
    2. Create the data feed
    3. Assert the data feed was created
    4. Delete the data feed
    5. Delete the sub-account

This then raises the further question; how do I test the deletion of accounts / data feeds? testCreateDataFeed feels dirty to me - What if creating the data feed fails? The test fails, therefore the sub-account is never deleted... I can't test deletion without creation, so do I write another test (testDeleteAccount) that relies on testCreateAccount before creating then deleting an account of its own (since data shouldn't be shared between tests).

In Summary

  • How do I test interacting with an external API that effects live data?
  • How can I mock / stub objects in an Integration test when they're hidden behind layers of abstraction?
  • What do I do when a test fails and the live data is left in an inconsistent state?
  • How in code do I actually go about doing all this?

Related:

解决方案

How do I test interacting with an external API that effects live data?

You don't. You have to actually trust that the actual API actually works.

You can -- and should -- exercise the API with live data to be sure you understand it.

But you don't need to test it. If the API doesn't work, simply stop using it. Don't test every edge and corner case.

How can I mock / stub objects in an Integration test when they're hidden behind layers of abstraction?

That's the point. Test the abstraction. You have to trust that the implementation works. You're testing your code. Not their code.

What do I do when a test fails and the live data is left in an inconsistent state?

What? Why are you testing live API's to be sure they work? You don't trust them? If you don't trust them, don't test. Find a vendor you can trust.

You only test your code. You trust their code. You trivially mock enough of their code to be sure your code works.


How you do this.

  1. Play around with the API. Send requests. Get responses.

  2. Play around with your app. Figure out what kinds of requests you're going to send.

  3. Go back to the API. Send a known good request. Get the response. Save this response. This is your gold-standard response to a good request. Canonize this into a test case.

  4. Now you can work on your app knowing that you have a gold-standard response thatreally came from the real API. That should be sufficient to get started handling the responses.

  5. After you have worked through a few use cases (good request, bad request) you should be able to get a good response and some typical error responses from the API. Save the good and the error messages. These are useful for unit testing to be sure you're handling some of the kinds of responses properly.

这篇关于如何集成测试与外部API进行交互写的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆