聚焦测试,驱动卓越

在经历了“七年之痒”后,蓝鲸项目进入第八个年头,项目的一切趋于稳定。团队倡导持续改进,这时大家的感觉是已经尽力做到最好,似乎没有什么可以改进的了。为了突破这个局面,项目重新聚焦测试,从质量和测试的角度对现状进行了一次评估。

评估采用的是基于软件测试原则的模型,本文就是跟大家分享一下这个模型。

测试原则

在2012年澳大利亚敏捷大会(Agile Australia)上,ThoughtWorks非常资深的测试实践带头人Kristan Vingrys分享了如上测试原则,这些原则是ThoughtWorkers在多年软件测试实践基础上总结出来的。

1. 质量内建(Build quality in)

You cannot inspect quality into the product; it is already there. — W.Edwards Deming

著名的质量管理专家戴明指出:产品质量不是检测出来的,从产品生产出来后质量就已经在那了。这同样适用于软件产品。

缺陷发现的越晚,修复的成本就越高。质量内建要求我们做好软件开发每个环节,尽早预防,以降低缺陷出现后的修复成本,要减少对创可贴式的补丁(hotfix)的依赖。

推荐实践: TDD、ATDD等。

2. 快速反馈(Fast feedback)

每个环节的任何变化都能最快的反馈给需要的人,从而能够基于当下最新信息做出明智的决定,降低风险。这要求我们对系统进行频繁的测试,缩短回归测试的周期。

推荐实践:

  • 符合测试金字塔结构的自动化测试,让每一层的测试都能发挥尽可能大的价值,给出最快速的反馈;
  • 持续集成,尽早排查集成引起的问题,降低集成所带来的风险。

3. 全员参与(Involve everyone)

这次上线好多bug,QA是怎么测的?!

那个xxx组在上线前发现了很多的bug,他们的QA真给力!

成也QA,败也QA…如果还是这样的认识,那是极为片面的。测试不仅仅是QA的事情,团队成员要一起为质量负责,软件开发生命周期的测试相关活动需要全员的参与。

全员参与的好处是利用不同角色的不同领域知识和不同的思维模式,不仅可以使得测试的质量更高,同时还能最优化利用测试资源,做到价值最大化。

推荐实践:

  • 自动化测试:QA和BA结对用DSL编写测试用例,QA和Dev结对编码实现测试,生成业务人员可读的测试报告;
  • Bug bash(bug大扫除):团队不同角色一起参与的一个找bug的测试活动。

4. 测试作为资产(Tests as asset)

自动化测试帮助我们验证系统功能的正确性,好的自动化测试还有文档的功能,是宝贵的资产。如果每个项目都构建自己独立的自动化测试,没有跨项目共享,其浪费显而易见。

这个原则要求把自动化测试的代码跟产品开发的代码一起,当做资产管理起来,在不同项目间做到尽可能的复用。这笔宝贵的资产能帮助我们更好的统计跨项目的测试覆盖率,更好的优化测试。

推荐实践:利用版本控制管理工具把测试代码和产品构建代码一起管理,都作为产品的一部分。

5. 更快的交付(Faster delivery into production)

任何一个idea越快做成软件产品交付给用户,给企业带来的价值越大。

该原则要求我们把测试活动融入软件开发生命周期的每个环节,不要在后期进行长时间的集中测试;同时测试人员的关注点不再是发现更多的bug以阻止不符合质量要求的产品上线,而是把目标放在如何能够帮助团队尽快的让产品上线,让企业投资回报更早,也就是更快的赚钱。

推荐实践:自动化构建流水线、关注平均恢复时间、发布与部署解耦等。

6. 清晰一致的测试视图(Clear and consistent view of testing)

用可视化的报告给客户和内部团队展示测试的状态和产品内外部的质量,对项目的质量和风险把控是非常有帮助的。不同项目各自采用五花八门的图表样式,将不利于项目间的信息共享和比较,无端增加复杂性,带来浪费。

因此,我们需要把状态报告做的尽可能简单、清晰,并且保持跨项目的指标一致性;同时,我们不应该为了让某个指标变得好看而改变我们的行为,整个报告要诚实开放,这样才能真实反映出项目的状况。

7. 优化业务价值(Optimize business value)

开发软件无疑是要给客户的业务带来价值,软件测试也需要为这个目标服务,测试要跟业务价值保持一致,帮助客户优化业务价值。要求做到:

  • 测试不仅是保险,不仅是保证软件质量的;
  • 要有目的的关注变化的特性,不要盲目的散弹枪式的对任何特性进行测试,要有优先级;
  • 要能帮助企业驱动新的特性和功能;
  • 帮助客户创造安全的尝试新点子的环境,提供快速的反馈。

推荐实践:

  • 基于风险的测试,根据业务优先级需要调整测试策略,在测试过程中尽可能规避给业务带来的风险;
  • 生产环境下的QA,通过收集生产环境的真实用户行为和应用使用数据,对业务的优化做出贡献。

评估模型以及在项目中的应用

评估模型就是将上述七条原则的每一条细化,列出该原则对应的实践和行为,并给每个实践或行为设定0-5分的不同评分标准,最后统计各个原则的总分,形成类似下图的结果报告:

在项目中的应用

以Cristan分享的模型为基础,由Tech Lead和几个DEV、QA成立一个评估小组。

第一步:分别根据各自的理解给项目打分,结果是很有意思的,请看下图:

根据这些结果,可以看出大家的认识是不太一致的。

第二步:评估小组对模型中的每条细节进行review,做适当修改以更符合项目情况,并且在评估小组内达成共识。其中,所做的修改包括修改原有的实践评分指标、增加新的更适合项目和当前技术趋势的实践、删除过时的或者不符合项目特点的实践。

第三步:根据更新过后的模型指标对项目上的一个team做评估试点,详细分析该team对应到测试原则各个维度的well和less well部分,由评估小组成员一起打分,得到该team的评估结果图。

第四步:根据评估结果并结合项目目标排定需要改进的优先级,制定出改进action,并更新给试点team执行。

后续:试点一个周期后再次评估,并重新review评估模型,再推行到整个项目。同时,周期性的进行新的评估和制定新的action,以做到持续的改进和优化。

总结

应用程序的质量、测试的快速性、以及上线后轻松自信的更新服务的能力,是帮助企业实现业务价值最大化的关键因素之一,一直是我们所追求的。

基于测试原则的评估模型,可以帮助我们在追求这个目标的道路上少走弯路,帮助我们持续的改进,以驱动出更加卓越的软件。


更多精彩洞见,请关注微信公众号:思特沃克

Share

测试三明治和雪鸮探索测试

测试金字塔理论被广泛应用于计划和实施敏捷软件开发所倡导的测试自动化,并且取得了令人瞩目的成就。本文尝试从产品开发的角度出发,结合Kent Beck最近提出的3X模型和近年来迅速发展的自动化测试技术,提出并讨论一种新的测试层级动态平衡观:三明治模型。同时,为了应对端到端测试在实践中面临的种种挑战,设计并实现了一种面向用户旅程的端到端自动化测试框架——雪鸮。实际项目经验表明,雪鸮能够显著提升端到端测试的可维护性,减少不确定性影响,帮助开发人员更快定位和修复问题,对特定时期的产品开发活动更具吸引力。

背景

测试金字塔

按照自动化测试的层级,从下至上依次为单元测试、集成测试和端到端测试,尽量保持数量较多的低层单元测试,以及相对较少的高层端到端测试,这就是测试金字塔理论。随着敏捷软件开发的日益普及,测试金字塔逐渐为人所知,进而得到广泛应用。Mike CohnMartin Fowler以及Mike Wacker等先后对测试金字塔进行了很好的诠释和发展,其主要观点如下:

  • 测试层级越高,运行效率就越低,进而延缓持续交付的构建-反馈循环。
  • 测试层级越高,开发复杂度就越高,如果团队能力受限,交付进度就会受到影响。
  • 端到端测试更容易遇到测试结果的不确定性问题,按照Martin Fowler的说法,这种结果不确定性的测试毫无意义。
  • 测试层级越低,测试的代码隔离性越强,越能帮助开发人员快速定位和修复问题。

3X模型

2016年起,敏捷和TDD先驱Kent Beck开始在个人Facebook主页撰写系列文章,阐述产品开发的三个阶段——Explore、Expand和Extract,以及在不同阶段中产品与工程实践之间的关系问题,即3X模型。近二十年软硬件技术的飞速发展,使得软件开发活动面临敏捷早期从未遇到的市场变革,而根据在Facebook工作的经历,Kent Beck把产品开发总结为三个阶段:

  • 探索(Explore),此时的产品开发仍处于非常初期的阶段,仍然需要花费大量时间寻找产品和市场的适配点,也是收益最低的阶段。
  • 扩张(Expand),一旦产品拥有助推器(通常意味着已经找到了市场的适配点),市场需求就会呈现指数级上升,产品本身也需要具备足够的伸缩性以满足这些需求,由此收益也会快速上升。
  • 提取(Extract),当位于该阶段时,公司通常希望最大化产品收益。但此时收益的增幅会小于扩张阶段。

(3X)

Kent Beck认为,如果以产品是否成功作为衡量依据,那么引入自动化测试在探索阶段的作用就不大,甚至会延缓产品接受市场反馈循环的速度,对产品的最终成功毫无用处,还不如不引入;当位于扩张阶段时,市场一方面要求产品更高的伸缩性,另一方面也开始要求产品保证一致的行为(例如质量需求),那么此时就该引入自动化测试来保证产品的行为一致性;当产品最终处于提取阶段时,任何改动都应以不牺牲现有行为为前提,否则由此引发的损失可能远高于改动带来的收益,此时自动化测试就扮演了非常重要的角色。

测试工具爆炸式增长和综合技能学习曲线陡升

根据SoftwareQATest网站的历史数据,2010年记录的测试工具有440个,共划分为12个大类。这个数字到2017年已经变为560个,共15个大类,且其中有340个在2010年之后才出现。也就是说,平均每年就有50个新的测试工具诞生。

面对测试工具的爆炸式增长,一方面所支持的测试类型更加完善,更加有利于在产品开发过程中保证产品的一致性;另一方面也导致针对多种测试工具组合的综合技能学习曲线不断上升。在实践中,团队也往往对如何定义相关测试的覆盖范围感到不知所措,难以真正发挥测试工具的效用,也很难对产品最终成功作出应有的贡献。

从金字塔到三明治

作为敏捷在特定时期的产物,测试金字塔并不失其合理性,甚至还对自动化测试起到了重要推广作用。但是,随着行业整体技术能力的不断提升,市场需求和竞争日趋激烈,在项目中具体实施测试金字塔时往往遭遇困难,即便借助外力强推,其质量和效果也难以度量。

此外,随着软件设计和开发技术的不断发展,低层单元测试的传统测试技术和落地,因前、后端技术栈的多样化而大相径庭;同时,在经历过覆盖率之争,如何确保单元测试的规范和有效,也成为工程质量管理的一大挑战;高层的端到端测试则基本不受技术栈频繁更替的影响,随着不同载体上driver类技术的不断成熟,其开发复杂度反而逐渐降低。

这里讨论一种新的测试层级分配策略,我们称之为三明治模型 。如下图所示,该模型允许对不同测试层级的占比进行动态调整,说明了倒金字塔形、沙漏形以及金字塔形分配对特定产品开发阶段的积极作用。

(Sandwich)

产品开发的自动化测试策略

根据3X模型,在探索初期往往选择避开自动化测试。一旦进入扩张期,产品的可伸缩性和行为一致性就成为共同目标,但此时也常会发生大的代码重构甚至重写,如果沿用测试金字塔,无论补充缺失的单元测试,还是只对新模块写单元测试,都既损害了产品的快速伸缩能力,也无法保证面向用户的产品行为一致性。因此,如果在探索后期先引入高层的端到端测试,覆盖主要用户旅程,那么扩张期内所产生的一系列改动都能够受到端到端测试的保障。

需要注意的是,用户旅程在产品即将结束探索期时通常会趋于稳定,在扩张期出现颠覆性变化的概率会逐渐减少,端到端测试的增长率会逐步下降。

除此以外,随着扩张期内不断产生的模块重构和服务化,团队还应增加单元测试和集成测试的占比。其中,单元测试应确保覆盖分支场景(可以在CI中引入基于模块的覆盖率检测);集成测试和某些团队实践的验收测试,则需进一步覆盖集成条件和验收条件(在story sign-off和code review时验收)。

许多新兴的测试技术和工具擅长各自场景下的验收测试,但更重要的仍是识别产品阶段和当前需求,以满足收益最大化。

(Sandwich-3x)

由此我们认为,随着产品开发的演进,测试层级的分配应参考三明治模型,动态调整层级占比,更加重视运营和市场反馈,致力于真正帮助产品走向成功。

端到端测试的机遇和挑战

与其他测试层级相比,端到端测试技术的发展程度相对滞后。一方面,作为其基础的driver工具要在相应载体成熟一段时间之后才能趋于稳定,web、mobile无不如是。另一方面,端到端测试偏向黑盒测试,更加侧重描述用户交互和业务功能,寻求硬核技术突破的难度较高,于是较少受开发人员青睐。但是,由于端到端测试更接近真实用户,其在特定产品开发活动中的性价比较高,有一定的发展潜力。

然而,当前实践中的端到端测试,普遍存在如下问题:

  • 低可维护性。一般实践并不对测试代码质量作特别要求,而这点在端到端测试就体现得更糟。因为其涉及数据、载体、交互、功能、参照(oracle)等远比单元测试复杂的broad stack。虽然也有Page Object等模式的广泛应用,但仍难以应对快速变化。
  • 低运行效率。如果拿单次端到端测试与单元测试相比,前者的运行效率肯定更低。因此只一味增加端到端测试肯定会损害构建-反馈循环,进而影响持续交付。
  • 高不确定性。同样因为broad stack的问题,端到端测试有更高的几率产生不确定测试,表现为测试结果呈随机性成功/失败,进一步降低运行效率,使得真正的问题很容易被掩盖,团队也逐渐丧失对端到端测试的信心。
  • 难以定位问题根因。端到端测试结果很难触及代码级别的错误,这就需要额外人工恢复测试环境并尝试进行问题重现。其中所涉及的数据重建、用户交互等会耗费可观的成本。

方法

为了解决传统端到端测试遇到的种种挑战,本文设计了一种面向用户旅程的端到端自动化测试框架——雪鸮(snowy_owl),通过用户旅程优先、数据分离、业务复用和状态持久化等方法,显著提高了端到端测试的可维护性,降低不确定性的影响,并且能够帮助团队成员快速定位问题。

用户旅程驱动

端到端测试应尽量贴近用户,从用户旅程出发能保证这一点。在雪鸮中,用户旅程使用被称作play books的若干yaml格式的文件进行组织,例如下列目录结构:

play_books/
  core_journey.yml
  external_integration.yml
  online_payment.yml

其中每个play book由若干plots所组成,plot用于表示用户旅程中的“情节”单位,其基本特征如下:

  • 单一plot可以作为端到端测试独立运行,例如发送一条tweet的情节:
SnowyOwl::Plots.write 'send a plain text tweet' do
  visit '/login'  
  page.fill_in 'username', with: 'username'
  page.fill_in 'password', with: 'password'
  page.find('a', text: 'Sign In').click
  # verify already login?
  page.find('a', text: 'Home').click
  # verify already on home page?
  page.fill_in 'textarea', with: 'Hello World'
  page.find('a', text: 'Send').click
  # verify already sent?
end
  • 单一plot应是紧密关联的一组用户交互,并且具备体现一个较小业务价值的测试参照。
  • plot可以被play book引用任意次从而组成用户旅程,play book同时定义了所引用plots之间的顺序关系,基本语法如下所示:
---
- plot_name: send a plain text tweet
  digest: 2aae6c35c94fcfb415dbe95f408b9ce91ee846ed
  parent: d6b0d82cea4269b51572b8fab43adcee9fc3cf9a

其中plot_name表示情节的标题,digest和parent分别表示当前情节引用在整个端到端测试过程中的唯一标识和前序情节标识,初期开发人员可以通过各个情节的引用顺序定义用户旅程,大多数情况下digest和parent将由系统自动生成并维护。

整个play books集合将是一个以plots为基础组成的森林结构,而端到端测试的执行顺序则是针对其中每棵树进行深度遍历。

通用业务复用

由于plot本身必须是一个独立可运行的端到端测试,那么plots之间通常会共享一部分交互操作,例如用户登录。雪鸮允许把高度可复用的交互代码进行二次抽取,称作determination:

SnowyOwl::Determinations.determine('user login') do |name, user_profile|
  # return if already login
  visit '/login'  
  page.fill_in 'username', with: user_profile[:username]
  page.fill_in 'password', with: user_profile[:password]
  page.find('a', text: 'Sign In').click
  # verify already login?
end

这样,plot的代码就可以简化成:

SnowyOwl::Plots.write 'send a plain text tweet' do
  determine_user_login({username: 'username', password: 'password'})
  page.find('a', text: 'Home').click
  # verify already on home page?
  page.fill_in 'textarea', with: 'Hello World'
  page.find('a', text: 'Send').click
  # verify already sent?
end

这里应注意Determination和Page Object的区别。看似使用Page Object可以达到相同的目的,但是后者与Page这一概念强绑定。而Determination更加侧重描述业务本身,更符合对用户旅程的描述,因此比Page Object在plot中更具适用性。当然,在描述更低层的组件交互时,Page Object仍然是最佳选择。

测试数据分离

合理的数据设计对描绘用户旅程非常重要,雪鸮对测试逻辑和数据进行了进一步分离。例如用户基本数据(profile),同样是使用yaml文件进行表示:

data/
  tweets/
    plain_text.yml
  users/
    plain_user.yml

那么在plot的实现中,就可以使用同名对象方法替代字面值:

SnowyOwl::Plots.write 'send a plain text tweet' do
  determine_user_login({username: plain_user.username, password: plain_user.password})
  page.find('a', text: 'Home').click
  # verify already on home page?
  page.fill_in 'textarea', with: plain_text.value
  page.find('a', text: 'Send').click
  # verify already sent?
end

情节状态持久化

雪鸮的另一个重要功能是情节状态的持久化和场景复原。为了启用情节状态持久化,开发人员需要自己实现一个持久化脚本,例如对当前数据库进行dump,并按照雪鸮提供的持久化接口把dump文件存储至指定位置。

当端到端测试运行每进入一个新的情节之前,系统会自动执行持久化脚本。也就是说,雪鸮支持保存每个情节的前置运行状态。

当端到端测试需要从特定的情节重新开始运行时,雪鸮同样会提供一个恢复接口,通过用户自定义的数据恢复脚本把指定位置的dump文件恢复至当前系统。

该功能有两处消费场景:

  • 由于broad stack的问题,端到端测试不确定性的技术因素一般较为复杂。实际经验表明,测试的随机失败率越低,就越难以定位和修复问题,而通过不断改进测试代码的方式消除这种不确定性的成本较高,效果也不好。但是,可以尽量消除不确定性带来的影响。例如,不确定测试导致的测试失败,通常会导致额外人工验证时间,完全可以选择让系统自动重试失败的测试。另一方面,重试会造成测试运行效率降低,特别是针对端到端测试。当一轮端到端测试结束后,雪鸮只会自动重试失败的情节测试,同时利用该情节对应的数据dump文件保证场景一致性,这就减少了重试整个端到端测试带来的运行效率下降问题。
  • 当团队成员发现端到端测试失败,通常需要在本地复现该问题。而借助测试dump文件,可以直接运行指定plot测试,从而避免额外的人工设置数据和交互操作,加快问题定位和解决。

实践

雪鸮在笔者所在的项目有超过6个月的应用时间。该项目在产品开发方面长期陷入困境,例如过程中同时兼具了3X每个阶段的特点,不仅缺少清晰的产品主线,还背负了接棒遗留系统的包袱。这种状况对工程质量管理提出了更大挑战。

项目采用雪鸮对已有端到端测试进行了重构,生成了一个核心用户旅程和三个涉及外部系统集成的重要用户旅程,包含24个plots,9个determinations,使端到端测试实现了长期稳定运行。在本地相同软硬件环境下,不确定性导致的随机失败从原有10%降低至1%以内,部署至云环境并采用headless模式后,连续15天测试失败记录为零,运行效率的损失可以忽略不计。同时,当用户旅程产生新分支时,可以引入新的情节测试节点,并且根据业务需求将其加入现有play book树,从而实现端到端测试的快速维护。

持续集成与常态化运行

项目完整的端到端测试的平均运行时间保持在19分钟左右,为了不影响现有持续集成节奏,CI每30分钟自动更新代码并运行端到端测试,结果在dashboard同步显示,一旦发生测试失败,第一优先级查找失败原因并尝试在本地复现和修复。

常态化运行端到端测试的另一个好处是,能够以低成本的方式实现24小时监控系统各个组件的功能正确性,有助于更早发现问题:一次,产品即将上线的支付功能发生异常,查看CI记录发现端到端测试在晚上9:15左右出现了首次告警。通过及时沟通,确认是海外团队在当时擅自改动了支付网关的一个配置,造成服务不可用的问题,并迅速解决。

结论与展望

Kent Beck的3X模型,提出了从不同产品开发阶段看待工程实践的新视角。而敏捷一贯推崇的TDD等实践,更多体现在个人技术专长(Expertise)方面,与产品是否成功并无必然联系。然而,程序员的专业主义(Professionalism)的确同时涵盖了技术专长和产品成功两个方面,二者相辅相成。因此,如何通过平衡众多因素并最终提高整体专业性,这才是软件工程面临的经典问题。本文给出的测试三明治模型,目的就是帮助思考产品开发过程中测试层级间的平衡问题。

为了应对现有端到端测试面临的挑战,本文设计并实现了一种新的面向用户旅程的端到端测试框架,通过职责隔离、业务复用和状态持久化等手段,构建了易于维护且更加有效的端到端测试。同时,基于上述方法构建的测试代码,更易于和自动化测试的其他研究领域相结合,在诸如测试数据构建、用例生成、随机测试和测试参照增强等方向有进一步的应用潜力。


更多精彩洞见,请关注微信公众号:思特沃克

Share

测试矩阵

迷阵

“单元测试,集成测试,端到端测试,安全测试,性能测试,压力测试,契约测试,冒烟测试,验收测试,API测试,UI测试,兼容性测试……”

不知道你是不是像我一样,曾被这些各种各样的“测试”搞得晕头转向。作为一个有追求的开发人员,保证所写的程序、所构建的系统具备良好的质量自然是分内之事。但是面对这些千奇百怪的测试难免会望而却步,只能劝自己一句“专业的事情还是交给专业的人去做吧”,然后把测试的工作一把推给QA,闷头写自己的代码去了。

不光是测试种类众多,每个人对于某一个测试的理解也都不一样。就拿大家最熟悉的“单元测试(unit testing)”来举例,问题的关键就被聚焦到了“到底如何才算是一个单元(unit)?”有人说是一个方法,有的人说是一个类,有的人说都不对,应该是一个最小的业务单元(至少是API级别的)。还有人提出了Integration Unit Test的概念,即集成级别的单元测试。

不光是我等软件小辈,就连很多IT界的神级人物也常常为此争论不休。

古话说的好,一千个人心中有一千种单元测试,看来说的是有道理的。

列表法

(列表法)

这是昨天陪闺女写作业的时候,看到她使用了一种被称作“列表法”的方法去解一个小学2年级的逻辑题。闺女说,这种方法很神奇,原本看起来弯弯绕的问题,画个表勾勾叉叉就解决了。

随后我也查了一下:“列表法是小学数学学科中经常使用的一种方法,使用列表法可以解决许多复杂而有趣的问题。运用列出表格来分析思考、寻找思路、求解问题,经常用来解决类似于鸡兔同笼的经典问题……”

虽然我一直没有搞清楚为啥要把鸡和兔子放到一个笼子里,但回到测试迷阵的问题,好像这种小学3年级就教授的方法也能适用。

测试矩阵

(测试矩阵)

测试的种类繁多,难于理解,难于沟通。我觉得主要是在于我们将两个测试分类的维度混杂在了一起。

其中第一个维度是测试实现的层次或粒度,说白了就是在哪个层次上的测试,也可以理解成测试到底测的是哪儿。是方法?是类?是API?是单个Service?是两两Service?还是应用?还是系统?还是平台?

我们常说的单元测试,API测试,端到端测试,UI测试都是侧重于按照这种维度去分类不同的测试种类的。

但是我们在谈论这些测试的时候,其实隐含了一个概念就是他们测的是什么?也就是测试的目标。例如当我们提到上面的单元测试、API测试、端到端测试的时候其实隐含的想表达的是单元级别的功能测试,API级别的功能测试和端到端级别的功能测试。

这时候你肯定会想,这不废话么,不测功能我测什么?

这就是我想说的第二个测试分类的维度:我们测试的标的物,或是说测试的目标。如果说第一种测试维度是根据“测哪儿”区分的,那第二个维度就是根据“测什么”区分的。

例如,我们常常提到的:功能测试、集成测试、性能测试、安全测试、压力测试、兼容性测试,契约测试都是这种按照这个维度去区分不同的测试种类的,他们都不是关注于我们要测哪儿,而是更侧重于我们到底要测什么:业务功能是否正确?是否能按预期集成?契约是否被保证?安全能否达到要求?性能是否满足预期和要求?

只不过我们日常工作中,大多数情况下测试都是在验证功能是否正确,所以我们常常忽略了第二个维度,只关注于测哪儿。只有当我们去测试像性能和安全这种非功能需求的时候才会想到第二个维度,但有趣的是往往我们这时候又会忽略第一个维度,例如当我们听到有人提及性能测试的时候,并没有明确的表达测的是方法的性能、API的性能,还是UI的性能,进而导致了理解的不一致和混乱。

换个叫法

可见,之前之所以被测试迷阵困扰,其本质原因就是并没有明确区分开这两个维度,甚至将之混为一谈,从而使我们对于“XX测试”的定位和理解包括沟通都变得模糊而不准确。

如果我们不再提“单元测试”、“性能测试”这种含糊不清的概念,而是通过测试矩阵上的二维定位法,改称“方法级别的功能测试”和“API级别的性能测试”,我想我们对于测试的沟通讨论甚至学习实现将明确的多,也简单的多。


更多精彩洞见,请关注微信公众号:思特沃克

Share

QA请勿忘初心

让我们回顾一下QA与QC的区别:

Quality Assurance :The planned and systematic activities implemented in a quality system so that quality requirements for a product or service will be fulfilled.

Quality Control :The observation techniques and activities used to fulfill requirements for quality.

QA的工作涉及软件研发流程的各个环节,且涉及到每一位参与研发的人员,但质量保证工作又不涉及具体的软件研发细节,侧重于整个流程。

QC则侧重于点,利用各种方法去检查某个功能是否满足业务需求。

ThoughtWorks 的QA则是这两者的混合体,既要保证开发流程的质量,又要保证Story的功能正确。

我来ThoughtWorks已经3年了,当过BQConf讲师与主持人,参加过公司内各类测试相关活动,也阅读过邮件中分享的关于测试的话题,大部分人关注点都离不开自动化测试,面试的QA也说想到ThoughtWorks来学习高深的自动化测试,仿佛自动化测试代表了整个QA界,我反对盲目的自动化测试,确切的说反对盲目的UI自动化测试。很多QA在自动化测试海洋里迷失了自己。

我要强调自动化测试:真的没有银弹。

QA的最终价值体现

Faster Delivery Of Quality Software From Idea To Consumer

确保项目的正确性

所以自动化测试只是其中的一小部分。

如上图顶部和底部的文字是对一个QA所能带给项目的总结:“我们在开发正确的产品吗?如果是,那么我们开发的产品正确吗?”所以QA首先需要在整个项目过程中不断询问所有成员上述问题,确保团队是在开发客户所需的产品,而不是自己YY出来的产品。

确保流程的正确性

Quality is not just in the software but also in the process.

质量从来都不只是QA的职责,而是整个团队的职责。但QA如果自己都不注重,不督促组内成员改进质量,再将责任强加于整个团队,那么产品质量又何谈提升与保证。

中间的图片从一个QA的角度表明了一个用户故事的生命周期以及QA如何参与其中每个环节。

首先BA和客户将要开发的用户故事列出之后,BA与QA可以一起结对编写具体用户故事的内容,场景与验收条件,利用自己对业务以及系统的熟悉度,尽量配合BA将用户故事中坑排除掉。

所有参与用户故事kick off的角色,都应该提前了解用户故事内容。在kick off过程中,提出自己对用户故事的疑问,尽量在这个阶段解决掉业务需求上的问题。

在完成kick off后,QA可以和开发人员一起结对编写unit test以及Automated Acceptance Tests,身为一个敏捷QA,我们起码要了解团队选用的单元测试工具,熟悉项目的技术架构,这样更好的便于我们把控整个项目质量,在与开发人员结对的过程中,帮助开发人员分析业务场景的分支,来确保单元测试覆盖的是正确的场景,而不是为了交代上级随便乱写的单元测试,这也可以帮助QA熟悉代码,提高编码能力。

当开发人员完成编码工作后,这时QA、UX、BA、开发人员一起检查用户故事,是否按照用户故事来检查是否完成对应的功能。UX也可发表对用户故事UI以及交互的一些看法,有任何问题及时讨论后,将问题尽早的反馈给客户。

当开发人员交付一部分功能之后,QA就可以做常规的用户故事测试,几个迭代之后,QA开始进行跨功能需求测试和探索性测试等。根据探索性测试的结果,QA可能会调整测试策略,调整测试优先级,完善测试用例等等。

上面这些QA实践貌似已经很完美,其实还差最重要的一环:uality Analysis 。每次发布后,我们总以为我们发布了一个完美的产品,但却总能在新迭代开发过程中发现之前存在的问题,历史总是惊人的相似,为什么,没有分析总结问题,以及相应的预防手段,那么同样的问题只会重现。

同时我们也要回顾下自己在工作中真的将这些敏捷实践都应用到工作中了吗?我想或多或少的都有所欠缺。对于一个QA来说,不应循规蹈矩照搬敏捷实践。例如,在kick off中,发现开发人员、UX对用户故事涉及的场景以及内容了解不清楚,QA也可能漏掉一些测试场景,那么我们可以在kick off之前,加入一个pre-kick off的实践,留出时间,让每个角色都能够完整了解用户故事。在kick off之中,UX没有办法完整的确认页面的字体大小或者颜色等是否正确,那么在sign off之后,我们也加入一个UX-test实践,帮助UX更好解决这些问题。

所以每个项目也应都有适合自己项目的敏捷实践,发现项目存在的问题,持续改进才是最佳实践。

再来谈谈自动化测试

上面的测试金字塔对于大家来说再熟悉不过了,对于自动化测试来说最有价值的仍然是单元测试,但对于QA来说无疑最复杂的。

大部分QA或者tester,仍然以UI自动化为重心。之所以反对盲目的UI自动化测试,是因为变化频繁的UI设计,投入产生比极低,这都应该让我们重新思考下UI自动化的价值。

我们需要一个实施UI自动化的正确方式:

  • 能不用UI自动化测试就不用,梳理业务主线,只保留用户操作最频繁、交互最多的场景。
  • 根据面向对象设计的原则,构建适合项目的UI自动化框架,无论自己编写框架,还是采用开源框架。
  • 尽量采用独立测试数据,确保运行测试不受影响。例如采用mock数据库或者每次运行时还原测试数据库。

回到正题,面对自动化测试的大潮,QA应该关注什么?

  • 编码规范。这是个真实例子,开发人员对于类名命名没有用Camel-Case,造成在Linux系统中部署不成功,Python中乱使用缩进等。其实这些都可以避免,例如开发工具加入自动检查,或者在CI上加入校验编码规范的步骤,采用一些工具就可以达到目的,比如jshint,RuboCop等。
  • 结对完成单元测试或API测试等,一方面可以提高QA的编码能力,另一面可以给出开发人员一些建议,将单元测试覆盖到更多的场景。

例如,如果你们项目采用React作为前端框架,如果你不能理解React virtual dom 与jsx,当我们在写UI自动化脚本时,你会发现根本无法进行下去,日常中我们需要定位的元素全是这样:

<div class="styles__formField___1fyGy">
    <input type="text" placeholder="Email">
    <svg class="styles__formIcon___37VGd" viewBox="0 0 24 24" style="display: inline-block; fill: rgba(0, 0, 0, 0.870588); height: 24px; width: 24px; user-select: none; transition: all 450ms cubic-bezier(0.23, 1, 0.32, 1) 0ms;">
        <path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z">
        </path>
    </svg>
</div>

所有的页面都是js渲染出来的,如果你懂jsx,就知道只需要在对于的Component render方法中更改加入id等元素就可以搞定。

render() {
    return (
        <div>
            <input type="text" placeholder="Email" id="Email">
        </div>
    )
}

控制单元测试覆盖率,100%的单元测试覆盖率当然是最好的,但如果交付压力大,和客户商量后,我们可以尽量覆盖业务主线,而不是为了达到覆盖率延误了交付周期。

再来谈谈质量分析

作为一个QA,我们不仅要检测项目中存在的问题,也要改进团队的实践活动,更重要的是预防问题的发生。

  • 每次bug bash或相应迭代完成后,要分析统计,找出产生缺陷的环节,并采取措施防止问题再现。例如每次release或者bug bash之后,可以按照功能模块与bug类型进行统计划分,分析统计bug的成因,例如某次迭代我们bug数量激增,经调查,发现我们对某些模块的前端代码进行了重构,但缺乏相应的单元测试与集成测试,造成了我们没有及时发现bug。之后我们就对应的采取措施防止问题再现。
  • 总结分析报告,及时反馈这些信息给团队。总结分析是一个长期的任务,每次bug数量的变动,都会直接体现整个团队上次迭代的开发质量,例如bug数量减少了,可以鼓励成员再接再厉。或者某几次迭代某些模块bug呈上升趋势,那么就需要组织团队一起讨论问题根源,采取措施防止问题重现。
  • 利用代码质量分析工具,帮助我们尽早预防问题的发生。例如Sonar代码质量管理平台,可以帮助我们从代码复杂度,重复代码,单元测试覆盖率,编码规范等纬度来分析代码所存在的问题。当然也有其他的开源工具,像RubyCritic,/plato不同的语言都会有相应的工具。
  • 在线监控,利用像newrelic,airbrake等监控工具对部署在本地或在云中的Web应用程序进行监控、故障修复、诊断、线程分析以及容量计划。这样不管产品环境有任何问题,我们都能及时响应,尽早修复,减低损失。

最后让我们再看看QA应具有那些能力与技能

软技能方面包括风险控制、辅导他人、沟通能力、分析定位等。技能方面则包括缺陷管理、流程改进、测试分析、可用性测试、性能测试、安全测试等。

写在最后

回顾上述实践,其实我们可以做的更好,而不是把团队的质量全都交给自动化,回归QA的应有的初心,让我们从各个方面改进质量,带给团队更好的未来。


更多精彩文章,请关注微信公众号:软件乌托邦

Share

项目实施DevOps时,我们是如何做测试的

正如我们所知,DevOps最近几年很风靡,很多企业正在如火如荼的推行它。然而,你可曾想过,从传统到敏捷、再到DevOps,开发模式的不断革新对测试提出了怎样的挑战?

最近我们项目在实施DevOps,因此想趁热打铁,就DevOps模式下如何做测试,谈一谈自己的认知。

DevOps有什么特征

DevOps是一系列软件开发实践,强调开发人员(Dev)和运维人员(Ops)之间的沟通合作,通过自动化流程,使得软件构建、测试、发布更加快捷、频繁和可靠。

1. DevOps强调一种文化

在很多企业中,开发和运维人员通常隶属于不同部门,有着不同的工作环境,采用不同的沟通方式,使用不同的开发或运维工具,并且有着不同的业务目标,这使得他们之间形成一道参不透的墙。

DevOps实际是一种文化上的变迁,强调开发、运维、测试等环节之间的沟通合作。意在帮助这些人向着一个共同的目标努力:尽可能为公司提供更多价值。为了支持这种合作的发生,需要在团队内部文化和企业组织文化两个层面做出努力。

2. DevOps是一种实践

所谓DevOps,就是将敏捷方法延伸到Production!

DevOps主要是为了将敏捷开发实践扩展到运维阶段,进一步完善软件构建、验证、部署、交付等流程,使得跨职能团队能够完成从设计到生产支持等各环节的工作。

3. DevOps包含一系列工具链

DevOps是一种融合了一系列基本原则和实践的方法论,并从这些实践中派生出了各种工具。这些工具体现在软件开发和交付过程的不同阶段:

  • 编码:代码开发和审阅,版本控制工具、代码合并工具
  • 构建:持续集成工具、构建状态统计工具
  • 测试:通过测试和结果确定绩效的工具
  • 打包:成品仓库、应用程序部署前暂存
  • 发布:变更管理、发布审批、发布自动化
  • 配置:基础架构配置和部署,基础架构即代码工具
  • 监视:应用程序性能监视、最终用户体验

DevOps对测试提出了哪些挑战

刚参加工作时,我参与了某Audi系汽车电子的软件研发,采用的是传统瀑布开发模式。在整个项目生命周期中,前半部分设计和编码,后半部分用来测试。然而我在东家工作了两年,也没能等到产品交付到用户手上。直到去年,我们的软件才得以量产并投入市场。在这4年中,产品从未交到用户手上,因此无法验证它所带来的价值,也没有任何机会得到用户反馈从而适应变化。

后来,我又参与一个银行项目,我们采用敏捷的开发模式,全功能团队,开发测试并行,每2-3周就交付一个版本。但因为没有真正发布到生产环境,我们仍然无法及时得到有效的用户反馈。

现在,我们采用DevOps的优秀实践,开发和运维协同工作。每个迭代完成,或者每修复一个线上缺陷就立即部署到生产环境。这样,我们就能够迅速从用户处获得反馈并且快速做出响应。

通过参与传统、敏捷和DevOps的项目,我深深地感受到流程的改进对团队以及项目的产出和质量所带来的改变。

那么,这些改变究竟是对测试提出了什么样的挑战? 我认为有以下几点:

1. 频繁部署

在采用DevOps之后,我们能够根据项目具体情况做到每天甚至一天多次部署。在生产环境频繁部署软件,最大的挑战就是测试。以前,测试基本上都在开发阶段之后和产品上线之前完成。但现在,不再有充足的时间留给QA团队去发现问题再抛给开发团队来修复。那么,速度成了测试面临的一大挑战。

2. 自动化

DevOps强调将流程自动化,测试作为其中一个重要环节,势必要大规模实现自动化。因此测试人员的自动化编码能力正在面临极大的挑战。

3. 实践和反馈

敏捷提倡我们要拥抱变化,更多的是要适应需求的不断变化。虽然一部分功能性需求是明确又具体的,我们清楚的知道用户想要什么,也因此易于测试。然而,也有一些非功能性需求的验收标准没那么明确,比如:提高应用性能达到良好的用户体验。我们如何才能验证用户体验是否真的良好呢?仅仅通过性能指标吗?当然不是,满足指标只能说明一部分问题,唯有真实的用户数据和反馈才是可最靠的。

4. 协作

敏捷强调全功能开发团队的共同协作,但这仅仅止于开发阶段。而DevOps注重Dev、Ops和QA三个群体之间的密切协作。因此,良好的角色定位能够帮助测试人员将价值最大化。

我们是如何做测试的

Laurent曾经在Hiptest上发表了博客《Shift left and shift right: the testing Swing》,提出了一个有意思的测试矩阵,从四个维度进行分析,描述了当软件开发模式从瀑布到敏捷、再到DevOps转型时,测试该如何响应变化。

Laurent提出一个测试左移和右移的概念:

  • 测试左移,就是指在开发阶段之前定义测试。
  • 测试右移,就是直接在生产环境中监控,并且实时获取用户反馈。

在敏捷开发的生命周期中,我们通过每一次迭代来丰富和更新产品,以使其最大限度地符合客户对系统的需求。当时测试的关注点基本停留在开发阶段,以保证产品达到上线标准。引入DevOps之后,我们不仅要关注产品的质量是否达标,还需要使价值假设得到及时的验证。因此,我们不仅要将测试左移,在开发环境验证功能的可用性,还要进行测试右移,通过监控产品在生产环境的运作情况,来验证其价值并获得反馈,从而持续改进。基于这些理解,我在项目上做了初步的尝试并取得良好的效果。我将这些尝试和实践总结为以下几点:

1.如何保证新功能得以实现?

在开发环境,我们开发新功能,并且通过测试保证其达到产品验收标准。

首先,使用BDD(Behavior Driven Development,BDD)的方式定义用户需求,这样用特定的语言来描述用户行为,能够使各个角色(测试、开发、产品负责人、市场等)对业务价值达成一致的理解,从而使其从需求到最后的测试验证,进行高度的协作和沟通,最后交付最有价值的功能。同时,QA能够提前Review故事卡,补充验收标准。除此之外,BDD方式的用户需求可以直接指导测试,后续我会写到。

其次,采用单元测试来验证最基本的代码逻辑。在编写单元测试时,建议Dev和QA Pair工作。单元测试可以认为是编码的一部分,要对系统的代码逻辑有深入的了解,因此,Dev是最合适的人选,而QA可以帮助测试覆盖的更全面。

最后,每一个功能都要严格按照故事卡的AC(Acceptance Criteria)进行验收,并采用探索性测试方法来对新功能进行无死角测试。

2.怎样验证新功能的价值?

我们将新功能部署到生产环境以后,接下来就应该衡量业务价值是否达到预期

验证预期的一个好方法是衡量用户的行为变化。比如:在上传图片的功能后面添加了一个预览按钮,但用户却极少用它,很可能是因为用户根本不需要这个按钮,或者按钮放在了不恰当的位置导致用户不方便使用,亦或是按钮样式不够友好,导致用户没有欲望使用它。这时候,该按钮的业务价值就没有真正达到,是时候调整一下了。

3.如何确保已有功能不被破坏?

在软件开发中,任何代码都不可能完全独立存在,一行代码的变更也有可能导致系统的全面崩溃。那么,如何保证在开发新功能的同时,已有功能不被破坏?换句话说,如何做到全面的回归测试?人力是最高成本,也有现实的局限性,比如,人手不够,重复做同样的事情人会变得烦躁,手不够快导致效率低下等。因此,自动化测试才是不二选择。

将BDD需求直接转化为自动化测试用例。每个测试用例都应该讲一个关于应用程序的故事。当一个测试用例使用一致的业务术语定义时,它的可读性会比较高,且容易自动化。与此同时,上一个迭代的用例在下一个迭代就可以迅速转化为回归测试的基线。

支持BDD的工具有很多,比如:Cucumber。简单举个例子,如图:

BA用BDD方式定义用户需求,QA Review并补充AC,然后将其编写为自动化测试脚本。如果QA的编码能力较弱,可以让Dev协助完成代码实现的部分。这也充分说明了协作的意义。

最后,也是更重要的部分,测试应该集成在CI中。每一次Build或者每天都要去执行测试,验证已有功能是否完好。这样才会对没有预期到的变化产生的问题给出快速反馈。

另外,做一些性能测试、兼容性测试、和安全性测试等等。

4.怎样验证产品的可靠性?

有时候,某些缺陷并不是源于代码的错误,而是一个不好的用户体验,或者只有当数据达到一定量时才会出现,测试人员是无法模拟这种类型的测试的,因此直接在生产环境监控变得高效又可靠。通常我们需要监控两种特性:性能和可用性。

使用工具持续获取用户数据,或者使用log持续获取性能信息。这有助于监控产品部署到生产环境后是如何正确运作的。快速启用一个功能,在生产环境实时监控验证其业务价值,获取到有效且快速的用户反馈,加之拥有持续部署的能力,我们能够在出现问题的时候快速做出反应,从而使得我们的产品更加可靠。

这里实际上融入了《QA in Production》的理念。现如今,已经有很多工具和方法支持在生产环境做测试了。篇幅太长,这里就不做详细阐述了,请参考原文

到这里,再来回顾一下,我们的实践是否真的卓有成效。

  1. 用BDD的方式定义用户需求、编写测试,有益于不同角色之间的一致理解和共同协作。
  2. 自动化测试解决了频繁部署所带来的挑战,同时保证产品的整体功能持续得到回归和验证。
  3. 在线监控能有效地验证不确定需求,通过生产数据分析和预警问题的发生,并且快速获取用户反馈从而及时调整。除此之外,这一点也充分体现了Dev、QA和Ops的协作,像监控等原本只能Ops做的事,现在Dev或QA一样可以做。

写在最后

测试是一种活动,曾经我们通过它来验证产品是否达到上线标准。现在DevOps模式下,我们需要在各个阶段不断地执行测试活动,以达到产品质量的持续改进。

而QA(Tester)仅仅是一种较多进行测试活动的角色。敏捷一直强调“团队为质量负责”,测试不再是QA(Tester)的专属。DevOps模式更是对测试、尤其是自动化测试提出了更高的要求,也对QA的编码能力提出了极大的挑战。作为团队成员,每个人都有责任了解开发流程、提高测试技能,把好测试这一关。但是,测试活动作为QA(Tester)的主要职责之一,提高自动化测试技能,就是当下每个QA(Tester)最为紧急且重要的事情了。


更多精彩洞见,请关注微信公众号:思特沃克

Share