都100%代码覆盖了,还会有什么问题?

引言

(图片来自:http://t.cn/R06rQHi

很多人看到这个标题时,都会想“你都100%代码覆盖了,怎么还会有问题呢?” 让我们看一下代码例子:

public class TestCalculator {

       public Double add(Double a, Double b) {

              return a + b;}

}

再看看用junit写出的测试代码:

@Test
public void testAdd() {

       Double a = new Double(1);

       Double b = new Double(2);

       Double c = new Double(3);

       assertEquals(c, testCalculator.add(a, b));

}

当我们使用EclEmma或者Jacoco来进行覆盖测试时,对于这个类,我们将得到100%测试覆盖率。

一切看起来都那么的完美,真是这样的吗?

好吧,让我们来来看看另一个测试,当其中一个变量为null时,返回值将会是什么?

@Test
public void testAddNullPointerException() {

       Double a = new Double(1);

       Double b = null;

       Double c = new Double(3);

       assertEquals(c, testCalculator.add(a, b));

}

好了,你会发现尽管覆盖率为100%,但程序却抛出了NullPointerException。

那么肯定有人会问,这样的话单元测试覆盖率的高低都不能作为衡量项目代码质量的指标,那么我们要单元测试还有什么用?

首先,我想我们可能搞错了测试覆盖的定义。

我们先听听Martin Fowler对于测试覆盖的定义

Test coverage is a useful tool for finding untested parts of a codebase. Test coverage is of little use as a numeric statement of how good your tests are.

(图片来自:https://martinfowler.com/bliki/TestCoverage.html

他认为:把测试覆盖作为质量目标没有任何意义,我们应该把它作为一种发现未被测试覆盖的代码的手段。

所以100%的代码覆盖率还值得追求吗?

当然,这应该是每个程序员毕生的追求之一,但是如果从项目角度考虑ROI(投入产出比),对于需要快速上线的短期项目,需要注重的是让测试覆盖核心功能代码。如果你的项目是一个长期项目,那么高覆盖率是非常有必要的,它意味着高可维护性,以及更少的bug。(前提是你的测试采用TDD/BDD方式编写,我见过将测试代码写的一团糟的人,看着他的代码,我宁愿重新写一遍)

那么对于一个项目来说,覆盖率应该达到多少?

其实没有适用于所有项目的数值,每个项目都应有自己的阈值,但共性是,测试必须覆盖主要业务场景,代码的逻辑分支也必须尽可能的覆盖。

如何改进你的项目代码覆盖率?

首先我们要阅读和理解项目代码,找出其中需要测试并且与业务强相关的代码,结合sonar等代码质量管理平台,从代码编写规范、复杂度、重复代码等方面进行代码重构,进一步提高项目的可维护性与可读性。

这也意味着重构,重构的同时,你需要更多的测试来保证你重构代码的正确性。

其次要对code coverage进行度量分析,那么我们应该怎么度量code coverage?

一般来说我们从以下四个维度来度量,如上图所示:

  1. 行覆盖率(line coverage):度量被测代码中每个可执行语句是否都被执行到,但不包括java import,空行,注释等。
  2. 函数覆盖率(function coverage):度量被测代码中每个定义的函数是否都被调用。
  3. 分支覆盖率(branch coverage):度量被测代码中每一个判定的分支是否都被测试到。
  4. 语句覆盖率(statement coverage):度量被测代码是否每个语句都被执行。

所以行覆盖率的高低不能说明项目的好坏,我们要从多方面进行思考,一般我们遵循的标准应是:函数覆盖率 > 分支覆盖率 > 语句覆盖率**

代码覆盖率最重要的意义在于:

  • 阅读分析之前项目中未覆盖部分的代码,进而反推在前期QA以及相关测试人员在进行黑盒测试设计时是否考虑充分,没有覆盖到的代码是否是测试设计的盲点,为什么没有考虑到?是需求或者UX设计不够清晰,还是测试设计的理解有误。
  • 检测出程序中的废代码,可以逆向反推代码设计中不合理的地方,提醒设计/开发人员理清代码逻辑关系,提升代码质量。
  • 代码覆盖率高不能说明代码质量高,但是反过来看,代码覆盖率低,代码质量绝对不会高到哪里去,可以作为测试自我审视的重要工具之一。

结束语

单元测试的覆盖率并不只是为了取悦客户或者管理层的数据,它能够实实在在反应项目中代码的健康程度,帮助我们更好的改善了代码的质量,增加了我们对所编写代码的信心。


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

Share

为什么不能每周发布一次?

“看,车来了!不过貌似咱赶不上这趟车了吧?”

“啊!那快点跑,错过这趟就得再等半个小时!”

……

好无奈,可是真的赶不上也没有办法,这个场景很多人都经历过。

“这个release又是一定包就开始上hotfix,四天跟了四个,我根本没时间做回归测试!” QA小静同学抱怨道。

“每次都是定包后就开始无休止的上hotfix,咱们还不如改成每周发布一次!”Dev大鹏同学也被hotfix折磨苦了。

这是发生在蓝鲸项目中一次真实而平常的对话,跟前面赶公交车的场景有什么关系呢?

发车间隔与发布周期

发车间隔的不同带给乘客的感受会完全不同。

有些公交车很少,每半个小时一趟,有时候眼看着一辆车来了又走了,没赶上会无比懊恼,下一趟还得等上半个小时,实在着急的可能会考虑叫一辆快车赶紧走。

而有些公交车发车间隔非常短,几分钟一趟,就算错过一趟也无需等待太久,只要不是着急去救火,乘客一般不会太在乎。

项目的不同发布周期带给客户的感受也是类似的。

蓝鲸项目的发布周期跟第一种公交车发车间隔非常类似,是四周发布一次。如果功能没能在这次上线,或者有导致功能无法正常工作的缺陷,得再等一个月才能再次上线。一个月,那是多少白花花的银子啊!对于那些特别重要的功能,客户着急就会要求上hotfix,于是就出现了前面小静和大鹏的对话场景。

Hotfix是否真的能解决软件交付过程中的问题呢?其实不然。

Hotfix的引入带来很多额外的工作,影响新功能的按时交付,会打乱交付节奏,有形成恶性循环的趋势。既然这样,大家一定希望能把问题解决,而且通过公交车的例子很容易想到前面大鹏说的那个方案。

如果发布周期缩短,比如说缩短为一周甚至更短,这次没上的功能也不用那么着急的通过hotfix来上线,就能解决问题。那么蓝鲸项目为什么不一周发布一次呢?

如何才能缩短发布周期?

1. 合适的迭代计划和合理的需求切分

半个小时一趟的公交车,就算车子足够大、能够承载半个小时到达的乘客数,也会导致乘客等待时间过长,造成很多不便。要解决这种情况,可以把大型公交车换成多辆小型车,增加发车次数,缩短发车间隔。发车间隔缩短到多少,车子换成多大都是需要仔细分析和考虑的问题。

映射到蓝鲸项目,要缩短发布周期,就得有相应的小规模需求正好能够乘坐小发布周期那样的小车,因此要做好发布计划和需求切分。

这样对需求源的要求很高,需要客户那头的紧密配合。要想缩短发布周期,首先必须得有足够的、粒度合适的功能需求,能够正好安排到较短的发布周期上线。如果需求范围不能提前确定好,就没法提前做好短周期发布计划,不可能把发布周期缩短。

2. 强大的开发能力

在把乘客的需求分析清楚之后,缩短发车间隔非常关键的一点就是要有足够的安全的车和靠谱的司机。如果这一点满足不了,其他都免谈。

对应到蓝鲸项目,安全的车子和靠谱的司机组成了拥有强大开发能力的团队,包括架构支撑和人员技能。

车子就是对持续交付友好的技术架构,需要减少模块间的依赖,比如采用微服务。蓝鲸项目是一个七年之久的老项目,很多陈年依赖已经形成,要拆分不是一时半会的事情,团队正在朝着这个方向努力。

司机就是我们的开发团队,除了要有必要的开发技能外,要做到靠谱就得透彻理解持续交付的精髓,需要团队中人人都有质量意识,人人都有发布周期的紧迫感,并且能够做到高效合作,从需求划分、代码质量、测试保障等做好各个环节的工作,做好缺陷的预防和监控,不让严重缺陷流入后面阶段。

蓝鲸项目由于新人较多、人员流动大等原因,质量意识和紧迫感都有待提高。不过,在各位QA的影响下,这些问题都在改善,新人的技能也在不断的学习和实践中得到提高,但仍然不能放松警惕,需要时刻保持向前的精神面貌。

3. 充分必要的测试支撑

有了足够的安全的车和足够靠谱的司机,还得保证路况够好,这样才能做到不管到达哪个站,间隔都是相同的。

要想蓝鲸项目的持续交付能够顺利前行、一路畅通,需要严格做好质量内建工作,各层都有充分必要的自动化测试保护,减少新功能开发过程中对老功能的破坏;同时持续集成流水线也要健全,不能耽误代码提交和出包,以防影响开发和测试的进度。

蓝鲸项目开发年限已久,复杂度很高,在持续交付的路上行走的有些坎坷。目前团队正在这些方面努力采取改进措施,取得了不少进展,但确实还有不少提高的空间。

前景堪忧?

过去七年,蓝鲸的持续交付之路有些坎坷,但不应因此而失去信心。

通过跟公交系统进行对比,我们可以看到蓝鲸项目要缩短发布周期、杜绝hotfix,需要从需求切分、迭代规划、技术架构、团队能力建设和测试策略调整等多方面进行优化,才能保证持续、快速的发布节奏,这是一个系统的问题。

七年之痒已经平安度过,蓝鲸团队正在采取相应的改进措施,一旦做好了上述各方面的优化,在下一个七年,一周发布一次或者更短的发布周期都将不是梦!


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

Share

为什么QA不喜欢重构?

经常听到开发人员抱怨 ,“这么烂的代码,我来重构一下!”,“这代码怎么能这么写呢?谁来重构一下?”,“这儿有个坏味道,重构吧!”

作为一名QA,每次听到“重构”两个字,既想给追求卓越代码的开发人员点个赞,同时又会感觉非常紧张,为什么又要重构?马上就要上线了,怎么还要改?是不是应该阻止开发人员做重构?

重构几乎是开发人员最喜欢的一项实践了,可QA们却充满了顾虑,那么为什么QA不喜欢重构呢?

老功能被破坏

不止一次遇到这样的场景,某一天一个老功能突然被破坏了,QA们感到奇怪,产品这块儿的功能已经很稳定了,也没有在这部分开发什么新功能,为什么突然出问题了呢?

追查下去发现是近期做了重构。再追问下去,对于老代码,已经几乎看不懂老的测试了,可是开发人员看到代码的坏味道就想重构,于是功能破坏了。

在快速迭代的开发模式下,QA们主要关注用户故事的生命周期,重点测试新的特性功能,所以对于老功能回归测试的投入是非常有限的,如果开发人员突然对老功能进行了重构又没有告知团队,这样的问题很可能就会进入生产环境,这样是非常有风险的。即便很多开发人员重构老功能时会告知QA,以提前发现和修复导致的问题,但无疑会大大增加QA做回归测试的负担。

新功能推迟/重复测试

按照用户故事的开发流程,开发人员完成功能后,多方角色会首先在开发人员的机器上进行用户故事的快速验收以及探索性测试,然后开发人员会提交代码,由QA拿到包之后部署到测试环境进行测试。

但有的时候QA在开发机器上快速验收之后,开发人员又进行重构,曾经经历过“故事验收的时候功能都是正常的,拿到包部署之后好多功能不工作了”的事情,跟开发人员确认,又是重构导致的。

有时候开发人员会在用户故事验收之后告知QA还会继续重构,QA可以等到重构完成后再拿新的版本做测试,这样就会导致用户故事的测试时间推迟。或者开发人员会先重构再做验收,而这样又会导致QA在开发机器上进行重复的验收测试。

无计划不可见

开发人员的重构时机对我们来说是无规律的。有的时候一边开发用户故事一边重构,有的时候会在故事完成后、QA在开发机器上验证结束后才做重构,有的是代码评审后大家指出问题后做重构,有的甚至得等到项目组来了有经验的开发人员才开始重构。对QA来说,重构的时机是无计划的。

另外重构也是不可见的。我们总谈可视化,日常的开发工作由用户故事和缺陷来可视化,而重构经常是幕后进行的,不被跟踪、不被记录、不可见,有经验的开发人员会在进行“大”的重构之前口头告知团队,如果没有告知,就在不知不觉中发生了,只有功能被破坏了才能意识到。

总结

以上列出了QA不喜欢重构的三个理由,归根结底其实都是重构不当导致的。代码重构(Code refactoring)指对软件代码做任何更动以增加可读性或者简化结构而不影响输出结果。而不当的重构往往只关注了前半部分却忽略了对输出结果的影响。

个人认为,如果能够注意以下几个方面,也许可以很大程度减少QA对重构的顾虑:

  • 充足的自动化测试是保障输出结果的一个有效途径。不管对于历史代码还是新代码,进行重构的前提应该是确保这部分功能已经被足够的自动化测试覆盖,有的开发人员认为这是一个不可行的建议,因为“充足”对于每个人每个角色的标准可能是不一样的,QA也许永远都不会觉得测试足够,但是其实可以借助测试覆盖率等度量工具,甚至直接针对功能特性由QA来定义需要哪些自动化测试,在补齐这些自动化测试的基础上再做重构。
  • 对于新功能的重构,应该融入到正常开发过程中,在故事验收之前已经完成相应的重构,因为自动化测试不是万能的,也不可能达到100%的覆盖率,所以还是需要QA验证的。这么做就意味着客户要为我们的重构买单,所以作为软件领域专家的我们,如何让业务领域专家的客户理解重构的价值,这就变的至关重要了。
  • 小步前进,尽量避免或者减少大的重构,这样可以减少突然增多的回归测试工作量,也会减少功能被破坏的风险。如果发生了大的重构,反思一下是哪里出了问题?是我们积攒了太多的技术债?还是业务领域模型发生了大的改变?然后采取措施避免类似的事情再度发生。
  • 对于一些核心功能或者组件,进行重构之前尽早告知团队QA,如果能够给QA讲解一下重构的目的以及本次重构可能会影响到的业务区域会对QA有很大的帮助。这样做一方面可以让QA把重心放在最容易受到影响的功能上加强回归测试,另一方面也能帮助QA更合理地安排测试计划。
  • 尽量避免在产品上线前进行重构,不仅可以减轻QA回归测试的负担,也会降低功能破坏后来不及修复的风险。

重构的目标是为了改善代码质量,长远来看应该是可以减少软件缺陷的,从这个角度来说QA和开发人员的目标是一致的,我们相信,如果重构恰当,必定对项目是有利无害的。

Share

致测试同仁们:让我们一起做安全测试吧!

本文首发于InfoQ:

http://www.infoq.com/cn/articles/to-test-colleagues-let-us-do-a-safety-test

今天,很多的软件并没有经过专门的安全测试就被放到互联网上,它们携带着各类安全漏洞暴露在公众面前,其中一些漏洞甚至直指软件所承载的核心敏感信息或业务逻辑。这些漏洞一旦被不怀好意者利用,很可能会给企业造成经济损失。带来负面声誉影响的同时,还可能被起诉、遭到罚款等等,细思极恐。其中的一部分原因是企业本身安全意识不强,但是很多时候虽然软件企业已经开始意识到这些问题,却苦于缺少专业的安全测试人员,他们不得不冒着极大的风险先上线赌一把运气再说。

既然如此,作为质量代言人的我们,怎能对此置之不理呢?

你也许会问?怎么理?安全测试水太深了。

安全测试并不遥远

是的,安全测试在软件测试里面是一个很特别的科目(或作“工种”),很多人都觉得这个科目应该全权交给神秘的安全测试人员来管。这个观念导致很多测试人员徘徊在安全测试的门口却迟迟不进去,包括我自己。

直到后来,我非常有幸能够在不同规模的软件开发项目上跟“神秘的安全测试人员”学习如何进行安全测试,发现“神秘的安全测试人员”不光是名字跟我们一样都有“测试”二字,所做的事情在本质上也跟我们测试人员有很多相通之处。

想想看我们都做过什么:

我们修改过url的参数,对不对?他们也是!
我们在数据输入处提供过不合法的数据,对不对?他们也是!
我们尝试过修改只读数据,对不对?他们也是!
我们也测过用户会话是否如期timeout,对不对?他们也是!

Ok,这还不是全部。他们也做测试计划、测试用例设计、bug分析与管理等等。所以,安全测试离我们并没有那么遥远。

当然,我必须承认,安全测试是非常复杂的。一个专业的安全测试专家在某种程度上来说是一个全栈工程师。所以,想要在安全测试上一夜成才不太容易。不过好消息是,作为测试人员的我们却有得天独厚的优势,使我们能够在安全测试上快速起步,帮助团队尽快展开预防并检测安全漏洞的工作。
在这里我想要跟大家分享一下在敏捷开发团队中如何利用我们的测试经验开展安全测试。

安全测试并不陌生

首先,让我们先来了解什么是安全测试,我们作为测试人员有什么可以直接用上的技能和经验。
简单来说,安全测试其实就是一个发现软件安全漏洞的过程,旨在保护软件系统的数据与功能。它跟常规测试相似的地方至少有以下几点:

No. Summary Details
1 目标类似 预防、检测系统的缺陷(尽早、频繁反馈系统质量信息)
2 在软件生命周期中的工作过程类似(以敏捷团队为例) -了解系统业务需求
-针对业务与系统功能设计用例
-与其他角色一起启动需求的开发
-与其他角色一起在开发环境验收需求
-在测试环境进行全面测试
-分析并总结测试结果
-反馈测试结果
3 测试用例有很多重合 面向用户的测试场景非常类似
4 都需要有探索的过程 对会对不同的业务场景有目的的进行探索
5 都要有测试人员必备的“怀疑态度” 对开发人员的代码保持友好而警惕的态度

1. 目标类似
不管是常规测试还是安全测试,都有一个原则:预防胜于检测。这个比较容易理解,不管是常规测试的缺陷也好,还是安全测试的漏洞也好,如果能预防使它不发生,就省了后期的修复与验证工作。如果不能成功的预防缺陷,能早一些发现的话,肯定比晚发现的修复的成本低。

2. 在软件生命周期中的过程类似

以敏捷开发团队为例,常规测试人员在各个阶段做的事情,安全测试人员也要做:

  • 了解业务的需求,以避免混乱的测试优先级;
  • 针对业务与系统功能设计用例:常规测试需要关注系统功能,安全测试同样也不能脱离系统功能;
  • 与其他角色一起启动需求的开发:沟通测试用例,避免因为沟通不足造成返工;
  • 与其他角色一起在开发环境验收需求:尽早提供反馈,发现缺陷时开发可以马上修正
  • 在测试环境进行全面测试:针对端到端的场景进行测试,尽可能把第三方系统(如果有的话)也包括进来;
  • 分析并总结测试结果:整理问题清单,排列优先级;
  • 反馈测试结果:把测试结果反馈给团队等干系人。

3. 测试用例很多重合

在面向终端用户的测试场景上,常规测试的用例与安全测试的用例是非常类似的。比如对于登录系统的功能,不管是常规测试还是安全测试,我们都会测试用户输入正确的用户名是否可以登录,输入错误的用户名或密码系统会如何反应。

比如我曾经工作的一个搜集报税人信息的系统,不管是常规测试还是安全测试,都会测试系统的登录,系统信息的录入与编辑,文件的上传等等。因为在每一个终端用户可以操作的场景上,都可能会有安全漏洞存在。所以,有了常规测试的经验,我们就相当于有了不少安全测试用例的储备。

4. 都需要有探索的过程

测试是一个了解软件系统能否完成我们预期的过程,也是探索系统还有哪些我们没有预期的行为的过程。安全测试的过程需要把探索的目标转向安全漏洞。当我们这么做时,我们同样会得到很多探索的乐趣。

5. 都要有测试人员必备的“怀疑态度”

相信咱们测试人员都非常熟悉一个场景--开发人员说:“我只做了一个很小的代码改动。”然后我们带着友好而警惕的态度,发现这个“很小的改动”引发了很大的问题。不管是在安全测试还是非安全测试,这个警惕性是我们都需要保持的良好传统。

那么,有了这么多类似的地方,还缺什么呢?如果想要做专家,还差很多。但是如果想马上安全测试上起步,我们可以先做下面的改变。

安全测试从何做起

第一,转换视角

在我看来,不管是带着全栈的经验,还是只有部分技术知识,想要做好安全测试必须先转换我们观察软件的视角。举个例子,让我们看看下这幅画:

(图片来自:http://imgur.com/gallery/ZCgQ3)

同样一幅画,有人一眼看过去看到的是两个人脸,而有人看到的是一个花瓶。这就是观察视角的不同造成的。

在我刚开始接触安全测试时就很深的体会到了这一点。当时我在测试一个Web应用的用户登录功能。当我输入错误的用户名来试着登陆时,浏览器上的提示信息为“该用户名不存在”。当我尝试正确的用户名而错误的密码时,提示信息变成“密码输入错误。”对于这个清晰的错误提示我非常满意。试想我若是一个真实的终端用户,这个信息有效的帮助我缩小纠错范围,提高效率,非常好。

可是,就在我身边坐着的安全测试人员马上跳了出来:“这个提示信息需要改!敏感信息暴露了!”看到我一脸茫然,这位安全测试人员告诉我,通过我们的提示信息,恶意的系统使用者可以推测出哪些用户名已经存在于系统中,然后利用这些用户名可以再进行密码的暴力破解,缩小破解的范围。所以,这个信息虽然为合法用户提供了便利,也为不怀好意的系统使用者提供了便利。而往往这种便利为恶意的系统使用者带来的好处远大于给合法用户带来的好处。

这个经历在让我受震动的同时,也使我意识到可能很多安全漏洞之前就摆在我的面前了,我却没有看出来,因为我把它们过滤了。事实证明,在后来经历的不同项目中,当我转换了视角,有些安全漏洞不需要我去找,而是自己跑到我眼前来的。真是得来全不费功夫。

第二,改变测试中模拟的对象

为了能从不同的视角来观察软件,我们必须改变我们所模拟的对象。这也是一个让我们刻意练习转换视角的有效方法。

我们在做非安全测试的时候通常把自己想象成一个合法用户,然后开始验证系统是否能完成预设的目标。比如对于一个网上商城,我们会验证系统是否能让用户完成商品的浏览与购买,我们也会测试一些异常的行为,比如购买的商品数量不是数字而是一串无意义的字母时,看系统是否能比较优雅的做出回应。我们这么测试的目的往往是为了确保用户误操作以后还能够继续他们的购买,或者说不要给系统造成什么严重的伤害。

如果要做安全测试,我们则必须去模拟系统的另一类使用者-恶意用户。他们的目的是寻找系统中可钻的漏洞。比如同样是一个网上商城,恶意用户的目标之一就是要想办法以较少的钱,甚至不付钱就能拿到商品。所以,如果恶意用户进行了“误操作”,他们不会停留在“误操作”,而是通过“误操作”来看系统是否给自己提供更多的线索。

所以,我们需要转换测试时所模拟的对象,把思维从一个合法用户的视角中拉出来,转换成一个恶意用户。这需要一点时间,就如同之前看到的画,如果我们一开始看到的是人脸,要想下一次第一眼看到的是花瓶,我们需要时间来刻意练习。

第三,使用专用的测试工具

有了思维的转换,我们可以加入新的测试想法。但是,在具体做安全测试的时候我们会发现并不是那么容易去模拟恶意用户的行为。毕竟系统的前端会给我们设置很多的屏障。而且恶意用户可不总是从系统前门进去的。这时候,使用一些工具,比如OWASP Zap(https://www.owasp.org/index.php/OWASP_Zed_Attack_Proxy_Project)、Burp(https://portswigger.net/burp/)等是非常有帮助的。我们可以在系统界面上执行功能测试的用例,用这些工具来获取http请求,篡改后发送给后台服务器。有了这些实用又比较容易上手的工具,我们就可以执行很多恶意用户的操作场景了。

能做到这三点,起步就基本够用了。

举个栗子吧🌰

下面让我们以网上商城的买家在商品评价中上传图片这个功能来讲讲如何实践这“三板斧”。假设我们从项目初期就加入了,那么我们大致有七件事情要做:

  1. 识别系统中有价值的数据;
  2. 在需求分析阶段加入恶意用户需求;
  3. 针对恶意用户需求设计测试用例;
  4. 参与启动恶意需求的开发;
  5. 在开发环境验收恶意需求的实现;
  6. 在测试环境中进行安全测试;
  7. 向团队反馈所发现的安全漏洞。

不要担心,这不是7个全新的事情。只是在每个需要测试人员出现的地方增加了安全的工作而已。

1. 识别系统中有价值的数据
很多人认为执行测试才是测试,而我们的安全测试从这里就开始了。
了解业务之后,我们需要考虑系统中会有什么有价值的数据。这是为下一步加入恶意用户需求做准备。对于一个网上商城,有价值的数据可能包括产品信息、订单信息、用户信息、支付,等等。
这个环节对我们测试人员来说并没有太多额外的工作,毕竟我们做非安全测试的时候也需要了解业务。不过要注意了,我们要测试的“图片上传功能”是一个涉及有价值数据的功能。我们需要提高警惕了。

2. 在需求阶段加入恶意用户需求
恶意用户需求是用来记录恶意用户想要在系统中达到的目的。与普通用户需求的区别是,我们不是要去实现它,而是使用它来帮助我们远离对系统使用者“不恰当的信任”。通常我们需要针对每一个合法用户需求来增加一个或多个相对应的恶意用户需求。


举个例子,如果我们这个“图片上传功能”的合法用户需求为:作为一个买家,我想在对商品进行评价的时候上传图片作为买家秀,以便于参加返现营销活动。那么对应的恶意用户需求可以是:为一个恶意用户,我想破坏买家秀返现活动,以便破坏商城的营销活动。“破坏买家秀返现活动”是一个大的目标。为了设计用例方便,它可以被细分为一系列小目标。比如让用户无法上传图片、让页面无法正确显示图片等等。

有了恶意用户需求的主干信息,我们就可以开始下一步设计安全测试用例了。

3. 针对“恶意用户需求”设计测试用例
现在我们需要做的是努力把自己限制在“恶意用户”的角度做头脑风暴:“到底有什么方法可以使买家无法上传图片信息呢?”, “让页面无法正确显示买家秀图片又怎么做到?”嗯,也许最直接的办法就是让服务器所在的机房断电、断网之类的。这是些不错的想法,虽然执行难度有点大。没关系,记录下来。除此之外,我们还可以有其他测试用例,比如:

  • 使存储图片的磁盘空间被占满而无法接受新的图片;
  • 使处理上传图片的进程繁忙而无法接受新的上传任务;
  • 上传特别大的图片使用户的客户端需要很长时间才能下载完
  • 上传伪装成图片的恶意代码,进一步获取服务器权限,删除所有的买家秀图片;
  • 等等

如果这个时候想到新的测试用例也同样记录下来,比如“我想不购买也上传买家秀图片以获得返现”之类的。
不用太担心这个阶段的测试用例过于“疯狂”或者不够完整,毕竟我们对于系统的实现还不是很了解。我们会在接下来的环节中完善具体的步骤。

4. 参与启动恶意需求的开发(evil story kickoff)
在开发人员开始开发合法用户需求之前,我们需要跟业务分析人员、开发人员一起沟通需求的内容。在敏捷软件开发项目中我们叫它story kickoff,即用户故事启动。当有了对应的恶意用户需求时,我们必然也要把它也加到启动的范围里。目的是把我们头脑风暴出来的测试用例跟所有的角色来沟通。预防胜于检测。

5. 在开发环境验收恶意需求的实现

100%预防软件的缺陷与漏洞是不太可能的,所以这个环节的存在是为了提早反馈。
我曾经经历过一个项目,都快上线了才决定做安全测试,结果测出来的问题之一是用户会话(user session)不能正确过期的问题,经过一番研究,发现需要对系统设计的架构进行比较大的修改,只能做个临时的修复让系统先上线,然后再把系统的架构给改了,重写这部分功能,重新测试。代价非常高。所以不管是安全测试还是非安全测试,”在开发环境验收恶意需求的实现“这个步骤都不能缺少。

而这个环节存在的第二个目的是让我们可以从开发人员那里得到支持-具体实施的细节,帮助我们完善具体的测试用例。比如在这个时间点我们若从开发人员那里得知系统的后台没有对图片上传者做身份验证,我们就可以至少增加一个测试的用例:“恶意用户以其他用户的身份上传一个风马牛不相及的图片”。有时候错误的图片比没有图片更具有杀伤力。

6. 在测试环境中进行安全测试
终于到了运行测试的阶段。可能这个时候我们之前想到的测试用例已经被开发人员给解决了。如果是这样那就太好了。但是,事实并非有这么美好。第一,可能这些用例只是在开发环境上成功通过了,但是在理想的测试环境里,也就是类产品环境里,这些用例可能并不能完全通过;第二,肯定还有其他需要探索的地方。这时我们就可以用OWASP Zap、Burp这样的工具来辅助我们把之前的安全测试用例执行一次,同时还再可以对系统的安全性做一下探索测试。

7. 向团队反馈所发现的安全漏洞
都测得差不多的时候,我们就可以向团队以及相关干系人汇报安全测试的结果了。跟非安全测试不同的地方是,当我们反馈安全漏洞的时候,要考虑不同漏洞结合起来是否会增加系统的安全风险。举个例子:如果有两个安全漏洞,一个是系统没有很强的用户账户密码规规则,另一个是系统没有对上传图片的大小做限制,那么恶意用户把这两个漏洞一结合起来,事情就比原来风险大很多。那么我们就必须建议提高这两个漏洞中任意一个的优先级。

当我们用“三板斧”走完这七步以后,我们已经可以把很多安全漏洞都挖出来了。是不是没有想象中的难?所以,测试同仁们,让我们做安全测试吧!

Share

我在测试移动弱网时踩过的坑

什么是弱网测试

在当今移动互联网盛行的时代,网络的形态除了有线连接,还有2G/3G/Edge/4G/Wifi等多种手机网络连接方式。不同的协议、不同的制式、不同的速率,使移动应用运行的场景更加丰富。

1-iphone

从测试角度来说,需要额外关注的场景就远不止断网、网络故障等情况了。对于弱网的数据定义,不同的应用所界定的含义是不一样且不清晰的,不仅要考虑各类型网络最低速率,还要结合业务场景和应用类型去划分。按照移动的特性来说,一般应用低于2G速率的都属于弱网,也可以将3G划分为弱网。除此之外,弱信号的Wifi通常也会被纳入到弱网测试场景中。

为何要进行弱网测试

我当前所在项目的产品是一款适配于低资源环境的医疗IT系统,目前主要是在坦桑尼亚地区使用。根据资料显示,在坦桑尼亚等东非国家,普遍使用的都是2G网络,覆盖率达到40%以上,3G网络的覆盖都非常少,并且稳定性较差。由此,对于当前的App应用交付要求即至少在弱网以及无网状态下能正常运行。

2-test

如何做弱网测试

弱网环境测试主要依赖于弱网环境的模拟。环境搭建方式一般有两种:软件方式和硬件方式。软件方式的成本低,主要就是通过模拟网络参数来配置弱网环境,通常来讲可以达到测试目的.一般可通过热点共享设置,或者第三方,例如 :Charles ,Network link Conditioner. 在各类网络软件中,主要就是对带宽、丢包、延时等进行模拟弱网环境。如果要求更接近弱网环境,比如现在很多的专项测试,会更倾向于通过硬件方式来协助测试,但这种方式相对会麻烦很多,一般会由网维协助搭建,例如树莓派。当然,对于有些无法模拟的情况,只能靠人工移动到例如电梯、地铁等信号比较弱的地方。

3-corner

弱网测试时碰到的问题和解决方案

1、现象:用户登录应用时下载初始化数据,下载过程中因网速太慢点击取消并重新登录,数据初始化完成后出现重复,造成数据不一致。

原因:数据下载过程中、下载失败后,未进行数据回滚,中止后重新下载,出现数据重复

解决方案 :通过事务处理数据下载逻辑,下载失败后,应用本地数据库进行数据回滚。

2、现象:用户点击数据上传,数据上传过程中网络弱且不稳定,基于联网状态自动触发数据上传,导致出现数据重复写入,形成脏数据

原因:数据上传过程中, 由于失败重传机制,会出现连续两次写操作,并且未做唯一识别处理

解决方案 :根据数据特性,对可能造成脏数据的地方,通过关键字段,例如创建时间,key-value值等生成hash键,标记记录唯一性,即数据写入时,检查hash键是否存在,如果已经存在,当前重复数据丢弃。

3、现象:在弱网环境下,用户输入用户名和密码点击登录,应用链接超时返回用户名和密码错误提示。

原因:在弱网环境下的连接超时后,按照强网业务逻辑处理,导致返回超时异常。

解决方案 : 弱网连接超时后,检查应用本地数据库是否有用户登录信息,若存在,获取应用本地用户信息进行登录。

4-ss-cloud-wifi

4、现象:在弱网环境下,用户输入用户名和密码后点击登录,登录过程中应用崩溃并且闪退。

原因:弱网环境下数据下载超时,加载数据严重依赖于后来的异步加载。数据还没来得及返回,应用跳转到下个activity,导致崩溃。

解决方案 :健壮数据加载流程,通过标记后台数据下载状态加载界面,依赖数据下载完成后,再进行页面跳转。

5、现象:弱网络环境下,用户请求页面响应时间较长,等待的过程中,页面上的部分控件仍然可以操作,当用户点击控件时,出现应用闪退现象;

原因:没有对数据加载流程进行判断,直接暴露控件可控,当出现依赖数据的控件操作时,没有在数据返回前做兼容处理。

解决方案 :在数据加载过程中,设置页面对外暴露的控件为“不可操作”,当数据加载完再释放。

6、现象:在弱网环境下,用户第一次输入搜索关键字没有得到响应后,再次输入全新关键字并发送请求,等待搜索结果返回后,当前结果页被之前的关键字搜索结果刷新覆盖

原因:中间的请求返回较慢,显示最终的结果后,之前请求返回的数据应不做处理。

解决方案 :对异步请求未完成的任务进行cancel.

总结

当然,出现以上问题的根本因素并不是弱网,在我们平时的PC应用中一样会遇到,但是这些问题在移动弱网环境下会表现的更突出。所以综上所述,从功能、性能、稳定、异常处理等几个维度来归纳场景特性,弱网测试主要集中在如下场景:

5-blog

 


更多精彩洞见,请关注微信公众号:ThoughtWorks

wechat

Share