在编程中,"to stub" 是什么意思?

176

例如,在这个引用中,“stub”是什么意思?

与外部API集成几乎是任何现代Web应用的必备要素。为了有效地测试此类集成,您需要存根它。一个好的存根应该易于创建,并且始终保持最新的API响应。在本文中,我们将概述使用存根进行外部API测试的策略。


11
你看过What is a "Stub"?中被接受的答案了吗? - Nick
11个回答

148

存根是系统中某个现有依赖项(或协作者)可控的替代品。使用存根,您可以在不直接处理依赖项的情况下测试您的代码。

外部依赖项 - 现有依赖项:
它是您的测试代码与之交互但您无法控制的系统中的对象。(常见示例包括文件系统、线程、内存、时间等。)

例如,在以下代码中:

public void Analyze(string filename)
    {
        if(filename.Length>8)
        {
            try
            {
                errorService.LogError("long file entered named:" + filename);
            }
            catch (Exception e)
            {
                mailService.SendEMail("admin@hotmail.com", "ErrorOnWebService", "someerror");
            }
        }
    }

如果您想测试mailService.SendEMail()方法,但需要在测试方法中模拟Exception,那么您只需要创建一个伪造的存根errorService对象以模拟所需结果,然后您的测试代码就能够测试mailService.SendEMail()方法。正如您所看到的,您需要模拟来自另一个依赖项ErrorService类对象(现有依赖项对象)的结果。


15
现有的依赖是什么?它指的是一个软件或系统中需要其他软件或组件来正常运行的情况。 - Jwan622
21
laymen terms: 代码使用的所有东西。如果你将“依赖项”替换为“类”、“函数”或其他内容(取决于您的背景),可能会有所帮助。有时,使用现有的类/函数并不是可行的选择,您需要一个存根(例如,在依赖系统环境(如当前日期和时间)的函数进行自动化单元测试时)。 - MasterMastic

132
在这个上下文中,stub 意味着一个模拟实现。也就是说,它是一个简单的、虚假的实现,符合接口规范,用于测试。

3
更多细节请参考马丁·福勒(Martin Fowler)的著名文章《模拟不是存根(Mocks Aren't Stubs)》: “但是我经常看到人们对模拟对象的描述不太准确。特别是我经常看到它们被误解为一个常见的测试环境帮手——存根。” - pba

82

通俗来说,它是虚假数据(或伪数据,测试数据...等),你可以用它来测试或开发你的代码,直到你(或其他人)准备好呈现/接收真实数据。这是程序员的“Lorem Ipsum”。

员工数据库没有准备好?用Jane Doe、John Doe等简单的虚构人名来创建一个。 API还未准备好?通过创建一个包含虚假数据的静态 .json 文件来虚构一个。


2
谢谢您的示例 :) - user5228393

18
在这个上下文中,“stub”一词被用来代替“mock”,但为了清晰和精确,作者应该使用“mock”,因为“mock”是一种用于测试的 stub。为了避免进一步混淆,我们需要定义 stub 是什么。
在一般情况下,stub 是一个程序片段(通常是函数或对象),它封装了调用另一个程序的复杂性(通常位于另一台机器、虚拟机或进程上,但不总是,它也可以是本地对象)。由于要调用的实际程序通常不位于同一内存空间,所以调用它需要许多操作,例如 addressing、执行实际的远程调用、将要传递的数据/参数进行 marshalling/serializing(并且与潜在结果相同),甚至处理身份验证/安全等等。请注意,在某些情况下,stubs 也被称为代理(例如 Java 中的动态代理)。
mock 是一种非常特定和限制性的 stub,因为 mock 是用于测试的另一个函数或对象的替代品。在实践中,我们经常使用 mocks 作为本地程序(函数或对象)来替换测试环境中的远程程序。在任何情况下,mock 可以模拟被替换程序的实际行为在受限的上下文中。
最著名的存根类型显然是针对分布式编程的,当需要调用远程过程(RPC)或远程对象(RMICORBA)时。大多数分布式编程框架/库都自动化生成存根,使您不必手动编写。存根可以从接口定义生成,例如使用IDL编写(但也可以使用任何语言定义接口)。
通常,在RPC、RMI、CORBA等中,人们区分客户端存根服务器端存根,客户端存根主要负责编组/序列化参数并执行远程调用,而服务器端存根主要负责解组/反序列化参数并实际执行远程函数/方法。显然,客户端存根位于客户端,而服务器存根(通常称为骨架)位于服务器端。
写出高效、通用的存根在处理对象引用时变得非常具有挑战性。大多数分布式对象框架例如RMI和CORBA处理分布式对象引用,但是在REST环境中,这是大多数程序员避免的。通常,在REST环境中,JavaScript程序员制作简单的存根函数来封装AJAX调用(对象序列化由JSON.parse和JSON.stringify支持)。Swagger Codegen项目提供了广泛的支持,可以自动生成各种语言的REST存根。

10

桩函数是具有正确函数名称、正确参数数量和生成正确类型的虚拟结果的函数定义。

它有助于编写测试,并充当一种脚手架,使得在函数设计完成之前就可以运行示例。


5
一个存根可以被称为原始函数的虚假替代品,它会提供输出,但由于以下原因目前无法访问:
  • 目前未开发
  • 当前环境中不可调用(可能是测试)

一个存根有:

  • 确切数量的参数
  • 确切的输出格式(不一定是正确的输出)

为什么要使用存根?
当函数在环境中不可访问,比如测试时,或者实现不可用时。

例如:
假设您想测试一个包含网络调用的函数。在测试代码时,您不能等待网络调用的结果进行测试。因此,您编写了网络调用的模拟输出,并继续进行测试。

TestFunction(){
  // Some things here

  // Some things here

  var result = networkCall(param)

  // something depending on the result
}

这个 networkCall 返回一个字符串,因此您需要创建一个具有完全相同参数的函数,并且它应该返回字符串输出。
String fakeNetworkCall(int param){
  if(param == 1) return "OK";
  else return "NOT OK";
}

现在,您已经编写了一个虚假函数,请将其用作代码中的替代品。
TestFunction(){
  // Some things here

  // Some things here

  var result = fakeNetworkCall(param)

  // something depending on the result
}

这个fakeNetworkCall是一个存根。

5
这个短语几乎肯定是与房屋建设中的一种阶段“stubbing out” plumbing有关。在施工期间,当墙壁还未封闭时,会安装粗略的管道。这对于建筑的继续进行是必要的。然后,当周围的一切都准备好了,人们回来添加水龙头、马桶和实际的最终产品(例如参见如何安装管道Stub-Out)。
在编程中,“stub out”一个函数时,您需要构建足够的功能以便能够测试或编写其他代码。然后,稍后再用完整的实现替换它。

3

RPC存根

  • 基本上,客户端存根是一个过程,对客户端而言,它看起来像一个可调用的服务器过程。
  • 服务器端存根对服务器而言看起来像一个调用的客户端。
  • 客户端程序认为自己在调用服务器;实际上,它在调用客户端存根。
  • 服务器程序认为自己被客户端调用;实际上,它被服务器存根调用。
  • 存根之间发送消息以进行RPC操作。

来源


3
"将函数桩起来意味着你仅编写足够的内容以显示函数已被调用,留下细节需要在以后有更多时间再来完成。"
来源:SAMS Teach yourself C++, Jesse Liberty and Bradley Jones

3

你还有很好的测试框架可以创建这样的存根。 我最喜欢的之一是Mockito。 还有EasyMock和其他... 但Mockito非常好,你应该阅读它-非常优雅和强大的包。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接