发布新日志

  • 【翻译】我受够了“缺陷”(Defects)这个词

    2014-04-22 14:16:30

    http://www.developsense.com/blog/2014/04/ive-had-it-with-defects/

    我留在测试行业的时间越长,思考越多,我越相信“缺陷”这个概念是不清楚且无益的。

    程序中出现与规格说明不一致的一个编码错误,于是我会说,我已经找到了一个缺陷。另一天,在产品自动更新过程中出现了失败,使产品无法使用。很明显,这也是缺陷。然而,让我们来看看一些其他情况:
    • 我完成了一堆的测试,没有发现任何看起来像一个bug,但在通过检查代码,我看到它是如此混乱,难以维护,未来的变化将是危险的。我发现了一个缺陷吗?有多少个缺陷呢?
    • 我观察到一个程序是近乎完美的coding了,却是按照一个可怕的规格说明。产品有缺陷吗?
    • 一个程序完美按照一个伟大的规格说明进行了coding,然而规格说明的作者也许是按照一组考虑不周的需求来编写的伟大的规格说明,这应该叫产品的缺陷吗?
    • 我们的开发项目即将发布,但是我发现了一个有竞争力的产品,这个竞争者有完全令人信服的功能,使得我们的产品看起来像另外一个可运行的版本。是我们的产品有缺陷吗?
    • 我采访的一半用户说,我们的产品应该这样来做,现在的外表很丑陋,应该变得更容易学习; 另一半说,它应该表那样来做,指出外表并不重要,一旦你已经使用该产品一段时间,就可以快速,高效地使用它。我有没有发现一个缺陷?
    • 该产品不会产生日志文件。但是如果有一个日志文件的话,测试可能会更快,更容易,更可靠。如果产品缺少可测试性,这是否也是缺陷?
    • 我注意到,在用餐使用,当越来越多的人登录到订单时,支持我们的连锁比萨饼店的Web服务明显减慢。我看到这样的一个风险:如果企业变得好多了,网站可能拖垮充分,我们可能会失去一些客户。但此刻,web服务一切都还正常。 这是一个缺陷吗?如果它不是一个“现在的”缺陷,它会在未来“神奇地“地变成一个缺陷吗?
       除此之外,“缺陷”是一堆关于如何衡量测试质量、或者软件的质量的无用的概念的中心,如:“缺陷数”; “缺陷检测率”; “缺陷移除效率”。但是,什么缺陷?如果您访问LinkedIn,你常常能看到一些关于缺陷的学院派的论述。人们谈论缺陷,似乎是指绝对的、无可争议的产品的错误。然而,根据我的经验,问题很少会如此清晰。如果还不清楚什么是,什么不是一个缺陷,那么对其做计数就没有任何意义了。
    这就是为什么,作为一个测试人员,我觉得更有助于思考的概念应该是“问题”。“问题”是指“接收到的和所期望的存在的差异”或者“不是预期的情况,这种情况对某些人很重要,或者某些人可以将其解决的,虽然可能有一定的难度”。(参见这里。)。问题不是某种一定存在于软件中的某些东西,而是是相对而言的,是软件和某些人之间的关系。问题可能反映为bug(威胁产品的价值)或问题Issue的形式(威胁测试的价值,项目的价值,或者商业价值)。

    作为测试人员,我不去攻破软件。作为我的实际角色的提醒,我经常使用艾伦·乔根森(Alan Jorgenson)笑话,但它很可能起源于我的同事詹姆斯·巴赫(James Bach):“我没有去攻破软件; 当我得到它时,它已经是破的了”,换句话说,不是去攻破软件,而是发现了如何在那里破掉了。但即使这样,我也觉得不是很正确。我经常发现,我无法描述产品是“破”的: 该产品和某些人之间的关系可能破了。我通过使用及描述oracles去确定和阐明有问题的关系,这是我们在测试过程中识别问题的手段。

    Oracles并不完美测试人员不是法官,所以对我来说,标记某个东西为缺陷,似乎有些放肆的。正如詹姆斯所指出的,“如果我告诉我的妻子,她有一个缺点,多半不是个好做法。如果我说她正在做的东西让我觉得困扰,效果会好很多”。或如杰姆坎那(Cem Kaner)建议的,发布带有已知缺陷的产品意味着发布”有缺陷的软件“,可能会带来合同或其他法律后果(见这里这里,为例子)。

    一方面,我觉得“寻找缺陷”似乎太狭隘,太绝对,太放肆,对我来说很有些风险。另一方面,如果你看一下上面的列表中,所有这些事情,都是值得商榷的缺陷,可以更容易且较少争议地描述为的问题(这些问题会潜在地威胁到产品的价值)。因此,“找问题”为我提供了更广泛的视野,去认识些模糊概念,鼓励认知的谦卑,并承认主观性。也进一步,让我必须我参与自己的游戏,用许多不同的方式对产品进行建模,考虑许多不同的质量标准,不但只想到找功能上的问题,还会考虑任何可能会造成的损失、损害或给相关人员带来烦扰的东西。

    此外,拒绝缺陷的概念,可以帮助我们阻止对缺陷进行计数。鉴于“问题”的开放性和不确定性的性质,考虑如何去计数问题听起来非常愚蠢了。但我们可以谈论的问题。这将是朝着解决这些问题,解决什么是应该被接受的,什么是关心它的人想要等重要想法的第一步。

    这就是为什么我更喜欢寻找“问题”。上述,就是我对“缺陷”这个概念找到的问题。
  • 频繁称重不能减肥!-对测试的一点理解

    2010-12-15 11:05:21

    “想通过更多的测试来改善软件的质量,就跟妄想通过更频繁的称重来减肥一样”

                                                                                              《代码大全(第二版)

    当第一次听到这句话的时候,很震惊,也很难理解:我们不是通过测试找到软件的很多bug,不是通过测试让开发对代码进行了修改吗?为什么作者会有如此说法?

     也许上面那句话说得有些绝对,但是,如果我们只把测试定义为软件质量度量的一种方式,把测试工作的目的定义为寻找缺陷,那么上面这句话就是正确的了。传统测试过多侧重于大规模的集成测试,处于的项目后期,太过被动。做为项目质量最后的守门员,它的责任太大,它也担当不起这个角色。因为,永远没有完备的测试!就算QA一天24小时都在写用例、执行用例,也保证不了产品上线之后没有问题!就算我们发现了很多缺陷,开发在做修改的时候,由于缺少更细粒度、更精确的对原有代码功能的保证,可能会引入更多的缺陷!辛辛苦苦发现的缺陷能否对软件最终质量提高起到积极作用,就要打个问号了。这让我想起来了测试领域一句名言------“测试永远只能证明代码有问题,而不能证明代码没有问题”。

     那测试对于代码质量的提高真的没有价值了吗?绝对不是!把我们的思想转变一下,把重点转换一下。测试的灵魂不在于去度量软件的质量,而在于按照下面的方式去保证和提高软件的质量:

    1.       通过测试,从设计及代码层面去提高软件质量;

    2.       通过测试,去加快每一次项目的发布周期;

    3.       通过测试,去保证每次改动、每个新功能添加的正确性。

     

    要如何达到这些作用呢?

    1.       将测试(特别是能够快速反馈、准确定位的小测试)作为代码所要实现的功能的夹钳(也就是必须保证通过的条件),保证有变更时的代码的正确性; 保证代码对外功能不稳定性,也能放心大胆地对代码进行重构、优化。

    2.       将可测试性作为测试对代码质量进行提升的途径。将测试作为添加功能或者修改代码时必须保证通过的条件,就能

    3.       将测试作为体现业务需求、代码功能的说明文档。代码本身的功能及使用方式, 通过测试用例本身就能得到准确而形象的说明。

    TDD的核心思想也就是这样子的。《修改代码的艺术》中也反复提到如何通过测试来保证修改,如何通过测试、为了测试去提高新老代码的质量。

     “不能被很好测试的代码,一定是设计糟糕、实现糟糕的代码!”,为了可以测试、方便测试而进行的代码可测试性方面的改善,对代码的封装性、可扩展性等方面会带来直接的提升。 只有这样的测试,才会对代码质量的提高起到至关重要的作用。

        测试人员应该与开发一起,提高代码的可测试性、提高设计的可测试性!在交付高质量软件产品的共同目标下,测试与开发的分工和界限会越来越模糊。

       为什么说可测试性与代码质量密切相关,测试对代码质量能产生积极影响呢?可以看看下面两个例子:

    例子1

    糟糕的代码:

    class Car {

    public Car() {

                 engine = new Engine();

                 windshield = new Windshield();

           }

    }

    class Engine {

     public Engine() {

                 sparkplugs.add(new Sparkplug());

                 sparkplugs.add(new Sparkplug());

    }

    }

    当要进行测试时,不管我们是否需要Car类的所包含的所有零件,我们都要花时间去实例化它们,这个过程可能代价会很大。如果某个零件的类还没有实现,我们就不能开展与其无关的关于CAR类的其他测试。

    从开发角度来说,这个实现很脆弱:当我们想使用一个新的高性能的sparkplug时,当我们想给car换一种engine时,我们必须对原来的代码进行修改。

     

    好的代码:

    class Engine {

    public Engine(Set<Sparkplug> sparkplugs) {

                 this.sparkplugs = sparkplugs;

    }

    }

    class Car {

    public Car(Engine engine, Windshield windshield) {

    this.engine = engine;

    this.windshield = windshield;

    }

    }

     

    可测试性上:非常方便地去进行mock或者fack。如可以做一个fackEngine,让它从来不会启动。这样测试用例就可以方便地模拟出各种情况。

    灵活性方面:现在不管是想要一个HighPerformanceSparkplugsEnging,还是要一个FuelEfficientEngine ,都变得很容易。

            

    例子2

    糟糕的代码:

    class User {

        private Preferences prefs;

        public User(File preferenceFile) {

            prefs = parseFile(preferenceFile);

        }

        public void doSomething() {

            … // using prefs

        }

        private Preferences parseFile {. . . }

    }

    测试方面的问题:1.涉及到了文件系统,测试会变得很慢 2.很难去构造测试执行的条件:需要去构造一个格式符合规范的文件 3如果文件格式发生变化,必须更新所有相关的测试用例

    灵活性方面的问题:不能够支持今后的扩展,当需要改变文件格式时,侵入性地修改User类,当需要支持更多perference版本时,User中要有更多的逻辑判断,等等问题

            

             好的代码:

    class User {

        private Preferences prefs;

        public User(Preferences prefs) {

            this.prefs = prefs;

        }

        public void doSomething() {

            … // using prefs

        }

    }

    灵活性方面:1.数据可以很方便地来源于更多的地方,2.对于每个新的数据来源,可以单独生成一个将数据转换为Perference的工具类。这个工具类本身是很好测试的。

    测试方面的好处:1,运行更快 2.preference对于初始化测试变得代价很小 3.测试不再与文件的格式相依赖。

    (例子来源:《Flexible Design? Testable Design?You Don't Have to Choose!Russ Rufer & Tracy Bialik Google, Inc. GTAC 2010。这份ppt中还有更多的例子来说明代码的可测试性就是代码质量一个非常重要的方面)

     

    由上面这个简单的例子可以看出,可测试性好的代码,能够方便地写出运行快、耦合小、依赖少的测试用例,而代码本身的结构也更加合理,在可扩展性、可维护性上面大大提高。

    当我们把测试与开发活动融合在一起,通过测试提高软件的可测试性,那测试就变成软件质量提高的重要因素了!不再是简单的称重。

     

    注:关于测试如何作为修改代码时的保证,如何做为代码功能的描述,大家可以参考TDD方面的资料及《修改代码的艺术》。

Open Toolbar