Automotive Aftermaket

关于汽车后市场

汽车后市场

从概念上来讲,当一辆汽车被购买,完成新车交付之后,就进入了汽车后市场,在汽车使用的生命周期中产生的所有后续的商业行为都可以算在汽车后市场的业务领域之内,所以这个概念涵盖范围很广。主要的服务种类可以分为以下类别:

  1. 金融保险
  2. 配件用品
  3. 维修养护
  4. 救援
  5. 汽车租赁
  6. 汽车改装
  7. 二手汽车
  8. 违章查询
  9. 洗车
  10. 停车
  11. 油耗

为了更切合项目的背景与上下文,本文从品牌商的角度,以配件用品和维修养护相关的服务为重点,力求为项目成员提供一个粗略的行业和市场背景知识,希望对后续项目的发展方向能有启发。

汽车后市场供应链

后市场规模

作为汽车销售市场的延伸,汽车后市场的市场规模必将受到汽车销售市场的影响。处于使用状态的汽车数量,也就是汽车保有量,可以看作整个后市场的广度基数,而针对这些汽车使用中产生的各类交易和服务,可以看作整个后市场的深度,二者大致决定了整个汽车后市场的市场规模。

根据罗兰贝格提供的销售数据,全球市场的新车销售变化表明,相比于已经进入成熟期的欧美和日韩市场,中国市场依旧处于快速增长期。在可以预见的未来几年里,不论是汽车销售还是后市场的服务都还将处于上升期。

figure 1

相比于美日德地区,中国的汽车保有量稳定攀升,预计在2021年突破2亿辆。随着新车的增速逐年放缓,平均车龄将逐年上升,也就意味着对后市场服务的需求将会更高。所以不论言就深度还是广度,汽车后市场这个从2009年就开始点燃的概念还将有非常广阔的市场竞争空间和潜力。

figure 2

戴姆勒的销售数据也表明,中国区正在成为带动整个戴姆勒汽车销售的重要牵引力,2016年的年报中也特意提及了中国市场的上升和将来,有意者可以参阅Daimler AG Annual report 2016

后市场供应链

回到前面所提的维修养护和配件用品的后市场行业,其供应链体系,跟汽车销售一样,同样还是分为三个环节:生产、分销、零售。区别在于,零部件的生产商更多,因而从生产到销售的路线也就更多更复杂。

在开始之前,我们先熟悉几个重要的概念:

  • 主机厂:因为握有发动机核心技术,汽车厂商会将一些非核心技术的零部件交由一般厂商来提供。所以相对于一般厂商,业内常用主机厂指代汽车公司本体。
  • 原厂件:由主机厂或授权配件商生产,具备主机厂标识的零配件
  • OEM:original equipment manufacturer。授权配件制造商
  • OES件: original equipment supplier。由授权配件商生产,具备授权配件商标识的零配件
  • 非授权生产商:未经主机厂授权的零配件生产商
  • 品牌件:由非授权生产商生产,具备其生产商标识的零配件
  • 独立后市场:主机厂授权渠道(4S店)之外的渠道,例如:汽车维保连锁店,独立维修厂,大大小小的路边夫妻店,以及近几年大火的O2O汽车服务提供商,配件、保养电商平台等等

原厂件、OES件和品牌件的质量价格对比如下图:

![figure 3]

原厂配件的主要销售渠道是经由主机厂售后部门直接到达各个4S店,再经由4S店销售给独立后市场的经销商、以及通过维修保养服务直接销售给顾客。在车辆保修期内,消费者更倾向于选择4S店进行维修和保养,因此在4S店和厂家共生博弈关系的保护下,2016年,原厂配件在配件后市场中占比约23%。

而OES件由于价格偏高,品牌识别度较低,市场份额仅为10%。这个可以理解,在价格悬殊的原厂件和品牌件之间,追求性价比的顾客会在品牌件中仔细挑选,对价格不敏感的顾客会优先选择原厂件,省心省力。而品牌配件经过消费者不断的验证和提升,最后通过市场选择的品牌配件则会进一步挤压OES配件的生存空间。

品牌配件占据后市场配件的67%。完全不同于原厂配件的流通方式,品牌件主要通过独立后市场进行销售。经销商层级复杂,也同样混乱无序。

![figure 4]

未来品牌配件的市场还会进一步扩大,主要原因有:

  • 中国市场车龄上升,出保车辆占比上升,使用品牌配件的车辆占比上升;
  • 电商体系的快速发展虽然短期内使市场复杂和混乱程度增加,但是从长期来看,同样是在加速流通层级的扁平化,促进配件信息体系的标准化,进而会为配件的自由流通打破壁垒;
  • 政策推动,详见后文。

相关政策

近几年随着汽车经济长足发展,与之相应的汽车维修业的发展非常迅猛,同时也存在市场结构不优,消费不透明、不诚信的问题。与汽车后市场相关的政策不断出台,推动汽车维修业从规模扩张型向质量效益型的转变。与零配件发展最有直接相关的政策有:

政策内容可以总结为以下几点:

  1. 取消汽车销售的授权备案制
  2. 破除配件渠道垄断,促进配件自由流通。经销商可以将原厂配件转售给非授权经销商
  3. 鼓励自主商标的独立售后配件
  4. 汽车供应商不应限制维修技术信息、测试仪器和维修工具的可获得性
  5. 统一汽车零配件的编码原则,建立完整的零配件追溯体系
  6. 经销商可以销售非授权品牌的汽车,及汽车零配件
  7. 禁止经销商在卖车过程中对后市场产品(配件、金融、保险等)捆绑销售

新政策传达出的汽车及汽车后市场方向:

  • 销售商与售后服务商可分离
  • 授权经销与非授权经销并行
  • 汽车销售和售后服务销售的环节更扁平、信息更公开
  • 鼓励经销商脱离供应商,变成更独立的销售环节
  • 整合经销商,打破现在不同供应商及其授权经销商之间的壁垒,重新分配经销商资源,减少资源浪费
  • 独立售后服务会成为重要竞争地,并且,独立售后的成熟度是独立销售的重要影响因素

项目背景

以上关于后市场供应链的分析是基于乘用车市场整体而言的,用来对中国的汽车后市场行业有一个粗略的概览。在映射到我们的项目上时,并不建议直接代入,因为不同品牌商面向不同的消费价值观的顾客群体,在后市场的市场表现必然不尽相同。例如,对于奔驰而言,车主对价格的敏感度整体更低,原厂配件和品牌配件的市场份额比应该会高于上面整体的1:3。

Daimler 集团

戴姆勒集团(Daimler AG)是一家总部位于德国斯图加特的汽车公司。Daimler AG的整体业务被划分为:Mercedes-Benz Cars,Daimler Trucks,Mercedes-Benz Vans,Daimler Buses,以及Daimler Finance。

figure 5

近五年股价如图,市值约720亿欧元。单从市值来看,跟大众不相上下,在汽车行业仅次于丰田。

figure 6

蔡澈在致股东的信中表示2016年是奔驰最成功的一年。诸多因素的作用下,2016年奔驰重新夺回豪华车销量第一的宝座。根据年报,2016年Daimler AG 的收入为1532亿欧元,较2015年上涨3%,净利率5.7%。按照业务模块的收入如下:

figure 7

乘用车和卡车业务是其收入的主要来源,我们重点关注乘用车业务。下图为Mercedes-Benz Cars在各主要市场的市场份额,在德国的占有率最高,达10%;随着中国经济发展,2016年的汽车销售市场,奔驰车在中国的市场占有率也达到了2.1%,跟北美市场相同。

figure 8

Mercedes-Benz Cars的销量组成如下图所示,可以看出以SUV系列和C系轿车最受市场欢迎:

figure 9

2016年MB Cars在全球销售2198000辆,其中中国销售494000辆,在全球占比约22%。再看近五年,从2013年开始,奔驰在中国的销量每年涨幅都超过20%,上涨势头依旧强劲。

figure 10

授权经销商网络的发展数量在经过2013年大幅上涨后,增速已经开始放缓,特别是在今年新的汽车销售政策出台后,经销商集团或将面临战略转型。

figure 11

北京梅赛德斯-奔驰销售服务有限公司 BMBS

北京梅赛德斯-奔驰销售服务有限公司是2012年由戴姆勒和北汽集团以51:49成立的合资公司,主要负责奔驰汽车在中国的销售,包括国产车和进口车。

戴姆勒大中华区投资有限公司

负责如下业务单元:梅赛德斯-奔驰轿车、梅赛德斯-奔驰轻型商用车、戴姆勒卡客车、梅赛德斯-奔驰金融、戴姆勒零部件贸易服务以及研发中心。其业务范围覆盖中国大陆、香港、澳门及台湾地区。

北京奔驰汽车有限公司

北京奔驰汽车有限公司(简称北京奔驰)是北京汽车股份有限公司与戴姆勒股份公司、戴姆勒大中华区投资有限公司组建的合资企业,于2005年8月正式成立。目前生产梅赛德斯-奔驰C级轿车、E级长轴距轿车和GLK级豪华中型SUV。

利星行

作为奔驰全球最大的经销商集团,利星行在中国77个城市拥有105家经销商,134个网店,2016年售出奔驰汽车157505辆,占奔驰中国总销量约32%。2017年5月,Daimler AG投资利星行,成为利星行股东,持股15%。值得琢磨的是,这笔投资的发生时间与新的《汽车销售管理办法》政策发布的时间(2017年4月)非常接近。

奔驰和利星行的合作可以追溯到1986年,当时两家公司在香港成立了梅赛德斯奔驰(中国)有限公司负责奔驰汽车在中国的销售,戴姆勒持股51%,利星行49%。2005年,随着北京奔驰汽车有限公司的成立,开始生产和销售国产汽车,原本的奔驰中国则负责进口车销售。这样双线营销体系并行的方式,利星行作为股东、总代理和经销商的身份很容易引发市场混乱,因此才有了2012年的成立的北京梅赛德斯-奔驰销售服务公司,由后者统一负责国产车和进口车在中国地区的销售。

厂商在后市场中的位置:未来与挑战

厂商与经销商的博弈

在中国,传统的汽车4S店模式发展至今,厂商和经销商之间的关系虽然是互利共生,但同时也暗藏冲突的利益诉求。

传统销售模式的好处在于制造和销售的分工明确:厂商致力于产品的研发和制造,销售给一级经销商之后完成资金回收,降低资金链风险;经销商致力于铺开销售网络,负责零售和提供售后服务。

厂商因为握有主导权和话语权,对授权经销商可以强势提出严苛的销售指标,冷门车型和热门车型的捆绑销售,以及售价要求种种束缚,使得授权经销商为了完成销售指标,催生了二级经销商市场的发展,进一步加深市场结构的层级。

二级经销商一般为非授权经销商,从多个品牌4S店拿车进行销售,因为不受厂家MRSP(厂商建议销售价格)的限制,价格相对比较灵活,一般低于4S店。同样也因为不受厂商控制,缺乏系统的管理,二级经销商市场上的不透明度和对消费者的欺瞒空间为公众所诟病,捆绑售后一条龙(牌照、保险等)进行新车销售在二级市场上屡见不鲜,最终买单的还是消费者。

因此,4月发布的《汽车销售管理办法》正是为了给经销商松绑。压缩销售层级,同时开放了单一品牌授权的模式,打破了品牌之间的壁垒,让汽车的销售网络和资源进行整合,对于现有的4S店模式无疑是重大的变革和挑战。同样,对于厂商来讲,未来的渠道建设也面临创新和机遇。

传统的4S店模式下,每家经销商都只忠于一个品牌,经销商会倾其所有资源为自己代理的品牌打开销售的局面和渠道,经销商的利益虽然会被厂商挤压,但是二者方向是一致的。打破品牌的限制之后,短期之内厂商当然还是握有把控权,但是长远来看,如果新兴的销售模式兴起,例如汽车超市或是电商+线下体验店模式,一家经销商销售多品牌汽车,品牌跟品牌之间的竞争将集中在主机厂和产品层面,经销商作为销售平台不再直接参与品牌竞争,消费者是买奔驰还是买宝马都随意,只要不离开我这家店就行。这种销售模式下,厂家对经销商的把控力大大减弱,也失去了原本经销商层级对终端顾客的影响力,例如,传统模式下,每家经销商对于有购车意愿的顾客都会进行商机管理,促成销售。所以新政策出台后,看似有利于经销商,但是厂商的余威尚存,对绝大部分经销商来说,短期内大约还是先观望大环境,谋定而后动。只有经销商和厂商之间达成共识和共赢的策略,共同以创新的思维来提供更卓越的产品体验和服务才能促使变革的产生。值得一提的是,阿里、苏宁、国美等零售行业的巨头开始布局汽车销售这盘棋,这将倒逼现在传统经销产业链上的各方,不得不尽快寻找出路和探索新的商业模式。

figure 12

要分析一个正在变化中的市场格局下,主机厂和经销商未来怎么合作,关键点在于共同的利益方向,也就是消费者的诉求。不管市场怎么变,消费者对于汽车销售的诉求没有根本的变化,那就是优质的产品和服务。对于主机厂而言,盈利的模式也没有根本的变化,就是卖出更多的车,以及在售后过程中卖出更多的原厂配件。对于经销商(暂且还用这个名词)而言,优质的产品由厂家来提供,最符合经销商自身比较优势的盈利模式就是提供优质的服务。

如果在售后环节,同样打破了品牌之间的壁垒,也就是说,未来的经销商跟独立售后一样,可以针对多个品牌同时提供汽车服务,那么优劣势是什么?

优势:

  • 已经积累的顾客资源
  • 4S店网店覆盖
  • 跟厂商的直接合作是优质服务的保证
  • 规范的管理和更好的服务体验

劣势:

  • 相比于灵活的独立售后服务商,经销商受到厂家的约束更多
  • 单品牌的售后技术和零配件供应需要向多品牌转变
  • 传统管理模式

发展趋势

  1. 未来一段时间之内,传统4S店模式依然是汽车销售的主导模式。想要真正地割开销售和售后的环节,必须以定义清楚的售后的保障方式为前提。汽车不同于一般消费品的点就在于其售后服务是消费者在做决策时很重要的影响因素,在汽车超市买的车,出现保修的问题如何处理,在哪儿处理,都必须先获得权威的保证。
  2. 长远来看,经销商的销售层级会趋于扁平,合纵连横
  3. 可能出现更多的厂家直销(例如日本的汽车销售模式)或是网络直销(例如特斯拉)等由厂家主导的销售模式。
  4. 授权经销的售后部门,可能会渐渐独立成服务提供商。服务市场竞争的重心只在于服务水平,以及针对不同层级的消费者划分竞争区域。
  5. 零配件市场的销售渠道整合。

主机厂的机会和挑战:

作为主机厂,卖车卖配件是其核心的商业模式。单车利润薄弱,而配件是其利润的主要来源。因此:

  1. 通过提升售前售后服务体验来巩固原厂配件的市场地位,可能是主机厂未来提升顾客与品牌粘合度的主要方式。

  2. 降低原厂配件的销售门槛。目前只有授权经销商具备对外销售原厂配件的资质,所以独立的售后服务提供商必从4S店购买原厂配件,再跟售后服务一起卖给消费者。长远来看,破除授权制之后,零配件的流通将会更加自由,在这个趋势下,不论厂商是否设置门槛,原厂配件的受众都会从4S店扩大到独立售后服务商。从主机厂角度,主动开放零配件的市场,通过更多的优质服务渠道进行原厂配件的销售,有助于提升原厂配件市场地位及收益。

Oct 12th, 2017

A User Story Discussion

一个由浅入深的User Story小分享

本文写作初衷来自于第一个国内交付小项目,项目Scope不大,是某公司内部一套通用的项目管理系统的首页Dashboard制作(和集成)。交付团队进场时,需求整体已经基本确定,主要的工作量集中在前端开发,以及跟客户现有的多个系统数据集成上,计划的开发周期四周。作为一个BA参与的第一个国内交付项目,尝试梳理一下这个小项目上的经验和感想,再加上新Sponsor的作业施压,便有了这篇博客。讨论的范围从User Story的原则和组成、到AC的写法,涉及少量BDD(主要是Gherkin语法)和实例化需求的分析。

User story原则

作为BA,大家都耳熟能详的INVEST原则,这里赘述一次是为了引出在项目中产生的疑问,熟悉这部分内容的可以直接针对疑问部分进行讨论。

我们都知道,在敏捷方法下,一个好的故事应该很清晰地体现对用户或客户的价值,同时具备以下特点:

Independent

我们要尽量避免stories之间的相互依赖,在排优先级和迭代计划的时候,依赖会导致估算和分工变得困难,因此每一个story都要尽量独立。通常我们可以使用以下方法来降低依赖:

  1. 将互相依赖的story合并成一个大的、独立的故事
  2. 换一种方式将故事进行拆分

Negotiable

Story只是一个feature的简单描述,细节在跟客户与开发团队的讨论中不断完善。Ron Jeffries提出的User story的3C原则:Card,Communication,Confirmation一样,沟通是Story非常重要的一环,因此在story的编写过程中,文档的详尽不是我们追求的方向

疑问1:实际的项目上,为了能节省跟开发沟通和确认需求的时间,也为了给开发人员提供详细的需求说明,详尽的文档(AC)反而感觉是必须的?即使是kickoff的过程中,更多也是偏向于给跟开发确认需求,而不是negotiate,Negotiable具体是在哪个层面上?

Valuable

User Story应该很清晰地体现对用户或客户的价值。这也是对故事排优先级的基础,比较理想的状况是让客户(或者与客户一起)进行故事的编写,这样也更容易达成Negotiable的原则。

Estimable

一个Story一定要是可以估算的,开发人员需要对Stories进行估算,以便于确定优先级、工作量和开发计划。一般情况下,故事拆分的粒度越细,越有利于估算的准确性。

Small(Size appropriate)

故事尽量越小越好,要确保在一个迭代以内可以完成;但也不要太小,比如小到无法在一张卡内保证有价值的交付。

Testable

如果说用户故事的描述代表了需要交付的价值,那么验收则是对价值的验证和确认过程。因此一个故事必须是可测的,也就意味着验收标准必须是清晰客观的。例如,如果我们要编写一张提升用户体验的故事,验收标准如果是:app的响应速度要很快.,这个故事就是不可测的;可测的故事是:当用户进行xx操作时,从xx到xxx的响应时间必须在0.1秒以内。


User Story构成

一个User Story通常会包含以下几个方面的内容,默认Narrative和验收标准(以下简称AC)为必需条件。一个没有明确定义出Narrative和AC的story,开发团队可以拒绝开发。除此以外,有些卡还会附上一些补充说明的部分,例如Scope、Notes、以及相应的设计图等等。

Narrative

Narrative某种意义上,就是用户故事的核心。通常包含角色、功能和商业价值三个要素。 通常的叙述格式为:

1
2
3
As a <role>,
I want to <activity>,
so that <Business value>

中文格式对应为:

1
2
3
作为一个<角色>,
我想要<功能>,
以便于<商业价值>

例如

1
2
3
作为一个ThoughtWorker,
我想要在手机上填写Timecard,
以便于我能在没有电脑时,也可以随时随地提交工时信息,确保公司能按时收回账款。

格式虽然简单,但是往往也最容易被忽视。这里我个人想强调的是<商业价值>部分。面对一个进入到写卡阶段的需求,<角色><功能>往往都已经相对明确,BA不过是做个总结,到了<商业价值>部分,很容易觉得功能的价值当然是不言自明的,不需要太多解释,(甚至有时还会觉得写了dev也不会看),这时很容易就敷衍了事,不经思考写上一句正确的废话或是从其他卡上复制一个差不多的,草草了事。

这样做很不好的一点是,习惯性默认了所有需求的合理性。可是需要提醒的是,需求在写卡时,其实就已经进入了验证阶段。一个连so that都写不出个所以然的需求,直接进入到开发阶段这本身就是最大的浪费。

在紧张的开发节奏中,来自多方的复杂的压力往往推着人不断向前,很容易忽视对需求本身的深思和探究。保持在写卡时习惯性地反问这样做有什么价值?能多大程度上实现原本的商业预期?可不可以不做?是反思价值和需求合理性、优先级的时机。所以,重要的不是要写给他人看(当然,真正要实现敏捷,必然是需要每个人都了解商业价值的上下文),而是作为需求分析师的角色时,必须要思考的过程。

疑问2: 所以Narrative可以写多条么?

Acceptance Criteria

AC是需求的细化和清晰化。我们都知道每一个User Story都应该是可估算、可测的。通过怎么来估算和测试呢?清晰的验收标准。

Narrative中通常只包含功能的描述,细节的明晰和界定是在不断地沟通和思考中产生的,这些界定出来的细节要求,便构成了我们验收和测试时的对象。将这部分细节要求以一种通用的领域语言呈现出来,形成业务和开发之间的沟通桥梁,这是我理解的写AC的过程。具体怎么写,我们后面再进一步讨论。

Scope

其实有了清晰的AC,Scope也就相应明确出来了,但是有时在一个Epic拆成不同的Stories导致Stories之间互相存有依赖或关联时,通过Scope的补充说明,可以更明确地界定出Story的上下文范围,方便理解。

但是,一定要注意的是,Scope的说明文字不能取代沟通。

Notes

如果说Narrative 覆盖了需求点,AC覆盖了验收的feature,若还有一些细节或说明条件需要补充,可以记作Notes。例如:标题输入不能超过15字.

Design

不论是Hi-Fi还是Lo-Fi的Mockup,图更能说明一切,最Lo-Fi的Sketch都胜过最详尽的文字。

在User Story里要写哪些模块,具体还可以参考myThoughtWorks上的历史讨论:Where do you put acceptance criteria?


怎么写AC?

所以,既然AC是user story里最关键的部分,AC要怎么写?在我刚进ThoughtWorks学习该怎么写AC之时,当时Buddy让我看了一些User Story的示例(myTW也曾有过讨论:Patterns for effective Acceptance Criteria),发现大部分的验收标准都是以某种标准格式Given...When...Then...的格式写成,所以照猫画虎,AC仿佛也写得有模有样:不就是将用户的行为拆成一条条的假设场景嘛,多容易啊。但是长期以来,我都有一个疑惑:明明用一句话就可以说清楚的事情,为什么要写上这么多的废话?比如:

1
2
3
4
- Given 我在xxx首页
- And 输入已注册的用户名和密码
- When 点击登录
- Then 进入登录后页面

这么长一串完全可以用一句人话说清楚:

1
- 用户可以使用已注册的账号密码登录网站。

大量的Given...When...Then...型AC带来的结果是,一张User Story动不动就写成一篇作文。从可读性角度来说,效果当然也不赖,从用户场景出发也可以帮助我们考虑到更全面的场景,所以虽然嫌累赘,但也不是不可以坚持。直到这次项目在邱大师的指引下,接触到了两个概念——BDD和Specification by Examples,于是豁然开朗,为什么AC要采用这样的方式来写成。在此,便需要引出一个重要概念——活文档

活文档

引用一下《Cucumber:行为驱动开发指南》对活文档的描述:“Cucumber测试同传统的规格说明文档一样能被利益相关人阅读和编写,然而其独特的优点在于,你可以在任何时刻给他们一台计算机让测试执行,结果会告诉你测试有多准确。这意味着,你的文档不再是一种写完后就慢慢过期的东西,而成为一种能随时反映项目真实状态的活的东西。”

Cucumber的Gherkin语法非常简单,举个例子一目了然:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Feature: Sign up
	Sign up should be quick and friendly.

	Scenario: Successful sign up

		New users should get a confirmation email and be greeted personally by the site once signed in.

		Given I have chosen to sign up
		When I sign up with valid details
		Then I should receive a confirmation email
		And I should see a personalized greeting message

	Scenario: Duplicate email

		Where someone tries to create an account for an email address that already exists.

		Given I have chosen to sign up
		But I enter an email address that has already registered
		Then I should be told that the email is already registered
		And I should be offered the option to recover my password

是不是很熟悉的格式?是的,如果AC能直接变成自动化测试的场景,那么所有这些人读起来略显累赘的格式便很合理了——人机都可读。因为文档直接跟程序挂钩,一旦发生变更,所有的变更会先在测试场景上变更,再变更代码以让新的测试通过,从而能自动保持文档和需求的同步,活的文档 便很好理解了。

这时,你可能会问了:Cucumber是适用于BDD的,如果我们没有采用BDD,那么AC也有必要这样写么?我们后面再说。

Specification by Examples

说完格式我们再来看内容,假设我们写AC的目的是为了更好的覆盖自动化测试的场景(不管是否采用BDD,区别主要是在于测试先行还是后发生)。所有符合Given...When...Then...格式的内容,都可以轻松程序化么?看一个例子(这就是我在项目上写的卡):

1
2
3
4
- Given: 我是已登陆pxxxk的项目经理
- When: 查看项目“任务项”
- Then: 我看到的任务项是按照*计划到期时间*的正序排列
- And: 已过期的任务项以红色显示

从人的阅读理解上来讲,这确实表达出了两个关键的规则:

  1. 按照deadline的正序排列;
  2. 过期的任务标红。

在跟dev们kickoff的时候,这条AC大家可以理解,也就可以顺利通过,不会产生什么疑问。可是如果将这个测试场景自动化,这可以直接挪用么?什么是时间的正序?什么样的任务项算是过期?

这里便引申出第二个概念——实例化需求(Specification by Examples)。

同样是上面这个例子,我们换一种表达方法:

1
2
3
4
5
- Given:我的项目上所有进行中的任务项的*计划到期时间*分别为:4月17号,4月18号,4月19号,4月20号
- And: 今天是4月18号
- When:查看项目“任务项”
- Then:我看到的任务项排序从上到下为: 4月17号,4月18号,4月19号,4月20号
- And:4月17号的任务项会标红显示已过期

同样是针对具体细节的描述,区别体现在:前一种方式,是细节的需求抽象之后再以概念表达出来;后者则是通过一个实例的表现来描述需求。

通常情况下,前一种表达会让我更有安全感,因为抽象之后的说明会更全面,更能切合我们对需求的定义和分析;而后者会让我忍不住担心:如果程序只cover了这一种测试情况怎么办(例如程序仅仅是让这项测试场景通过而不是完整实现了需求)?解决方式可以是添加多场景(具体可以参考Matt Wynne的《Cucumber:行为驱动开发指南》),这里就先不详细展开了。

我们可以看到实例化需求说明之后,AC就成了一条清晰的测试场景,有实例数据input,也有了应该产生的output。既能够让开发人员理解,又利于程序的编码和自动化测试。

再回到前面的问题,难道BA写AC就应该写成这种可执行的文档么?如果我们不采用BDD的方式进行开发,还需要以这种格式写成AC么?

实际上,如果我们翻查各种关于user story的书籍和经验博客,并没有一个人给出是的回答。在各种敏捷User Story的示例中,AC的格式并不固定(checklist就很常见),重点是从需求的角度出发,尽可能覆盖住各种场景,保持需求整体的逻辑自洽。将场景转变为可自动化测试的场景确实也不在BA的职责范畴之内,实例化需求也更多用于测试文档的编写(Cucumber本身也是一个自动化测试的框架)。可是在一个团队内,BA写完AC,QA再将需求转化为验收测试文档算不算是一种写文档的浪费?怎样才能避免团队在文档撰写上的浪费呢?比较推荐的方法是,在给每一张卡定AC的时候,由Tech Lead、QA和BA(three amigos)一起对故事卡进行沟通,从不同角度对场景进行完善,确保AC作为活文档的适用性。不管项目上是否采用BDD,我个人认为,能够在实现清晰的需求描述的同时,兼顾自动化测试友好的AC场景,对整个开发是百利无害的。

这篇博客虽然讨论的是User Story,但其实很大程度上是基于BDD的价值的假设。因篇幅和主题限制,并没有对BDD本身展开过多介绍,对BDD感兴趣的人可以参考林冰玉的这篇博客:说起BDD,你会想到什么?

参考信息:

[1] Wynne, M., & Hellesøy, A. (2013).《Cucumber: 行为驱动开发指南》,人民邮电出版社

[2] Adzic, G. (2012). 《实例化需求》,人民邮电出版社

[3] 林冰玉,说起BDD,你会想到什么? http://insights.thoughtworkers.org/when-we-talk-about-bdd/

[4] 毛超,醒醒吧少年,只用Cucumber不能帮助你BDD http://insights.thoughtworkers.org/bdd/

[5] https://github.com/cucumber/cucumber/wiki/Gherkin

May 3rd, 2017

Acceptance Test Anatomy

关于验收测试的剖析

原文地址:https://gojko.net/2010/06/16/anatomy-of-a-good-acceptance-test/

作者:Gojko Adzic

敏捷验收测试的长期收益来自于活文档——它是一份系统功能的描述文档,可靠,易于访问,并且比代码更容易阅读和理解。为了能够成为有效的、活的规范说明,验收测试必须以这样一种方式写作——能够让他人在几个月甚至几年以后,轻易地明白他们在做什么,为什么做以及具体的描述是什么。这里有一些简单的启发式方法,可以帮助你度量和改进测试,使其更好地成为一份活的规范说明。

需要思考的五个最重要的点是:

  • 它必须是自解释的 (self explanatory)
  • 它必须要聚焦
  • 它必须是一个具体描述,而不是脚本
  • 它必须使用领域语言
  • 它必须是关于业务功能,而不是软件设计

这里有一个很好的例子:

figure 1

它具备了上边列出的所有要素。每当我在工作坊向人们展示这个例子时,我都不需要解释一个字。它的标题和介绍段落清晰地说明了测试数据的结构,足以让读者不需要回到数据中去了解规则。而下方那些例子则使之变得实际可测,并解释了边缘情况下的行为。并且,它聚焦在免费送货的有效性的这个非常特殊的规则上,没有解释要如何购买这些书,而仅仅只是专注于可用的交付机制是什么,也没有尝试谈论任何实现的细节。

这里也有一个非常糟糕的案例(取自于旧的FitNesse用户指南):

figure 2

这个测试写得如此糟糕,以至于它某种意义上反而成为了一个很好的例子,用来说明当人们对写好测试漠不关心时,会发生什么。

首先,虽然它有一个标题,和一些围绕表格的说明文字,看起来像是解释发生了什么,但是结果差强人意。为什么说这个测试简单?工资单检查到底检查什么?在“自解释”的石蕊试验中,它彻底失败了。(译注:石蕊试验是利用石蕊检验酸碱度的古老检验方式。现在经常被用于比喻一种立见分晓的检验方法。)

其次,这个测试到底核查了什么完全不清楚。我们不得不回退。它似乎是在验证支票是以唯一的数字打印的,这个数字是从系统中可配置的下一个可用的数字开始。也似乎是在校验每张支票上所打印的数据。而且每个员工打印一张支票(稍后再回到这里)。

这里有很多看似偶然的复杂性——名称和地址这两项数据,除了在建立员工之外,整个测试中的任何地方都没有使用过。还有一些与业务规则完全无关的数据库编号,用于匹配员工和工资检查器。

工资检查器很显然只是为了测试而造的。没有哪个公司会派一身Clouseau装备的Peter Sellers(译注:作者这里的引用来自于经典电影粉红豹系列,Peter Sellers在片中长期扮演了脍炙人口的角色Clouseau检察官。)在那儿一张张的检查支票。如果你有大量的员工,不得不打印支票,你当然不会想要手动检查。那么这正是这个测试的关键。

在测试的断言部分还有一个非常有趣的空白单元格问题,以及两个看似无关联的工资检查表。FitNesse的空白单元格用于打印测试结果进行调试和故障排除,但是不检查任何内容。所以这是一个必须要人工检查的自动化测试——这几乎推翻了自动化的目的。在测试中,空白格通常是不稳定性的一个迹象(稍后将更详细说明这点),它们往往是一个信号,说明你漏了些什么——要么测错了地方,要么就是缺少某个规则,使得整个系统过程可重复并且可测试。

这种语言是不一致的,这使得一开始很难在输入和输出之间建立连接。最下面表格中的1001值是什么东西?表头告诉我们这是一个数字——谢天谢地,我以为那是一根香肠。上面的表格中有一个“支票号码”,但是是什么样的支票号码?这两件事情之间有什么关联?

所以这个测试写的真的是一塌糊涂。

假设地址信息存在是因为打印的支票是结算清单的一部分,有了地址信息,结算清单就可以进行自动化的信封包装,这个测试还是至少漏查了一件非常重要的事情:那就是对的人拿到了对的工资金额。如果第一个人得到了两张支票,这条测试会愉快地通过。如果两个人互相拿到了对方的薪水,这条测试同样也会通过。如果印在支票上的日期是在遥远将来的某一天,远到我们的员工可能来不及兑现,这条测试照样还是可以通过。这些单元格空白的原因还隐藏着另一个规则:支票产生的时间顺序。这一点也没有给出任何说明。所以,这种功能缺口的技术修补方案,就是创建一个带给我们大量假阳性结果的测试。

这个测试是要检查一件事还是多件事情?没有上下文信息,我们很难下结论。如果打印支票的系统还有其他的用途,我会提出一个事实,那就是支票号码是唯一的,并且是从一个可配置的数字开始到每一个单独的页面。而即便我们只是用其来打印工资支票,这依然有可能只是一件事情(工资支票打印)的某一部分。

现在,让我们来清理一下。我们试着回溯,并且删掉所有附带的东西。首先,起一个很好的描述性标题,例如“工资支票打印”,然后,添加一个段落来解释测试结构。

一张支票上有收款人姓名,金额和支付日期。支票本身并没有名字和工资。如果支票打印在结算清单上,那么它也会有一个地址,用于自动信封包装。一个名字和地址的组合应该足够让我们将员工与他的支票匹配起来——我们真的不需要数据库的编号信息。通过商定的排序规则,我们可以让整个系统变得更加可测试——不论这个规则是什么,例如,按字母顺序排序。

让我们将场景拉到测试的开始。我们的背景是工资日期、下一个可用的支票号码、以及员工的工资数据。让我们明确阐述一下每个数字分别是什么,这样以后的读者就不用再自己想办法弄清楚。我们还可以让这个区块视觉上突显出来,以表示它是关于上下文的描述。

需要启动的操作并不一定要列在测试中。一次工资的核算运行可以由检查工资单结果的表进行隐式执行。这个例子聚焦的是什么需要被测试,而不是怎么被测试。这里没有必要用一个单独的步骤来说“下一步我们付钱”。

同样,我们也可以将工资检查器重新命名为更好理解的内容。因为我们希望以后不论是谁来执行这个自动化的过程,都能够确保检查了所有打印出来的支票,所以我们把它放在表格标题当中。不然,有人可能会使用子集进行匹配,导致系统打印每张支票两次,而我们却毫不知情。

这是整理过后的版本。

figure 3

没有了那些附加的东西,测试变得更容易理解了。那么重点来了。当我们看到这样的测试,没有数据库编号,没有所有不必要的杂乱,我们有了一个非常干净的画面,来回答问题“我们是否还漏了什么?”。有哪些边缘情况可能会破坏这个测试?我们倒不需要在此验证员工数据的有效性,系统的其他部分会完成这个任务。但是,有没有任何一种有效的员工数据可以成为这个测试案例的临界值?我们是不是可以通过数字游戏将结果变得不合逻辑?

有一个很明显的答案——如果员工的工资是0,会发生什么?还是照旧打印支票么?规则里提到的是“每个员工一张支票”——所以那些已经被解雇多年的员工,即使他们已经不再领取工资,还是会收到打印的支票,只不过金额是0而已。如此,我们可以与业务部门展开一次讨论,看是否需要强化一下规则,确保在不必要的情况下,没有支票产生。

FitNesse因为这些残破不堪的测试案例而声名狼藉。Concordion(译注:Concordion是一种验收测试的框架)由此应运而生。最近的工作坊当中,也有一些人提出使用Given-When-Then结构的Cucumber能够更好的预防这些问题的产生。我不这么认为。这跟工具无关——人们有可能使用任何工具来进行这种糟糕的测试设计,同样,他们也可以使用FitNesse做出又好又干净的测试。我认为,指出FitNesse的基础案例如此糟糕的事实,这于事无补,问题的关键不在于工具,而在于我们所付出的过程和精力,必须要让测试变得容易理解。有趣的是,让测试变得干净漂亮花不了太多精力,但是它带来的价值却远远不止如此。

Apr 18th, 2017

Progressive Estimation Scale

为什么采用渐进式估算尺

原文链接:http://www.yakyma.com/2012/05/why-progressive-estimation-scale-is-so.html

作者:Alex Yakyma

在本文中,我们将会讨论为什么渐进式的估算尺(Progressive Estimation Scale)——例如敏捷团队常用的斐波那契数列,比线性的估算尺更为有效,在为团队衡量backlog的故事的大小时,呈现更多信息。我们将会用到信息理论的一些基本原则来得到结论,同时,也会形成一个针对“归一化的估算基准”的假设。

在敏捷方法中,我们常常能看到大家使用“渐进式”估算尺对backlog的故事进行估算。最常见的方法是修正过的斐波那契数列:0, ½, 1, 2, 3, 5, 8, 13, 20, 40, 100。还有一种不太常见的估算尺,是XP(译注:极限编程)的拥护者们所倡导的:0, 1, 2, 4, 拆分 (本文中我们且将这种估算尺称为XP尺)。之所以称之为渐进式,就是为了说明,其价值的增长速度比线性增长更快。所以我们常常也会称这类估算尺(虽然通常情况下并不是100%准确)为指数式估算尺。

事实上,XP尺本身就是指数的,而斐波那契数列与函数 $f(x)=1.6^{x-1}$ 次方所定义的范围大致相当。参考下图:

figure 1

图1. 修正过的斐波那契数列的的数值范围与指数函数大致相当

在这里作此说明的目的,是为了表示这两种估算尺都具有同样程度的精确性,我们称之为“指数级”精确度。

话虽如此,还是让我们先反问自己:为什么要使用指数级的估算尺?众所周知,已经有成千上万的敏捷团队成功地应用了这类估算尺,是什么使得指数尺如此堪以大用?过去,我们面对此类问题,通常的回答是——提出backlog里的故事越大(比如N个故事点),那么说出NN-1之间的差异就越难。这确实是正确的,然而这个回答本身并不是一个基本的公理,它只是基于一些通用原则的推论。就让我们来看一看这些原则。

信息理论视角

让我们换一个角度提出问题,假设我们(非常粗略地)知道backlog里故事U的大小不超过L个单位(可以是故事点,也可以是人·天,在这里这点并不重要),但是它可能是0L的任意值,并且所有的可能性均等。假设,有一种估算技术,允许我们以P的绝对精确度估算出U的大小,那么我们可以看看,运用这种技术能得到多少关于故事U大小的信息

figure 2

图2. 符号说明:U是backlog里的任意一张故事卡;L是backlog里所有卡片可能大小的最大值;P是估算的绝对精度;横轴上的连着U的点代表故事U的大小。

根据信息理论的基础知识我们知道,实验A中关于实验B的信息(也叫两个实验的互信息,详细可参考Wikipedia)可以表示如下:

这里,$H(B)$ 是信息熵,也就是实验B的不确定程度。$H_A(B)$是实验A发生后,实验B的(也称为条件熵)。所以现在可以很容易将实验A中包含的的信息量解释为它为实验B所减少的不确定度。

现在,让我们把这个公式应用于我们的案例。我们也有两个“实验”,实验 (B) 是确定故事U的准确大小,实验 (A) 是应用我们的估算技术,从而在一定程度上减少不确定度(例如,获得一些信息)。避免展开数学深度的计算,这里我们仅需注意到,通过应用香农定律(实际上是互信息的一种定义),我们可以得到:

这里对数的底数并不是非常重要,它可以是符合对数简单特征(参考Wikipedia)的任意数量(大于1),但是通常人们会使用2作为底数,从计算机的角度来看,这倒是非常有意义,因为最终我们所有的信息都是以二进制形式存储的。

后面这个方程给出了一个非常有意思的结果:相比估算精度的提升,我们通过估算获得信息量的提升要慢得多。更具体地说,它是一个对数函数式的增长。通过下图这个对数函数图,我们就可以看出为什么“少量的估算事半功倍,而大量的估算事倍功半。”

logarithm

图3. 故事大小的信息的对数行为,可作为估算过程的结果。横轴代表相对精度(或者更精确的说,代表了(L/P)的值),纵轴代表信息量(以比特计)。

所以最终,使用指数(或近指数)估算尺就变得非常合乎逻辑了——有价值的信息增加得更快。确实,任意函数 $f(x)=a^{log_bx}$ 的增长肯定比 $log_bx$ 自身要快(a和b都大于1)。在理想环境下——现实中我们一定不会做此期待,当 $a=b$ 时,这个函数就变成了线性函数:$f(x) = x$。但是既然我们不会去声明这是必须的条件,那么我们通常就会说“信息增加得更快”,而不是“线性增加信息”。

归一化假设

另一个有趣的问题是,考虑到大多数团队都在使用斐波那契数列作为估算尺,他们是怎么确保他们“校正后的信息曲线”(例如,函数 $f(x)=a^{log_bx}$ 对应的曲线)能够增长得更快,从而使其比线性尺更加有用呢?显然,“信息曲线”的对数行为是可以肯定的,但是,如何在实际中找到确切的对数底数却不是件容易的事。虽说我们可以寄希望于团队通过自身经验能够找到斐波那契数列的正确用法,从而有效地获得信息。那么,什么是“正确用法”呢?

为了进一步简化分析(如前文所作的分析),我们可以使用某些指数函数,而不是斐波那契数列。即便底数是固定的(例如,$f(x)=a^x$ ),我们的团队还是可以改变函数本身,通过什么方式呢?对,正是“重新调节故事点”。确实,如果我们赋予故事点新的含义,比如说,以前的2个故事点等于现在的1个故事点,那我们的函数 $f(x)$ 变成了新的函数 $g(t)=f(2x)$ , 其中 $t=2x$ 。或者反过来,$x=0.5t$。如果我们替换掉前面公式中的$x$,我们可以得到 $g(t)=b^t$ ,其中 $b$ 是 $a$ 的平方根。新的函数 $g(t)$ 又成了一个指数函数,只是底数不同而已。下图这个例子,说明了重新调节故事点所产生的影响——如果我们将以前的3个故事点变为现在的2个故事点的大小,那么“估算尺曲线”会产生什么样的变化呢?

figure 4

图4. 重新调节故事点——将原本的3个故事点变成新的2个故事点之后,指数函数(估算函数)的底数也随之改变。图中蓝线代表原来的估算尺,红线代表新的估算尺。

这就说明:

不断改变斐波那契数列的估算基础是一件非常有责任的事情,而这一点往往被低估。它可以让团队更趋高效的估算,当然,也有可能背道而驰。理想的估算底数只有一个,保证离它越来越近是很至关重要的。

现在,我们可以看看一个有趣的事实,也就是许多敏捷团队都可以确认的:

经过一段时间之后,敏捷团队常常会“归一”,到达一个近似于斐波那契数列的估算底数——在一个两周的sprint内,团队的平均速度通常为30-60个故事点。

当然,这也取决于团队规模,团队越小,这个数字越小;团队越大,数字也会越大。但也可能,这是团队为了更有效地管理潜在的可用信息,随着时间推移,不断优化其估算底数后的结果。基于此,我们也形成了我们的……

归一化假设:那些随着时间推移,使其估算基准归一化的团队,很大可能已经到达了他们的最佳估算能力(从他们估算过的,backlog里的项目所获得的信息的角度来看)。换言之,他们经验性的发现了这个指数函数,使得“矫正后的信息曲线”接近线性。

虽然看起来,要到达一个更好的估算基准,会有一段距离,但我们有了一个简单蠢笨但是可靠的方法作为开始。简单地在一个sprint给每个队员分配8个故事点,不是一个糟糕的初始估计值。事实上,考虑到上面的假设,当团队成员N48变化时,我们得到的故事点$N×8$正好从3264变化不等。

想要了解更多估算基准,可以参考 (Agile Software Requirements Enterprise Development 第八章,敏捷估算和速率)

Apr 18th, 2017