单元测试探析:什么是Stubs、Mocks、Spies、Dummies?带你了解4个核心工具

这篇具有很好参考价值的文章主要介绍了单元测试探析:什么是Stubs、Mocks、Spies、Dummies?带你了解4个核心工具。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

在单元测试中,对象之间的依赖往往交织到一起,需要拆成各个单元才能逐个击破,这也是单元测试的目的。如何将这些交织到一起的对象拆开,需要一些工具,这些工具业内人们称其为“测试替身”。

本文作者介绍了单元测试中的4个“测试替身”工具,即Stubs、Mocks,、Spies 和 Dummies。

Stubs 为被测试对象提供数据,没有任何行为,往往是测试对象依赖关系的上游。

Spies 被依赖对象的代理,行为往往由被代理的真实对象提供,代理的目的是为了断言程序运行的正确性。

Mocks 模拟一个具有特性行为的对象,在测试开始前根据期望提供需要的结果。被测试对象往往调用这个对象的方法时,根据条件得到不同的输入,从而满足测试对象的不同场景。例如,mock 数据库的存储层,返回正常数据、空或者丢出异常等情况。

Dummy 被用来仅仅作为填充参数列表的对象,实际上不会用到它们,对测试结果也没有任何影响。

以下为作者观点。

你可能讨厌或喜欢单元测试,这取决于你,但事实是,如果你不理解它们背后的概念,你写测试的效率可能就会弄得一团糟。

要成为写单元测试的高手,第一个核心步骤是了解其重点。单元测试不是集成测试,它们必须测试单一的代码单元。

让我们来看看在写单元测试时要用到的4个工具。我指的不是IDE或任何插件或扩展,我指的是概念性的工具:stubs、mocks、spies、 dummies。

什么是Stubs?

我经常看到开发人员通过启动一个“测试数据库”来编写与数据库交互的代码的测试,其中测试可以触发“写入”并通过查询数据库进行验证,我认为这是错误的。

Stubs可以帮助你处理这些情况,即你的代码与第三方服务进行交互。无论是数据库、API还是硬盘上的文件,stubs都提供了使用更简单版本的服务的代码。

这个Stub会返回一个已知的、可控的值。例如,如果你正在测试一个向数据库写值的函数,你应该编写一个Stub,避免与数据库的交互,但返回一个成功的结果。

通过这个,你就可以测试当写入操作工作时发生了什么。然后你可以编写另一个Stub(在另一个测试中),返回一个失败的结果,这样你就可以测试你的逻辑中发生处理错误的部分。

你可以在一个特定的对象中Stub一个函数或一个方法(只要语言允许)。

因此,让我们快速看一个例子:

/// the function to test
function saveUser(usrData, dbConn) {
 
  let q = createQueryFromUser(usrData)
  let result = dbConn.query(q)
 
  return result;
}


//the stub
makeStub(dbConn, 'query', () => {
  return true;
})

//the test
it("should return TRUE when the query succeeds", () => {
  let result = saveUser({
    name: "Fernando",
    password: "1234"
  }, dbConn)
  result.should.be.true
})

上面的例子有几个地方需要解读,同时注意到,虽然这个例子是用伪JavaScript写的,但其概念可以推导到所有语言。

首先是要测试的函数,现在它是一个接收数据的简单函数,一个数据库连接对象,并依靠一个伪createQueryFromUser函数来创建实际的SQL查询。来自dbConn对象的query方法是与数据库交互的方法,也是我们有兴趣Stub的方法,因为我们不希望query真正启动。

这里是Stub发挥作用的地方,makeStub函数负责用我们传递的匿名函数(这是一个伪函数,每次只返回TRUE)神奇地覆盖数据库连接的方法query。

最后,实际的单元测试是利用Stub(因为它之前就被定义了)。这个测试确保我们的函数在进展顺利时返回正确的布尔值(boolean value)。

上面只是一个例子,告诉你可以从Stubs中受益。说实话,在任何时候,如果你有一个具有动态结果的函数,你就必须找到一种方法来确保每次执行测试时都有相同的结果。所以,Stubs可以帮到你。

什么是Mocks?

Mocks就像Stubs的孪生兄弟,它们看起来很像,人们经常把它们混淆,其实它们两个完全不同。

当Stubs允许你替换或重新定义一个函数或方法时,Mocks允许你在真实的对象/函数上设置预期行为。因此,从技术上讲,你并没有替换对象或函数,你只是告诉它在某些非常特殊的情况下该做什么,除此之外,对象仍然照常工作。

让我们看一个例子来理解这个定义:想象一下,要测试一个过道补货功能。它从库存中提取物品,并把它们放在正确的过道上。这里测试的关键是,每次我们补充一个过道时,也需要从库存中取出相同数量的元素。

var inventory = createMock(Inventory("groceries"))
//set expectations
inventory.expect("getItems", 10).returns(TRUE).expect("removeFromInventory", 10).returns(TRUE)

var aisle = Aisle("groceries")
aisle.replenish(10, inventory) //executes the normal flow
assertion(aisle.isFull(), "equals to", TRUE)

请记住,在某些情况下,mocks的预期行为会被你所使用的框架自动检查。这就是为什么没有真正的断言来处理期望值的原因,如果它们没有被满足,模拟就会抛出一个异常,测试就不会通过。

在这个特殊的例子中,预期getItems方法将被调用,其属性为10,它将返回TRUE,它也将调用removeFromInventory函数,其属性也是10。最后返回的结果是TRUE。

当然,我们可以用Stubs来完成这个任务,但这不是重点,在许多情况下,这些工具可以用于相同或类似的用例。

Spies到底是什么?

顾名思义,Spies可以让我们了解被测试代码内部发生了什么,即使我们并没有真正访问到它。我知道,这听起来很诡异,但它有它的用途。

换句话说,Spies是收集执行信息的Stubs,因此他们最终可以告诉你调用了什么、何时调用了哪些参数。

想想上面mocks的例子,我们必须事先设定期望值(预期),以确保我们想要的东西都会被执行。我们可以通过 "监视 "库存来检查同样的事情,并询问这些方法是否真的被调用了,用了哪些参数。

我们来看看另一个例子,一个文件读取器函数,一旦它完成了文件处理,也应该关闭文件处理程序。

const filename = "yourfile.txt"
let myspy = new Spy(IOModule, "closeFile") //create a spy for the method closeFile in the module dedicated to I/

function readConfigFile(fname) {
 const reader = new FileReader(filename, IOModule)
 let content = reader.read()
 loadConfig(content)
 IOModule.closeFile(reader);
}
 
 
//The test

it("should call the 'closeFile' method after reading the content of the file", () => {
  readConfigFile(filename)
  assertion(myspy.called, "equals to", TRUE)  
})

要测试的函数叫做readConfigFile,它的目的是读取一个文件,并通过调用loadConfig方法将其内容加载为配置。作为测试的一部分,我们有兴趣了解该函数是否真的关闭了文件处理程序。

请记住,这个测试与我上面所说的相反,因为它实际上是在打开和读取文件,这是我们单元测试不应该有的第三方依赖。为了使这个测试完全 “合规”,当我们有兴趣测试成功的读取和失败的读取时,我们还必须为I0Module和控件添加一个stub。

注意:与stubs不同的是,Spies包装目标方法/函数,而不是替换它,因此目标的原始代码也将被执行。

什么是 dummies?

最后,我想介绍的最后一个工具是众多周知的无用的 “dummies”。顾名思义,除了在需要的时候出现之外,没有其他真正的用途。它们的目的是在语法需要时出现在那里。

例如,想象一下必须调用一个需要3个参数的函数,其中第一个参数是另一个函数(外部依赖)。考虑到该函数当前的stub,你知道其他两个属性不会被使用,然而,解释器/编译器正在抱怨你缺少该函数的最后两个属性,所以你需要添加它们。

你怎么能做到这一点呢?

你猜对了,通过dummies。你只需添加2个什么都不做但被编译器接受的dummy对象。

Dummies在强类型语言中使用时更有意义,因为这些类型的检查在那里更常见。例如,看看下面这个TypeScript的例子:

type UserData = {
  name: string;
  password: string
}

//The function to be tested
function saveUser(usrData: UserData, dbConn: DataBase, validators:DataValidators) {
 
  if(!validators.validateUserData(usrData)) {
    return false;
  }
  let query = createQueryFromData(usrData);
  let result = dbConn.query(query);
  return result;
}

// The test itself

//the stub
const stubbedValidators: DataValidators = {
  validateUserData: (data: UserData) => false;
}

//the dummies
const userData: UserData = {name: "", password: ""}
const dbConn: DataBase = {}

//the test
it("should return false if the user data is not valid", () => {
  let result = saveUser(userData, dbConn, stubbedValidators);
  result.should.be.false;
})

该代码定义了一个新的saveUser函数,该函数也需要一个validators依赖。我们还添加了一个验证步骤,以确保我们试图保存的数据是 “有效的”(不管这意味着什么)。

但我们测试的目的是确保如果数据无效,我们将返回false。这意味着我们没有真正执行任何验证,事实上,我们需要stub那个验证器来控制结果,否则如果明天我们的验证例程发生变化,我们现在可能会传递一个有效的数据样本,测试就会失败。

现在的问题是,通过查看我们的业务逻辑,如果数据是无效的,我们并没有真正使用数据库连接,也没有实际的用户数据。我们需要它们在那里,但我们并不真正需要它们。所以他们实际上已经变成了dummies。

这就是为什么我只是传递假的空对象(A.K.A dummies)作为函数的前两个属性。

Stubs, Mocks, Spies和Dummies是你在测试中所做的一切的面包和黄油,你越是使用它们,就越是感觉熟悉,你就越容易理解如何处理一个新的测试。

例子是否足够清楚?你对这些例子还有疑问吗?请留下评论,我们一起讨论!


资源分享

下方这份完整的软件测试视频学习教程已经上传CSDN官方认证的二维码,朋友们如果需要可以自行免费领取 【保证100%免费】

单元测试 stub,软件测试,职场经验,IT,单元测试,软件测试,程序人生,自动化测试,职场和发展

单元测试 stub,软件测试,职场经验,IT,单元测试,软件测试,程序人生,自动化测试,职场和发展文章来源地址https://www.toymoban.com/news/detail-572267.html

到了这里,关于单元测试探析:什么是Stubs、Mocks、Spies、Dummies?带你了解4个核心工具的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【软件测试】单元测试、集成测试、系统测试有什么区别?

    1、粒度不同 集成测试bai粒度居中,单元测试粒度最小,系统du测试粒度最大。 2、测试方式不同 集成测试一般由开发zhi小组采用白盒加黑盒的方式来测试,单元测试一般由开发小组采用白盒方式来测试,系统测试一般由独立测试小组采用黑盒方式来测试。 3、测试内容不同

    2024年02月09日
    浏览(50)
  • 为什么需要单元测试?

    为什么需要单元测试? 从产品角度而言,常规的功能测试、系统测试都是站在产品局部或全局功能进行测试,能够很好地与用户的需要相结合,但是缺乏了对产品研发细节(特别是代码细节的理解)。 从测试人员角度而言,功能测试和系统测试以及其他性能测试等等对测试

    2024年02月12日
    浏览(69)
  • 单元测试到底是什么?应该怎么做?

    单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。至于“单元”的大小或范围,并没有一个明确的标准,“单元”可以是一个函数、方法、类、功能模块或者子系统。 单元测试通常和白盒测试联系到一起, 如果单从概念上来讲两者是有区别的,不过

    2024年02月08日
    浏览(49)
  • 【JUnit技术专题】「入门到精通系列」手把手+零基础带你玩转单元测试,让你的代码更加“强壮”(实战开发篇)

    本节内容主要介绍JUnit单元测试功能框架,并以实战演练的形式进行讲解。本系列教程主要针对代码编程方式和模型,重点讲解实战代码开发。通过本系列教程的学习,您将能够深入了解JUnit单元测试框架的使用和原理,并掌握如何在实际项目中运用JUnit进行单元测试。 以下是

    2024年02月03日
    浏览(83)
  • 一文带你全面了解什么是自动化测试?

    目录 简介 自动化测试概述 自动化测试目标 自动化测试流程 1. 测试计划和设计 2. 测试脚本开发 3. 测试执行和管理 4. 测试维护和优化 自动化测试最佳实践 自动化测试工具和框架 结论 软件测试是软件开发过程中一个必不可少的环节。传统的软件测试方式通常是手动测试,即

    2024年02月16日
    浏览(44)
  • 什么是软件测试?5分钟带你快速了解!

    经常有人问我,你的公司是做什么的?我回答“软件测试”,看着对方一脸的迷茫。何为软件测试?软件测试究竟测试什么? 软件测试是伴随着软件工程的重要组成部分,是软件质量保证的重要前提。软件测试是为了尽快尽早地发现在软件产品中所存在的各种软件缺陷而展开

    2024年02月08日
    浏览(52)
  • 单元测试(unit testing)到底是什么?

    做开发的同学应该都听说过单元测试(unit testing),因为对单元测试的理解程度不同,所以对单元测试的看法也可能有所不同。本文就来深入讲解一下单元测试的概念、作用和是否需要做单元测试。 单元测试(unit testing)是软件开发中的一种最低级别的测试方式,对软件中的

    2024年02月08日
    浏览(55)
  • Junit4入门之什么是单元测试?

    干了一年多的后端了,从来没有了解过 单元测试 。虽然我知道测试不仅仅是测试们的任务,后端也要进行自测来保证自己的代码的可用性,但我一直都只是用postman来实施的,调用调通了即可。虽然我也知道Junit是用于测试的软件,项目里也引入过,但真的就只是点到为止,

    2024年03月18日
    浏览(74)
  • 【一文清晰】单元测试到底是什么?应该怎么做?

    我是java程序员出身,后来因为工作原因转到到了测试开发岗位。测试开发工作很多年后,现在是一名自由职业者 1、什么是单元测试 2、该怎么做单元测试 单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。至于“单元”的大小或范围,并没有一个明确

    2024年02月07日
    浏览(51)
  • 3分钟带你了解,软件测试是做什么的

    软件测试是互联网技术中一门重要的学科,它是软件生命周期中不可或缺的一个环节,担负着把控、监督软件的质量的重任。 目前,软件测试工程师缺口达30万,其中在我国大中型发达城市的人才需求就突破20万,并以每年20%的速度递增。人才稀缺自然带来待遇高涨。在某软

    2024年01月22日
    浏览(51)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包