发布新日志

  • 自动化测试哪种编程语言更好?

    2019-02-03 14:10:38

      软件测试工程师都有一门属于自己的工作语言。有的喜欢用Python,有的擅长用Java,有的偏爱于Go。 而其中对于选择用Python作为工作语言的测试工程师来说,它的优点不仅仅是简单而已 ~
      一:Python
      1、Python语法简单
      Python的语法非常简单,更适合初学编程者,这也是为什么那么多半路转行的人都选择 Python入门编程的原因。以前麻省理工的《编程导论》这门课程用的Schemer语言,这几年改成了Python引来很多大佬吐槽。
      他们的理由是: Python 太简单了,根本不能将那些不适合从事编程工作的人拒之门外。
      2、一年比一年火爆
      Python在社区和商业上都非常活跃,会有越来越多的人使用这么语言,意味着有什么问题,你能更容易的找到答案。
      最近微软在考虑将他们的官方脚本语言换成Python,连这头封闭出名的大象都这么积极的拥抱Python生态,可以知道它发展的迅猛。
      3、灵活
      Python语言最大的优势就在于灵活,灵活在测试领域是非常重要的。因为测试面对的业务和需求往往千变万化,甚至有时候都不是自己能遇见和决定的。你可以利用 Python广泛的第三方库来轻松实现自己的自动化方案。
      4、拓展
      除了测试行业,你可以通过 Python很容易的接触到 Web 开发、数据分析、自动化运维和人工智能等领域,如果你愿意的话。当然,任何一门语言都会有他的弊端。
      Python是一门动态语言,这也是他为什么容易学的原因,但是他的执行效率比较慢,这一点在测试行业的劣势并不明显。但如果要构建巨型应用、对性能有很高要求的领域就不太合适,这时候你需要学一门静态语言,比如说Java。当然,在1-3年之内,也没有这个必要,先把Python 学好。 如果你在其他地方听到有人说Python不好,想学静态语言,可以考虑Java。
      二:JAVA
      Java学起来稍微复杂一点,不过只要你肯用功,资料随便找,大牛到处是。现在很多应用还是 Java 语言开发的,意味着你在测试的时候不太需要考虑语言的兼容性,还有一些主流的测试工具是 Java 写出来的。
      比如Jmeter等,如果学了Java,你可以改造这些工具,让他们更好的为自己所用。
      Java语言的应用相信你比较熟悉。安卓开发、服务开发、大数据,你能想到的商业项目几乎都用了 Java。很多人都说Java老了,可是至今没有其他语言能撼动它在商业项目上的地位。也会有人说 Java种种不是,哪门语言都会有人喷。比如Java编译速度比较慢,比较笨重,这些我觉得不能作为拒绝一门语言的理由。
      三:GO
      如果你觉得Python、Java你都不适合的话。 你可以考虑Go语言,Go语言因为天然的并发性被很多人青睐,现在主要用于云计算和服务设计,对于并发要求不高的领域应用较少。
      都说它现在势不可挡,人人都说它是趋势,不过这些人都是一些老程序员,他们学一门新语言比你要容易太多,做为新人,我觉得你还是先学一门容易学的。能给自己信心的语言比较合适,学完一门语言以后再去学其他的会轻松很多。语言有太多种,每一种都有自己的设计目的。人们比较的时候往往不从这些设计目的出发,随意评判,新手很容易被搞晕。
      所以我也不建议你接触太多门语言,因为你在短时间内是很难知道别人说的是不是真的,也许连他们自己都不知道。选一个容易入门的,先知道什么是编程语言也许是最好的选择,千万不要一门语言学了几天又盯着另一门语言,那样你会很尴尬。
      祝你成功。
  • 前端单元测试实践

    2019-02-03 14:05:51

      一说到单元测试,可能对于业务一线同学来说,心理立马就会无形中有一种压迫感,心想 “业务都做不完了,写个球的单元测试,先保证功能完备,赶紧上线才是王道”,这句话的核心是以业务为重,没任何问题,但是,业务在任何时候都是重要的,除了业务,其实还有效率。
      没有效率,就没有生产力,没有生产力就没法给业务铺垫更广阔的道路,效率如此重要,那我们该从哪些维度来提升效率呢?从笔者的个人经验来看,不管是在什么领域,我们在提效道路上一定会经历以下几个阶段:
      · 规范标准化
      · 机器自动化
      · 系统平台化
      · 人工智能化
      要经历以上过程,必须要有代码质量的保证,如果我们不关注代码质量,我们的研发效率是没法做到质的飞越的,原因很简单,就是人类在解决各种问题的过程中,总会不由自主的引入其他问题,从而导致系统稳定性降低,如何在漫长的系统维护过程中,保证每次发布的代码质量则是我们一直在持续探索的方向。所以现在软件测试在高校里都有专门的学科,同时软件测试岗位在互联网公司里也是非常常见的,可见,企业对系统稳定性的要求是非常非常高的。说了那么多,下面直接进入正题。
      什么是单元测试?
      单元测试,是指对软件中的最小可测试单元进行检查和验证,也就是说一个测试单元往往是一个原子型函数,同时,单元测试的编写者必须是作者本人,拥有单元测试的程序有以下几个好处:
      1、它是一种验证行为
      程序中的每一项功能都是测试来验证它的正确性。它为以后的开发提供支援。就算是开发后期,我们也可以轻松的增加功能或更改程序结构,而不用担心这个过程中会破坏重要的东西。而且它为代码的重构提供了保障。这样,我们就可以更自由的对程序进行改进。
      2、它是一种设计行为
      编写单元测试将使我们从调用者观察、思考。特别是先写测试(Test First),迫使我们把程序设计成易于调用和可测试的,即迫使我们解除软件中的耦合。
      3、它是一种编写文档的行为
      单元测试是一种无价的文档,它是展示函数或类如何使用的最佳文档。这份文档是可编译、可运行的,并且它保持最新,永远与代码同步。
      4、它具有回归性
      自动化的单元测试避免了代码出现回归,编写完成之后,可以随时随地的快速运行测试。
      单元测试用例设计
      任何一个单元测试都应该包含:
      · 正常输入
      · 离散覆盖参数值域
      · 边界输入
      · 空值验证
      · 零值验证
      · 最大值验证
      · 非法输入
      · 入参数据类型非法
      · 内存溢出验证
      · 幂等
      对于单元测试来说,保证其幂等性非常重要,幂等就是在相同输入的前提下,其输出结果不随时间而改变。
      所以,我们可以看到,对于函数式编程语言来说,写单元测试则是非常容易的事情,因为在函数式范式中,我们的函数都是纯函数,在范式层面上就已经约束了开发者写出幂等的程序,那么,在javascript领域,我们想要写出质量更高,对测试友好的代码的话,则需要尽可能的写出各种纯函数,从而保证幂等性。
      对于前端而言,其实还包含UI界面的幂等,如何更加高效的保证界面幂等,我们是可以借助jest的快照能力实现html结构级别的幂等验证或者通过gemini的离线截图能力来实现像素级的幂等验证。
      Mock
      Mock数据,在编写单元测试用例的过程中,构造Mock数据是非常重要的实现手段,因为构造数据就是我们在构造输入的过程,比如正常输入/边界输入/非法输入
      Mock环境,对于前端自动化测试而言,我们的环境Mock,往往是通过jsdom之类的库实现环境mock,保证离线场景下可以验证依赖浏览器API的程序逻辑
      Mock事件,对于离线场景来说人机交互事件是不会有真实人类参与的,所以,我们需要Mock人机交互事件,帮助程序逻辑实现UI界面的交互功能性测试,在React中,是可以通过enzyme来实现Mock事件
      Mock模块/第三方包,有些场景我们的程序依赖了某些第三方包,但是第三方包会引入副作用,比如axios,如果被测试的程序使用了该模块,它会走真实的发请求逻辑,这样还需要开一个mock请求服务,如果有一个模块拦截Mock能力,我们就不需要再开一个mock请求服务了,恰好jest提供了模块mock的能力,对于这类问题便可以轻松解决。
      Mock函数/类,在Javascript语言中,函数的入参同样也可以是函数(匿名函数),这恰好是Js最灵活的地方,但是如果参数是函数,则会使得测试用例的编写难度大大提升,我们很难知道入参函数的调用情况,所以,如果我们可以跟踪入参函数调用情况,就能很轻松的验证函数式编程范式下的程序逻辑,恰好jest提供了一个函数Mock能力,可以帮助用户快速Mock一个可以跟踪其调用情况的匿名函数。同样,对于类也是,jest提供了mock类的能力,帮助用户跟踪一个类实例的使用过程。
      白盒覆盖
      白盒覆盖就是测试用例要尽可能的覆盖程序内部的所有分支语句,从而整体性的保证代码质量。
      我们都知道,覆盖率是衡量单元测试质量的核心指标,但是,对于TDD而言,我们肯定不可能做到一开始就达到100%的覆盖率,所以,正常的单元测试用例,往往是先从黑盒用例来写,也就是程序对外暴露的API层面的测试,前期先将这部分的单测覆盖全,后期,我们在bugfix或者feature addtion的过程中可以逐步增加测试用例,最终逐步达到80%以上的覆盖率即可满足白盒覆盖的效果。
      单测定级
      根据我们前面所述的白盒覆盖,覆盖率是一个非常客观的指标,但是覆盖率对于开发者的认知模型而言是不够清晰结构化的,所以,我们还需要对覆盖率再做一次结构化定级,方便开发者一步步完善单元测试,下面让我们来枚举一下所有的单测级别:
      Level1:正常流程可用,即一个函数在输入正确的参数时,会有正确的输出
      Level2:异常流程可抛出逻辑异常,即输入参数有误时,不能抛出系统异常,而是用自己定义的逻辑异常通知上层调用代码其错误之处
      Level3:极端情况和边界数据可用,对输入参数的边界情况也要单独测试,确保输出是正确有效的
      Level4:所有分支、循环的逻辑走通,不能有任何流程是测试不到的
      Level5:输出数据的所有字段验证,对有复杂数据结构的输出,确保每个字段都是正确的
      自动化单元测试
      其实前面已经提到过了,Jest,就是一款自动化单元测试解决方案,它基本上满足了前端单元测试的所有测试需求,而且它还是一款零配置解决方案,顾名思义,就是最简单场景下是无需任何配置即可快速编写测试用例。所以使用Jest,前端写测试用例就变得十分容易了。本文不会介绍具体jest该如何使用,它有哪些API,因为此类文章到处都能找到,本文更多的是从测试方法论出发探讨单元测试的实施方案。
      提高测试用例编写效率
      有了Jest,我们在写单元测试用例的配置成本已经很低了,所以,单元测试的成本,更多的是编写测试用例上,
      要提高测试用例编写效率,我们主要从几个方向来提高:
      定制标准用例模板,让开发者做填空题,而非选择题
      制定单元测试开发规范,帮助开发者写出统一一致的单元测试用例,也方便后续协同开发维护
      渐进式编写测试用例,借助bugfix/feature addtion过程逐步完善测试用例,最大化减轻前期时间压力
      React组件单元测试规范
      1. 测试文件统一在src/__tests__目录中维护
      主要是Follow Facebook的目录命名规范
      2. 测试文件命名与React组件命名保持一致,后面以.spec.js结尾
      主要是Follow Facebook的测试文件命名规范,比如:Form.spec.js
      3. 测试用例使用test("功能描述",()=>{})函数描述用例单元
      针对最小功能单元的测试用例主要集中在该函数内
      4. 一组功能集合测试使用describe("功能集合描述",()=>{})函数描述功能集合
      一个测试文件只能描述一个功能集合,这个功能集合可以是一个React组件,也可以是一个cjs模块
      5. UI测试套件统一使用enzyme
      使用enzyme可以借助jquery like的选择器方便的对DOM渲染结果做校验
      6. React组件测试用例必须包含
      · API属性覆盖性测试用例
      · DOM快照比对,幂等校验
      · 私有Utils函数测试用例,千万不能忽略Utils函数的测试用例,很多时候,bug就出在这上面
      7. 对DOM结构做用例校验
      一个标准的React组件测试用例的输入往往是组件配置或交互事件,输出则是具体的DOM结构,我们的用例校验也都是对DOM结构做用例校验
      8. bugfix/feature addtion必须要有对应的单元测试用例才能发布
      9. 团队协作,MR/PR必须要有对应的单元测试用例才能发布
  • 如何测试 ASP.NET Core Web

    2019-02-03 13:59:46

      本文要点
      1.正确理解并使用单元测试和你的 ASP .NET Core Web API 解决方案一样重要。
      2.了解并使用模拟数据进行单元测试可以帮助你获得稳定的测试场景。
      3.为 ASP .NET Core Web API 解决方案在 .NET Core 2.1 中创建模拟数据项目。
      4.了解并设置集成测试来从外部测试 API,这样可以帮助完整测试 ASP .NET Core 2.1 Web API 解决方案。
      .NET Core 最初是在 2016 年发布的,随着 .NET Core 2.0 的发布,微软拥有了下一个通用、模块化、跨平台和开源的平台主版本。.NET Core 已经创建了许多 API,在当前版本的 .net 框架中均可用。它最初是为下一代 ASP .NET 解决方案而创建的,但现在成了许多其他场景的驱动和基础,包括物联网、云计算和下一代移动解决方案。在本系列文章中,我们将探讨 .NET Core 的一些好处,以及它如何不仅能使传统的 .NET 开发人员受益,还能使所有需要为市场带来健壮、高效和经济的解决方案的技术人员受益。
      在使用 ASP .NET Core 2.1 Web API 构建并开发一组丰富的 API 的时候,需要记住这只是实现稳定且高效的解决方案的第一步。为你的解决方案提供一个稳定的环境是非常重要的。获得优秀的解决方案不仅需要完整地构建 API,还需要严格地测试你的 API,保证用户有良好的体验。
      这篇文章是我之前为 InfoQ 写的《针对 ASP.NET Core Web API 的先进架构》的后续文章。请放心,你不需要阅读另外一篇文章,就可以从这篇文章了解到如何从测试中获得好处,但读一下那篇文章可以帮助你更多了解我是如何构建我讨论的解决方案的。在过去的几年里,我花了大量时间来思考在为客户构建 API 时进行测试。了解 ASP .NET Core 2.1 Web API 的架构,可以帮助拓宽你的了解。
      本篇文章中的例子的解决方案和所有代码都可以在我的GitHub 库中找到。
      ASP .NET Core Web API 快速入门
      让我们一起来快速了解一下 .NET 和 ASP .NET Core。ASP .NET Core 是 Microsoft 创造的全新 Web 框架,以便摆脱自 ASP .NET 1.0 以来一直存在的遗留技术。ASP .NET Core 2.1 摆脱这些遗留依赖,从头开始开发框架,因此可以给开发人员提供更高的性能,并且它是为跨平台执行而构建的。
      什么是单元测试?
      对有些人来说,测试你的软件可能是一个新的概念,但是它很简单。我们从单元测试开始。维基百科对于它的严格定义是“是一种软件测试方法,对源代码的独立单元、一组或多组计算机程序模块以及相关的控制数据、使用程序和操作程序都进行测试,以了解它们是否适合使用”。我比较喜欢的是一个外行的解释,单元测试是保证在你添加了新的功能或进行了缺陷修复之后,你的解决方案中的代码能像预期一样执行。我们测试了一个简单的代码示例,来保证它符合我们的预期。让我们来看一下单元测试示例:
    [Fact]
    public async Task AlbumGetAllAsync()
    {
    // Arrange
    // Act
    var albums = await _repo.GetAllAsync();
    // Assert
    Assert.Single(albums);
    }
      优秀的单元测试有三个部分组成。第一个是Arrange的部分,用来设置测试中可能需要的任何资源。在上面的例子中,我没有进行任何设置,所以 Arrange 的部分是空的 (但我还是为它保留了注释)。第二个部分是Act的部分,用来执行测试的部分。在我们的例子中,我调用数据库中的专辑实体类型,返回当前使用的库中的数据源完整的专辑实体。单元测试的最后一个部分是Assert的部分,用来验证待测试的操作是否正确。对于该测试,我检验是否从数据库中返回了一个专辑。
      在本文中,我会使用 xUnit 工具进行单元测试。xUnit 是 .NET Framework 和现在的 .NET Core 的开源包。我们需要 .NET Core 版本的 xUnit,在你安装 .NET Core 2.1 SDK 的时候就已经直接获得了。你可以通过 .NET Core cli 指令 dotnet test,或是通过你喜欢的 IDE(如 Visual Studio 2017、Visual Studio Code 或 JetBrain 的 Rider.)中的项目模板来创造新的单元测试项目。
      图 1:在 Visual Studio 2017 中创建新的单元测试项目
      现在,让我们深入到测试你的 ASP .NET Core Web API 解决方案的单元测试中来。
      Web API 要单元测试一些什么?
      我非常支持使用单元测试来为你的客户保证稳定和健壮的 API。但我清楚地知道要如何使用单元测试,知道要测试什么东西。我相信,你要恰到好处地对解决方案进行单元测试,而不要做多余的测试。这是什么意思呢?可能我的观点会引发很多评论,但是我不太注重要 100% 覆盖你的测试。我是否认为我们需要能覆盖 API 解决方案重要部分的测试,单独隔开每个区域,保证每个代码段都是正确的?当然!我会这么做,这也是我想要讨论的。
      由于我们的示例 Chinook.API 很小,并且可以通过集成测试来完成测试(本文稍后讨论),我发现在 Domain 和 Data 项目中我最关注单元测试。我不会详细讨论单元测试的方法(因为这个问题超出了本文讨论的范围)。我想要让你不依赖于生产数据库的数据,从而在 Domain 和 Data 项目中进行更多的测试。这是我们下一个要讨论的问题,模拟数据和对象。
      为什么在单元测试中使用模拟数据和对象?
      我们已经讨论了为什么我们要进行单元测试,单元测试一些什么内容。接下来,了解如何准确地单元测试 ASP .NET Core Web API 解决方案是非常重要的。数据是测试 API 的重点。测试可预测的数据集是非常重要的。这就是为什么我不推荐使用生产数据或者其他可能随着时间根据你的了解和认知会改变的数据。我们需要稳定的数据集来保证所有单元测试的运行,确保代码段之间的测试是相同的。比如说,在我测试 Chinook.Domain 项目的时候,我想要得到 ID 是 42 的专辑,我需要保证它的确存在,并拥有类似专辑名称这样的细节,和一条艺术家数据有关联关系。我还希望确保在我从数据源中得到一系列专辑时,大小满足我编写的单元测试。
      许多业内人士使用“模拟数据”这一术语来表示这一类数据。有很多为单元测试产生模拟数据的方法,我希望你能创造出尽可能“真实”的数据集。你给测试创造的数据越好,测试效果也会越好。我会建议你确保数据没有隐私问题,不包含公司或客户的个人数据或敏感数据。
      要满足我们对于干净、稳定数据的需求,我单独创建了一个项目,封装了单元测试项目的模拟数据。让我们称其为 Chinook.MockData(就像你能在示例中看到的一样)。我的 MockData 项目几乎和 Chinook.Data 项目相同。它们都拥有相同数量的数据库,都和相同的接口依附。我希望 MockData 项目存储在依赖注入 (DI) 容器中,这样 Chinook.Domain 项目就可以像连接到了生产数据源那样得到使用。这就是为什么我很喜欢依赖注入。它可以帮助我通过配置切换 Data 项目,而不需要做任何代码变更。
      集成测试:什么是针对 Web API 的新测试?
      在我们为 ASP .NET Core Web API 解决方案执行并验证了单元测试之后,我们要看一个完全不同类型的测试。我希望单元测试可以验证并确保对解决方案内部组件的期望。当我们对内部测试的质量满意的时候,我们需要从外部接口进行 API 测试,这就是我们所说的集成测试。
      集成测试需要在所有的组件完成的时候编写并执行,所以你的 API 可以通过正确的 HTTP 响应来验证。单元测试时测试的是单独隔离开的代码段,而集成测试时测试的是 HTTP 端点上每个 API 的整体逻辑。测试将会遵循 API 的完整工作流,从 API 项目的控制器到域项目管理器,最后到 Data 项目的库(返回来响应)。
      创造集成测试项目
      要使用你现有的测试知识,集成测试功能是基于现有的单元测试库的。我将使用 xUnit 来创造我的集成测试。在我们创建了名为 Chinook.IntegrationTest 的新 xUnit 测试项目之后,我们需要添加合适的 NuGet 包。将 Microsoft.AspNetCore.TestHost 包添加到 Chinook.IntegrationTest 项目中来。这个包中包含了执行集成测试的资源。
      图 2:添加 Microsoft.AspNetCore.TestHost NuGet 包
      接下来,我们可以创建第一个集成测试来从外部验证我们的 API。
      创建第一个集成测试
      要想进行我们解决方案中的所有 API 外部测试,我要创建一个名为 API 的文件夹,其中包含了测试。我还需要在 API 域中给每个实体类型创建新的测试类。我们的首个集成测试将会覆盖专辑实体类型。
      在 API 文件夹中创建新的类 AlbumAPITest.cs。之后我们会在文件中添加如下的命名空间。
    using Xunit;
    using Chinook.API;
    using Microsoft.AspNetCore.TestHost;
    using Microsoft.AspNetCore.Hosting;
      图 3:使用指令进行集成测试
      现在我们要使用 TestServer 来设置类,使用 HttpClient 来执行测试。我们需要名为 _client,类型为 HttpClient 的私有变量,它是基于在 AlbumAPITest 类中的构造函数中初始化的 TestServer 而创建的。TestServer 是小型 web 服务器的包装器,是基于 Chinook.API Startup 和需要的开发环境创建的。在这个例子中,我使用开发环境。我们现在具备了运行 API 的 web 服务器,以及了解如何在 TestServer 中调用 API 的客户端。我们可以开始编写集成测试代码了。
      图 4:我们第一个集成测试,获得所有的专辑
      除了构造函数代码之外,图 4 中还展示了第一个集成测试的代码。AlbumGetAllTestAsync 方法将会测试验证从 API 获取所有专辑的调用。就像之前讨论的单元测试一样,集成测试的逻辑也是用了 Arrange、Act 和 Assert。我们首先创建 HttpRequestMessage 对象,其中 HTTP 作为 InlineData 注释中的变量而提供,URI 部分表示对于所有的专辑的调用 (“/api/Album/”)。之后我们会让 HttpClient _client 发送 HTTP 请求,最后,我们会检查验证 HTTP 响应是否满足我们的期望,在本例中是 200。图 4 中我展示了两种检验 API 调用的方法。你可以使用其中任意一种,但我更喜欢第二种方法,因为它允许我用相同的模式来检验对特定 HTTP 响应代码的响应。
      response.EnsureSuccessStatusCode();
      Assert.Equal(HttpStatusCode.OK, response.StatusCode);
      我们还可以创建需要从 API 测试特定实体键的集成测试。对于这类测试,我们需要在 InlineData 注释中添加额外的值,将会通过 AlbumGetTestAsync 方法参数传递。我们的新测试会使用相同的逻辑,并使用和之前测试一样的资源,但是我们为 HttpRequestMessage 对象在 API URI 端中传递实体键。你可以在图 5 中查看代码。
      图 5:专辑的第二个集成测试
      在你为测试 API 创造了所有集成测试之后,需要通过 Test Runner 来运行它们,并保证它们全部通过。你创建的所有测试也可以在 DevOps 持续集成(CI)过程中执行,这样可以在整体开发和部署过程中测试你的 API。现在需要有执行路径保证你的 API 在开发、质量保证和部署阶段都得到测试和维护,让你的 API 使用者拥有良好体验的同时不发生意外情况。
      图 6:在 Visual Studio 2017 中运行集成测试
      结论
      拥有良好设计的一套测试计划,使用单元测试来做内部测试,使用集成测试来验证 API 调用就和开发 ASP .NET Core Web API 阶段创建架构是一样重要的。
Open Toolbar