单元测试API客户端和包装器


14

我一直在兜圈子,试图找出对我正在开发的API客户端库进行单元测试的最佳方法。该库具有一个Client基本上与API进行1:1映射的Wrapper类,以及一个额外的类,该类在的顶部提供了更加用户友好的界面Client

Wrapper --> Client --> External API

我首先针对Client和编写了一系列测试Wrapper,实际上只是测试它们是否转发到操作的任何适当功能(Wrapper在上操作Client,并Client在HTTP连接上操作)。但是,我开始对此感到不舒服,因为我感觉自己正在测试这些类的实现,而不是接口。从理论上讲,我可以将这些类更改为具有另一个完全有效的实现,但是我的测试将失败,因为未调用我希望调用的函数。对我来说,这听起来像是脆弱的考验。

之后,我考虑了类的接口。测试应该验证类是否确实完成了其应做的工作,而不是他们是如何完成的。那我该怎么做呢?首先想到的是对外部API请求进行存根。但是,我担心过度简化外部服务。我见过的很多存根API的示例都给出了罐头响应,这听起来像是一种非常简单的方法,可以仅测试您的代码是否针对假API正确运行。另一种方法是模拟服务,这是不可行的,并且每当真实服务发生变化时就需要保持最新状态-感觉像是过分杀伤和浪费时间。

最后,我从程序员SE的另一个答案中读取了此内容:

远程API客户端的工作是发出某些调用-不多也不少。因此,它的测试应验证它是否发出了这些呼叫-不多也不少。

现在,我或多或少地确信-测试时Client,我需要测试的是它向API发出了正确的请求(当然,总有API会更改但我的测试继续通过的可能性-但这就是集成测试很有用的地方)。由于Client与API只是1:1的映射,因此在将一个有效实现更改为另一个之前,我的担心并没有真正适用-的每种方法只有一个有效实现Client

但是,我仍然坚持Wrapper上课。我看到以下选项:

  1. 我对Client类进行存根处理,然后测试是否调用了适当的方法。这样,我做的与上述相同,但是将其Client作为API的替代。这使我回到了起点。再一次,这给了我测试实现而不是界面的不舒服的感觉。将Wrapper很可能使用了完全不同的客户端实现。

  2. 我创建一个模拟Client。现在,我必须决定对它进行模拟的程度-创建服务的完整模拟将花费很多精力(比库本身要付出更多的工作)。API本身很简单,但是服务却很复杂(本质上是一个对数据进行操作的数据存储)。再说一次,我将必须使模拟与real保持同步Client

  3. 我只是测试是否正在发出适当的HTTP请求。这意味着Wrapper将通过一个真实的Client对象进行调用以发出这些HTTP请求,因此我并不是在单独测试它。这使得它有点可怕的单元测试。

因此,我对这些解决方案都不满意。你会怎么做?有正确的方法去做吗?


在这些情况下,我倾向于避免进行单元测试,因为在这种情况下,第三方库承担了大部分工作,而我只有一个包装器(主要是因为我不知道如何以测试任何真正有意义的方式进行此操作)。通常,在这种情况下,我会使用模拟服务进行集成测试。也许有人知道如何对这些进行真正有意义的单元测试-我倾向于优先考虑在我们控制下的系统中最关键的组件。这里的关键部分是我们无法控制的。:-(

Answers:


10

TLDR:尽管有困难,但是您应该对服务进行存根并将其用于客户端单元测试。


我不确定“远程API客户端的工作是发出某些调用,不多也不少...”,除非API仅由始终返回固定状态且不消耗也不产生的端点组成任何数据。这不是最有用的API ...

您还需要检查客户端不仅发送正确的请求,还可以正确处理响应内容,错误,重定向等。并测试所有这些情况。

如您所述,您应该具有集成测试,该测试涵盖了包装器->客户端->服务->数据库及以后的整个堆栈,但是要回答您的主要问题,除非您有一个可以在每个测试中运行集成测试的环境CI的构建没有很多麻烦(共享的测试数据库等),您应该花时间在创建API的存根上。

存根将使您可以创建服务的有效实现,而无需在服务本身之下实现任何层。

您可以考虑使用基于DI的解决方案来实现此目的,并在REST资源下方实现存储库模式的实现:

  • 用对IWhateverRepository的调用替换REST处理程序中的所有功能代码。
  • 使用从REST资源中提取的代码创建一个ProductionWhateverRepository,并创建一个TestWhateverRespository,它返回罐装响应供单元测试期间使用。
  • 根据配置,使用DI容器注入ProductionWhateverRepository或TestWhateverRepository DLL /类等。

无论如何,除非在政治上或实践上都无法进行存根和/或重构服务,否则我可能会采取与上述类似的操作。如果不可能的话,那么我将确保具有很好的集成覆盖范围,并在给定可用测试设置的情况下尽可能多地运行它们。

高温超导

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.