测试用例设计之用Go语言编写单元测试
小坐标 2018-03-08 来源 : 阅读 1050 评论 0

摘要:本篇软件测试教程将为大家讲解软件测试的知识点,看完这篇文章会让你对软件测试的知识点有更加清晰的理解和运用。

本篇软件测试教程将为大家讲解软件测试的知识点,看完这篇文章会让你对软件测试的知识点有更加清晰的理解和运用。

 大多数开发者都不知道如何设计测试用例

  每个开发者都知道我们需要写单元测试,这是为了防止出现线上问题。

  大多数开发者不知道每个单元测试应有的主要内容,失败的单元测试更是屡见不鲜,到底该如何正确地写一个成功的单元测试呢?小坐标会告诉你。

  为什么要认真对待测试准则?

  你的测试是你防御软件缺陷的第一道和最好的一道防线。你的测试比代码提示和静态分析更重要(提示和静态分析只能找到语言本身的一部分错误,不能找到你真正的程序逻辑中的错误)。测试与代码实现同样重要(最要紧的是代码符合需求,至于它是如何实现的不那么要紧,除非它用了一个糟糕的实现方案)。

  好的单元测试包含了许多特征,它是使你开发的产品走向成功的秘密武器:

  1.有助于设计:写单元测试首先给了你一个如何设计 API 的清晰视角。

  2.特性文档(为开发人员准备的):单元测试描述和记录了代码所要实现的所有需求。

  3.检查开发者需求理解程度:是否开发者足够理解问题,在测试代码里描述了所有的关键需求点?

  4.QA(质量保证):依靠人工 QA 容易出错。根据我的经验,让一个开发者记得要测试所有的特性,在代码改变后回归测试所有的功能以及新增或移除的功能,是一件不可能的事情。

  5.有助于持续交付:自动化的 QA 能够自动防止将有缺陷的产品构建并发布上线。

单元测试不用刻意去改变什么来迎合这些广泛的目标。而本质上一个好的测试集自然地满足所有这些需求,它们都是编写良好且覆盖率高的单元测试所带来的附加好处。

  测试驱动开发(TDD)是科学的

  有证据表明:

    • 测试驱动开发可以减少 bug 密度。

    • 测试驱动开发可以促进模块化设计。(提升软件敏捷性/团队速度)

    • 测试驱动开发可以减少代码复杂度。

  科学地说:重要的实践经验证明测试驱动开发确实有效。

  测试优先

  微软研究院、IBM 和 Springer 对先写测试再开发和先开发后写测试的有效性对比研究表明,测试优先比先开发再写测试的结果更好。因此,如何选择不言而喻:在你实现代码前,先写测试。

  在实现功能之前, 先写测试。

  什么才是好单元测试?

  好吧,测试驱动开发是有效的。开发前,测试优先。遵守准则,信任测试过程……我们明白了。但问题是,怎样写一个好的单元测试?

  接下来我们通过一个非常简单的例子来探索单元测试过程,这个例子来源于一个真实的项目:Stamp Specification 的 compose() 函数。

  我们将使用 tape 来实现测试,因为 tape 如丝般顺滑,本质上简洁而优雅。

  在我们回答如何写一个好的单元测试之前,首先我们需要明白单元测试怎么用:

    • 有助于设计:在设计阶段写,优先于开发实现。

    • 文档特性与检查开发者需求理解程度:测试需要提供一份对被测试特性的清晰的描述。

    • QA/持续交付:测试不通过时要能够停止交付流程并提供一份良好的 bug report。

  单元测试作为 Bug Report

  测试不通过时,测试失败报告通常是你的第一个和最好的线索,你通过它发现问题所在——快速追踪到本质问题的诀窍是懂得从那里开始查。当你手里有一份非常清晰的测试报告时,这个查 bug 的过程变得简单多了。

  一个测试失败的用例信息读起来

  要像一个高质量的 bug report

   

  什么是一个好的测试失败报告?

  1.你测试的是什么?

  2.它是做什么的?

  3.它实际输出了什么(实际行为)?

  4.它本该输出什么(预期的行为)?

测试用例设计之用Go语言编写单元测试


  一份好的测试失败报告的例子。

  开始于回答“我在测试什么?”:

    • 你在测试哪个组件切面(component aspect)?

    • 这个特性做什么用?你测试的具体行为需求是什么?

  compose() 函数接受任何数量的 stamp (可组合的工厂函数),然后生成一个新的 stamp。

  要实现这个测试,我们从任何单个测试用例的最终目的开始反向思考:最终目的是测试特定的行为是否符合需求,为了让这个测试通过,代码需要有怎样的特定行为?

  要测试的特性是做什么的?

  我喜欢从写一个字符串开始。这个字符串并不赋给任何变量,也不传递给任何函数。它只是描述待测试组件必须满足的具体需求的明确焦点。在这个例子里,我们从 compose() 函数必须返回一个函数开始。

一个简单的,可测试的需求:

  

"compose() should return a function."

  现在,我们将暂时跳过它,充实测试的其余部分。这个字符串将作为我们的目标,事先声明它有助于我们聚焦到目标上来。

  我们在测试哪个组件切面?

  这里说的“组件切面”在具体的每个测试中是不同的,它的划分取决于对被测试的组件提供足够的覆盖率所要求的粒度。

  在这个例子里,我们要测试 compose 函数的返回值类型来确保它返回的是正确的,而不是 undefined 或者因为调用过程中出错而不返回任何内容。

  让我们将这个问题写成测试代码。答案在测试描述中。测试描述这一步也定义了我们的函数调用,并将它作为回调函数传给 test runner,以便于 test runner 在测试被运行的时候调用它:

  

    test("<What   component aspect are we testing?>", assert => {
  });

  在这个例子里,我们将要测试的是 compose 函数的输出:

  test("Compose function output type.", assert => {
  });

  当然,我们仍然需要我们前面写下的那个问题,我们让它出现在回调函数的里面:

  test("Compose function output type.", assert => {
    "compose() should return a function."
  });

   

  返回值是什么(期望的和实际的)?

  equal() 是我最喜欢的一个断言(Assertion)。如果只允许在每一个测试集中使用 equal() 断言,那几乎世界上所有的测试集都会变得更好,为什么呢?

  因为 equal() 自然地回答了每一个单元测试必须要回答但是大多数情况下没有回答的两个最重要的问题:

    • 它实际的输出是什么?

    • 它预期的输出是什么?

  如果你测试完成却没有回答上面的两个问题,那你并没有真正做到单元测试。你只做了草率的,不完全的测试。

  如果你从这篇文章中只学到一样东西,那么我希望是:

  Equal 是你应该默认使用的断言。

  它是每个好测试集的主菜。

  种类繁多的断言库包含各式各样的断言,滥用它们会毁了你的测试的质量。

  一个挑战

  想要在单元测试上做得更好?在下一周里,尝试尽量对每一个断言使用 equal() 或 deepEqual(),如果你选择的断言库不是 tape,你也要选择你的断言库中等同于 equal() 或 deepEqual() 的断言。别担心影响你的测试集的质量,我打赌这个练习会显著地改善测试集的质量。

  下面的代码看起来如何?

    const actual = "<what is the actual   output?>";
    const expected = "<what is the expected   output?>";

   

  第一个问题在一个测试失败的时候真正承担双重职责。通过回答它(代码的实际输出是什么),你的代码也同时回答了另一个问题(测试结果如何复现):    

const actual = "<how is the test reproduced?>";

  非常重要的一点是,actual 值必须由运行那些组件公共的 API 产生。否则,测试不应该返回值。我见过一些测试集中充满了模拟的、历史遗留的、不应存在的测试,其中一些测试从不测任何应该被测试的实际代码。

  让我们回到例子:

    const actual = typeof compose();

    const expected = "function";

   

  你可以创建一个断言,不用像我这样特意将结果赋给 actual 和 expect 变量。但我最近开始在每一个测试中特意赋值给这两个变量,我发现这么做使得我的测试更容易读懂。

  看它如何阐明断言?

    assert.equal(actual, expected,
      "compose() should return a function.");

   

  这么写将“如何做”和“是什么”从测试主体里分开了。

    • 想要知道我们如何得到的结果?,看变量赋值。

    • 想要知道我们测试的是什么?,看断言描述。

  最后的结果便是,测试本身读起来就像是在读一个高质量的 bug report 一样简单。

  让我们看一下完整代码:

  import test from "tape";
  import compose from "../source/compose";
  test("Compose function output type", assert => {
    const actual = typeof compose();
    const expected = "function";
    assert.equal(actual, expected,
      "compose() should return a function.");
    assert.end();
  });

   

  下回你再写一个测试,记得回答下面所有的问题:

  1.你测试的是什么

  2.它是做什么的?

  3.它实际输出了什么(实际行为)?

  4.它本该输出什么(预期的行为)?

  5.测试结果如何复现?

  最后一个问题是通过得到 actual 值的代码回答的。

  单元测试模板:

  import test from "tape";
  // For each unit test you write,
  // answer these questions:
  test("What component aspect are you testing?", assert   => {
    const actual = "What is the actual output?";
    const expected = "What is the expected output?";
    assert.equal(actual, expected,
      "What should the feature do?");
    assert.end();
  });

   

  以上,关于软件测试的全部内容讲解完毕啦,欢迎大家继续关注!更多关于软件测试的干货请关注职坐标软件测试频道!希望这篇软件测试文章可以帮助到你。总之,同学们,你想要的职坐标软件测试频道都能找到!
本文由职坐标整理并发布,希望对同学们学习软件测试的知识有所帮助。了解更多详情请关注职坐标软件测试频道!

本文由 @小坐标 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程