【翻译】如何写可维护性好的自动化验收测试

原文链接:http://cwd.dhemery.com/2009/11/wmaat,看完这篇文章感觉挺有用,纯手打勉强翻译个意思吧。

测试自动化脚本是软件开发

测试自动化脚本是软件开发.这个原则意味着我们所知道的关于编写软件的许多知识也适用于测试自动化。然而没有开发软件经验的测试人员对这个的感触并没有那么深刻。
开发软件编写完成后,需要投入到维护的精力的多少,是区分好的测试代码结构的关键因素。一些组织或个人费了很大劲搭建起自动化测试,用了几个月就废弃掉了,当我问是什么导致他们放弃测试自动化,最常见的答案是,测试代码太他喵的难维护了。随便改个鸡毛按钮的位置,测试结果都一片血红,把测试代码改pass的功夫,老子都手工测完八遍了。
但一些公司成功的用自动化测试。他们就没有代码维护成本吗?当然啦,你跺你也麻呀。其中一个重要的区别是,好的代码结构考虑到可能的变数,而不是等代码都跑飞了,然后才幡然醒悟,这块实现变了,操,所有的代码都得改!好的测试开发人员,明白自动化测试类似开发一个软件,涉及到维护成本,时刻注意代码的可维护性。
通常有两种情况下我们需要维护测试脚本,新需求或系统实现的变更。都有可能导致n条case失败,如果不能及时的同步更新自动化脚本,跑出来的测试结果也没有任何意义,我怎么判断哪些失败是真的bug。
我们不能阻止产品经理一拍脑门灵感泉涌上需求,也不能阻止开发大哥菊花一紧大刀阔斧玩重构,唯一能做的就是保持测试代码结构的灵活,提高可维护性以应对各种诡异的情况。
通过含着眼泪改代码总结出来的教训,两个蛋疼的习惯让代码变得难以维护,冗余的细节和代码重复。

验收测试测的是什么
验收测试本质就是对系统的功能点进行访问,判断是否实现了该有的功能。不管用什么技术手段去实现这个访问。
假设我们现在需要测试一个系统的账户创建功能。命令create创建一个账户,输入参数username和password。账户创建的一个功能点就是验证密码的合法性,即接受合法的密码拒绝非法的密码。合法密码的规则为6-16个字符长度,至少包含一个数字,一个字母和一个符号字符。如果密码合法,系统返回" Account Created”,密码非法,返回 " Invalid Password  “。
不管这个功能怎么呈现给用户,web也好,GUI app也好,还是通过命令行,又或者是一只染着杀马特发型的草泥马会吐输入非法密码的用户一脸口水, 验证密码的合法性是这个功能的本质

冗余的细节
下面是一个RF testcase例子,测试create的密码校验功能。
*** Test Cases ***
The create command validates passwords
     ${status}= Run ruby app/cli.rb create fred 1234!@$^
     Should Be Equal ${status} Invalid Password
     ${status}= Run ruby app/cli.rb create fred abcd!@$^
     Should Be Equal ${status} Invalid Password
     ${status}= Run ruby app/cli.rb create fred abcd1234
     Should Be Equal ${status} Invalid Password
     ${status}= Run ruby app/cli.rb create fred !2c45
     Should Be Equal ${status} Invalid Password
     ${status}= Run ruby app/cli.rb create fred !2c456
     Should Be Equal ${status} Account Created
     ${status}= Run ruby app/cli.rb create fred !2c4567890123456
     Should Be Equal ${status} Account Created
     ${status}=  Run  ruby app/cli.rb create fred !2c45678901234567
     Should Be Equal  ${status}  Invalid Password

这是一条存在许多问题的case,最大的问题就是可读性。除了第二行的casename, The create command validates passwords表明了目的是create的密码校验功能,后面的一片代码很难直观的表述出在干嘛。
通过阅读3,4行,我们了解到密码是 1234!@$^,赋值给变量status,然后校验结果为Invalid Password。那中间的这些Run ruby fred 之类的玩意和密码校验有什么毛线关系?这些就是冗余的细节。
冗余的描述破坏代码的可维护性,假设现在我们的安全部门提出,6个字符长度的密码太短了,最小的密码长度改为10字符,这条case要做哪些修改?并不能很容易的找出来。
这是因为testcase没有明确描述出测的是哪个功能。当需求变更的时候,很难快速的找到那些部分修改。冗余的信息降低了可维护性。
所以提高这条case的可维护性就是屏蔽掉杂七杂八的“噪音”,直接可以看到case覆盖的功能。这条例子,大部分描述都是怎么运行一条cli ruby命令调用create 参数username和password分别为fred和 abcd!@$^。最后RF再把返回 赋值给变量status。谢特!下一行稍微好理解一点,判断返回的status是否是 Invalid Password。
RF允许我们通过创建keywords来简化这一过程,一个keywords相当于测试执行中的一个步骤。下面我们来创建一个keyword。
那么keyword咋写捏?假设并不知道系统是怎么实现这个功能,但是我们知道这个SUT的的功能点是 create account ,创建账户要提交username和password,所以我们的这步就可以写成:
      Create Account fred 1234!@$^
然后我们看下一步,假设我们并不知道 系统是怎么实现这个功能,怎么写这一步骤呢?生撸呗:
      Status Should Be Invalid Password
现在前两步就变成这个德行:
      Create Account fred 1234!@$^
      Status Should Be Invalid Password
好点了哈,没有那些多余的信息,两个步骤之间的联系变的清晰点了,应该返回密码是合法的。
如果我们现在执行测试,肯定会失败,因为RF并不认识这两个keyword,我们需要实现它:
*** Keywords ***
Create Account ${user_name} ${password}
    ${status}=  Run  ruby app/cli.rb create ${user_name} ${password}
    Set Test Variable   ${status}
    Status Should Be ${required_status}
    Should Be Equal  ${status}  ${required_status}

一个原则就是,testcase应该专注于what而不是how,把如何实现提出到关键字中。testcase变的清晰易懂。我们用新的keyword重写上面那条case。
** Test Cases **
The create command validates passwords
    Create Account fred 1234!@$^
    Status Should Be Invalid Password
    Create Account fred abcd!@$^
    Status Should Be Invalid Password
    Create Account fred abcd1234
    Status Should Be Invalid Password
    Create Account fred !2c45
    Status Should Be Invalid Password
    Create Account fred !2c456
    Status Should Be Account Created
    Create Account fred !2c4567890123456
    Status Should Be Account Created
    Create Account fred !2c45678901234567
    Status Should Be Invalid Password


代码重复
两个关键字的代价让我们的case变的简洁点了。但是还有个问题就是代码重复,我们要做的事密码校验,一个用户名fred每次都跑出来搞什么鸡毛。每两行就是输入密码和校验结果的动作,不同的只是输入的密码和期望的结果,其他的都是重复的代码。
回头看我们的代码,case要覆盖的功能点是什么,密码校验,而密码校验的核心,接受合法密码和拒绝非法密码。我们再创建两个keywoed
** Keywords **
Accepts Password ${valid_password}
    Create Account arbitraryUserName ${valid_password}
    Status Should Be Account Created

Rejects Password ${invalid_password}
    Create Account arbitraryUserName ${invalid_password}
    Status Should Be Invalid Password

新的keywords不仅让我们可以简单的复用,更重要的是它描述了每一步测试的目的。 我们用新的keyword再重写上面这条case。
** Test Cases **
The create command validates passwords
    Rejects Password 1234!@$^
    Rejects Password abcd!@$^
    Rejects Password abcd1234
    Rejects Password !2c45
    Accepts Password !2c456
    Accepts Password !2c4567890123456
    Rejects Password !2c45678901234567

变量命名
现在可以很直观的看出,这个testcase是测试接受和拒绝不同的password,但是看这些数据并不知道非法在哪儿。通过创建变量,我们可以给它一个一个显示的描述,例如:
** Variables **
${aPasswordWithNoLetters} 1234!@$^

很直观的就知道这数据是测试不含英文字符的非法密码。全部命名之后,我们的testcase就长这样。
** Test Cases **
The create command validates passwords
    Rejects Password ${aPasswordWithNoLetters}
    Rejects Password ${aPasswordWithNoDigits}
    Rejects Password ${aPasswordWithNoPunctuation}
    Rejects Password ${aTooShortPassword}
    Accepts Password ${aMinimumLengthPassword}
    Accepts Password ${aMaximumLengthPassword}
    Rejects Password ${aTooLongPassword}

再进一步,我们针对不同的方面把它分解成多条case。
** Test Cases **
Rejects passwords that omit required character types
    Rejects Password    ${aPasswordWithNoLetters}
    Rejects Password    ${aPasswordWithNoDigits}
    Rejects Password    ${aPasswordWithNoPunctuation}

Rejects passwords with bad lengths
    Rejects Password    ${aTooShortPassword}
    Rejects Password    ${aTooLongPassword}

Accepts minimum and maximum length passwords
    Accepts Password    ${aMinimumLengthPassword}
    Accepts Password    ${aMaximumLengthPassword}

现在我们在来读这条case,只需要瞟一眼就能明确知道覆盖的功能点是什么。如果现在有关于密码长度的需求变更,几分钟就可以找到修改的地方。那如果系统实现层面发生变更呢?我们假设代码被全部推翻重写,不用命令行了,变成了一个web界面包含用户名密码两个输入框和一个create的按钮。如果是我们最初的代码就全废了。而现在我们只需要修改两个关键字,又可以欢快的跑起来了。
Create Account ${username} ${password}
    Go To    http://localhost:4567/create
    Input Text    username    ${username}
    Input Text    password    ${password}
    Submit Form

Status Should Be ${required_status}
    ${status}=          Get Text    status
    Should Be Equal     ${required_status}  ${status}

将实现封装的底层的关键字,和testcase中的数据进行分离,系统实现有变化的时候,只需要修改底层的关键字,case中的数据还是不变的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
众所周知,人工智能是当前最热门的话题之一, 计算机技术与互联网技术的快速发展更是将对人工智能的研究推向一个新的高潮。 人工智能是研究模拟和扩展人类智能的理论与方法及其应用的一门新兴技术科学。 作为人工智能核心研究领域之一的机器学习, 其研究动机是为了使计算机系统具有人的学习能力以实现人工智能。 那么, 什么是机器学习呢? 机器学习 (Machine Learning) 是对研究问题进行模型假设,利用计算机从训练数据中学习得到模型参数,并最终对数据进行预测和分析的一门学科。 机器学习的用途 机器学习是一种通用的数据处理技术,其包含了大量的学习算法。不同的学习算法在不同的行业及应用中能够表现出不同的性能和优势。目前,机器学习已成功地应用于下列领域: 互联网领域----语音识别、搜索引擎、语言翻译、垃圾邮件过滤、自然语言处理等 生物领域----基因序列分析、DNA 序列预测、蛋白质结构预测等 自动化领域----人脸识别、无人驾驶技术、图像处理、信号处理等 金融领域----证券市场分析、信用卡欺诈检测等 医学领域----疾病鉴别/诊断、流行病爆发预测等 刑侦领域----潜在犯罪识别与预测、模拟人工智能侦探等 新闻领域----新闻推荐系统等 游戏领域----游戏战略规划等 从上述所列举的应用可知,机器学习正在成为各行各业都会经常使用到的分析工具,尤其是在各领域数据量爆炸的今天,各行业都希望通过数据处理与分析手段,得到数据中有价值的信息,以便明确客户的需求和指引企业的发展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值