您如何测试仅用于查询外部API但API使用复杂查询语法的函数?


16

唯一的逻辑是外部API的查询语法。我不想测试它是否查询api,我想测试它是否以返回正确数据的方式查询它。例如,一些伪代码:

function retrieve_related_data(id)
{
  query = "[potentially long, syntactically complex query that
            uses param id to get some data]";
  results = api_wrapper.query(query);
  return results;
}

一个由API组成的更具体的示例:

function retrieveLifeSupportingObjectsWithinRegion(id)
{
  query = "
    within region(" + id + ") as r
    find objects matching hydration>0 and temp_range has 75
    send name, id, relative(position, r)        
  ";
  results = astronomicalObjectApiWrapper.query(query);
  return results;
}

该查询采用API的语法自定义方式,非常复杂,并且有多种方法可以实现相同或相似的结果。该功能的目的不是获取由标识的数据,id而是根据与标识的数据之间的模糊关系找到其他数据的子集,该模糊关系id也满足其他一些要求。不管其他要求如何,其他要求始终相同,id但是随着系统的修改,其他要求可能会随时间而变化。例如,如果示例api添加了对重力信息的支持,我们可能希望更改查询以也使用重力来优化结果。或者,也许我们想出了一种更有效的方法来检查温度范围,但它不会改变结果。

我要测试的是,对于给定的输入id,将返回正确的数据集。我想对此进行测试,以便如果有人将查询弄乱,使其不再基于id失败而返回正确的数据,但我也希望人们能够修改查询以优化查询,而无需也进行修改考试。

我考虑过的选项:

  1. 我可以对api进行存根处理,但这可能太简单了(检查查询中id是否存在,然后返回预期的数据集(如果没有)或返回意外的数据集),太脆(检查查询字符串是否正确)。或功能太复杂(检查所使用的查询在语法上是否正确,会导致返回正确的数据)。

  2. 我可以将查询提交给真实的api,但是随着外部系统中数据的变化(在测试系统的控制范围之外),预期的结果可能会随着时间而变化。

  3. 我可以考虑设置真实api的测试安装以控制其具有的数据,但这是很多工作。

我倾向于#2,将其更多地作为不经常运行的集成测试,并查看外部系统数据的更改导致测试中断的频率。我认为目前这是最简单的方法,但是我想知道是否有我没有想到的替代方法或解决此问题的更好方法。任何意见,将不胜感激。


我本以为这是一个单元测试,但也许它确实是集成测试或低级验收测试?
Joshua Coady

2
它是只读API吗?或者,您是否能够写入可以可靠地验证您的读数的数据?
svidgen

这个问题与“如何测试我的sql(= complex语法)是否返回正确的数据”不同吗?对于数据库,您通常会进行一些集成测试,这些测试将测试crud-sql语法和模拟仓库外观以验证businesslogic
k3b

Answers:


7

似乎在验证外部API响应时,我们将测试我们的功能,但事实并非如此。不知何故,我们将测试外部API和运行该API的环境。

我们应该对测试进行处理,以保证我们编写的代码(而非第三方编写的代码)的预期行为。

在一定程度上,我们必须信任我们依赖的API和库的正确功能。例如,我们通常不测试我们实现的框架组件。

我为什么这么说呢?

我要测试的是,对于给定的输入id,返回正确的数据集

这里将要测试什么?如您所说,数据及其正确性不受我们控制,因此我们会将测试阶段的成功限制在我们无法控制的外部代理上。这些测试是不确定性和确定性的候选,我们不希望在建筑管道中进行此类测试

另一个问题是确认合同。我会发现合同测试1非常有用,可以确保在任何发行或部署之前,集成仍按预期工作。

我想对此进行测试,以便如果有人弄乱了查询,使其不再根据id返回正确的数据,它将失败

如果查询正常,但由于API中的错误而导致数据错误怎么办?不仅数据是我们无法控制的。逻辑也是。

实施功能测试或端到端测试可能会有所帮助。您可以解决这些测试以验证某些执行路径,以便如果API返回错误的数据,则可能会导致意外的行为和输出。另一方面,如果查询格式错误,我希望API会引发错误。

但我也希望人们能够修改查询以优化查询,而无需同时修改测试。

我建议为此目的实施工具。它可能很简单:

  • 作为测试运行的类,但不属于测试计划
  • 一个shell脚本+ curl

或更复杂的东西。例如,一个独立的客户端。

无论如何,问题下的函数值得进行两种测试:

  • 单元测试。如您所说,您必须使用外部API,但这就是单元测试的全部目的。测试我们的代码以隔离依赖关系。

  • 集成测试。检查代码不仅发送正确的请求,还正确处理响应内容,错误,重定向等。请针对所有这些情况进行测试但不测试数据

旁注:您的问题类似于-如何测试应用程序的SQL语句-?

相关问题


1:您可能对@DocBrown对此主题的回答感兴趣


“问题(IMO)是您过于专注于测试外部API。” -我什么都没有看到,表明质问者完全有兴趣测试外部API。另外,您说“对外部API进行存根”,但是您是否对询问者是否应使用“过于简单”选项,“过于脆弱”选项,“过于复杂”选项或第四个选项提出任何建议?
Tanner Swett's

OP问题是询问如何测试调用外部API的函数。但是在我看来,阅读他的疑惑似乎是他过于强调测试查询及其结果。我提出了4条建议:(1)不要对API进行测试。(2)不要将集成测试用作校正查询的工作台。改用工具。(3)回到主要问题,做其单一性和综合性测试。但不验证API响应的内容。(4)向项目经理询问是否/可以制作外部API测试套件作为项目测试计划的一部分。
2015年

1
我认为查询本身是“我们编写的代码”。最终目标是自动测试,以提醒我们是否在代码中引入了错误。按照您所说的关于SQL语句的说法,我想它类似于-我想我的问题就像您如何测试您的代码是否以一种预期的方式响应外部API(假设一个标称响应)。我认为您的意思是将其排除在单元测试和集成测试之外,但是如果查询对任务至关重要,我们可以单独设置其他一些自动化测试来测试实时外部api。
Joshua Coady

1
IMO,最好的方法是进行功能测试,where子句中永远不会成立的更改将在我的一个或多个功能测试中引起不同的行为。UT只是测试计划中的一小部分。
Laiv

2
感谢您担任共鸣。我猜想最终,即使查询中包含自定义逻辑,它也超出了单元测试的范围,因为查询是在被测试系统之外“执行”的代码。我只需要一个人在看到它之前用不同的方式告诉我多次;)
Joshua Coady

2

我已经看到了单元检查,该检查检查所生成的查询字符串是否与期望值匹配。

然而。我认为这是有限使用。查询语法很复杂,可能有错误,因此A即使有“正确”生成的字符串,也有无限的检查可能,而B则可以在实时环境中返回意外结果。

我认为您选择选项2是正确的。针对实时实例运行集成测试。

只要它们是非破坏性的,这些是您应该编写的第一个测试,因为它们将捕获(尽管不能确定)任何错误的原因。

选择选项3“使用虚拟数据部署测试实例”是更好的选择。但这不会影响您的测试编写,因为您可以将相同的测试指向测试服务器,前提是它可以很好地利用时间来部署一个测试。


0

它取决于API,但如果可能,请选择选项3(私有测试实例)。

由于您提到的原因,对API(选项#1)进行存根是最糟糕的选择,而走这条路线可能弊大于利(浪费大量时间)。

针对真实的API(选项#2)运行会使测试不稳定且不可靠,并且在出现一些误报之后,人们将停止使用它们。不仅数据会更改,而且服务可能也会关闭。在我看来,这类似于没有针对查询的测试,而是依靠集成/系统测试来发现问题。也就是说,如果API数据很少更改,并且API本身几乎总是处于运行状态,那么这可能是一个可行的选择。大多数API不符合此描述。

最终归结为这些查询的重要性和复杂性:如果有几个查询,而且其中一些查询足够复杂,以至于您需要测试它们,我将投入精力来建立一个专用实例进行测试。它将像其他单元测试一样为自己付出代价。


因此,基本上,您是在说单元测试(#1)和集成测试(#2)有害吗?尽管#3似乎是其中最好的,但它也可能是最昂贵的。每次API更改时都必须对其进行维护。没有#2,您将无法知道真正的API中可能存在的错误-更改,直到您的应用程序投入生产为止(为时已晚,无法采取措施)。好吧,#1似乎没用,因为没有代码行可以测试……今天……明天,怎么知道……
Laiv

我说不好的测试肯定是有害的。不稳定的测试浪费了大量的时间和精力,并导致人们对整个单元测试失去信心。那些因实现更改而中断的测试(第1个)或仅在数据更改时随机发生的测试(第2个)不是很好的测试。
Michal Tenenberg

集成测试不测试数据。而已。他们无法破坏测试,只需验证集成即可。测试不是信仰问题,而是拥有良好的习惯,可以为应用程序增加价值
-Laiv
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.