Hi, 如果有任何想法与我沟通, 请用: lifr_nj 在 msn.com

发布新日志

  • 提高测试用例覆盖率的分析方法

    2007-05-31 17:53:19

    开发高质量的测试用例是QA的基本工作。高质量的测试用例是既有高覆盖性又有高可执行性,当两者不可兼得时,它有最佳平衡点。本文不讨论如何取得最佳平衡,只关注采用何种 分析方法 来提高测试用例的覆盖率。

    首先来说,分析分为两个步骤,首先以不同得角度切分系统,使得它成为更简单得模块,第二是把不同得模块想象成一个黑盒子,对这个黑盒子做类似于单元测试得分析。

    1. 从不同得角度把系统分为不同得模块

    这里存在两种思路

    对系统进行完整得划分

    软件是复杂的。软件开发者面对这种复杂性采用的经典的方法瀑布模型,也就是从上到下,逐渐细分,大模块包括小模块,小模块包括更小的模块。对于软件测试来说,很自然的,我们也可以采用这种方法。但是,这还是远远不够的。我们还要从更多的角度切入系统,从不同的角度把系统切分成一块一块的,然后进行测试。

    比如遥控器的例子。我们就可以从下面一些方面来划分系统
        1)功能。
        这个划分非常直观,Spec上肯定会明确指明遥控器假设有3个功能,开关机,+-调台,+-调音。对每个功能我们测试它能否正常工作。当然我们也会注意到边界情况:当音量满的时候,加音量。。。
        2)状态
        Spec上没有说明遥控器的状态,但我们应该能分析得出遥控器有下面的状态
        关机,开机,正在调台,正在调音
        现在我们可以列一个matrix,测试在每种状态下执行上面的任意一种操作系统的反应。注意,这个时候,你就要把你自己当成用户,因为这些情况都不会在spec里详细的说明的。比如在关机状态下调音,竟然开机了。这个显然就是bug。
        3)按键序列
        现在我们走得更远。我们把遥控器看作具有按钮得一个玩意儿,然后输入任意一个序列看是否会出现异常情况,是否会让程序不正常得工作。这里不需要分任何得分析方法。这是一个很好得切入点。另外一个例子是,在测试web application时,做一个爬虫程序去点击页面上得任何link。

    通过这些不同得划分,testcase得覆盖率可以得到有效得提高。

    需要注意一点是,不同得划分肯定会带来testcase得冗余。在划分1)时,有测试开关机得case,在划分2)时,显然也会有这样得case。这是不可避免得,也没有关系。

    寻找某个特定得切面

    上面得划分系统可以看作 对整个系统得一种 分离方法,划分方法得结果是把测试对象分成不同得一块一块。而“特定得切面”则只是描述了测试对象得一个面,它不存在划分系统得问题。还是上面得例子,比如“长按按钮”就是一个“特定得切面”。

    ”长按Power按钮“是一个测试得关注点,“长按volumn+”也是这样得一个关注点,如果在系统中多处存在这样得相似得关注点,那么就构成了一个面,比如在这里是每个按钮都存在“长按按钮”这样一种可能,那么“长按按钮”这就可以看作系统得一个切面。对于这样一个切面,如果把它分散在每个功能测试case里,显然不是好主意。最好得方法是把它拿出来作为一个单独得testcase。

    在举一个例子是,“维护数据完整性” 是一个切面。很多系统都有用户这个对象,很多其他得对象都会引用到它。对于引用已经删除得对象就是一个容易出问题得地方。那么就把“删除用户”作为一个切面拿出来,对每一个相关得对象进行测试。这样一个切面是非常好得testcase。

    说到这里,你可能会发现这其实是面向方面编程(AOP)得概念。bingo!确实如此,好得思想方法在哪里都会闪光啊~_*.


    2. 功能单元测试

    面对一个比较小得功能单元,设计testcase就容易得多了。因为功能单元千差万别,所以我仅仅写一些相对通用得思路。

    1)从4个可能变化的要素入手:输入,输出,参数和状态。
    如果把某个功能想象成一个黑盒子,那么这个黑盒子任何时候得输出可以用下面得三个参数来确定(输入,状态,参数)。这种方法可以对功能进行详尽得测试。

    2)黑盒子得生命周期
    盒子不是凭空出现的,它也不是在真空之中。在它的生命周期中,有那些东西能影响它?它的初始化,重启动,关闭。。。

    3)GUI测试
    一个功能单元可能有GUI,那么他们也应该在这里测试。我们以GUI测试为例,GUI有它自己的特点
    1.    GUI很容易变化
    2.    GUI一般不容易错,因为GUI不包含复杂的逻辑
    3.    GUI的错误很容易看出来, 很多GUI问题其实看一下就知道了,比如字体不对
    4.    GUI难以描述。GUI涉及的内容很多颜色,布局,字体。。。
    所以对于GUI的测试用例,应该给出一个关键点,而不用给出具体的描述。比如“检查label字体”比“字体是宋体,大小11,斜体“要好,当然除非特别要求

  • Ruby+Watir经验谈: 漫谈针对功能的自动化测试框架

    2007-05-30 21:12:34

    0. 不讨论什么


    我们不讨论那种简单的自动化脚本,用来帮助QA对某个,或某几个testcase进行测试;这样的脚本往往用来代替手工执行testcase里的某些步骤,比如一个在数据库里产生数据的SQL脚本。又比如根据testcase录制了10个Robot脚本,通过replay这些脚本就能完成这个testcase的测试。这种自动化脚本几乎没有任何弊端,它短小和贴近testcase,没有多大的开发开销,它几乎总是能带来测试效率的提高,如果有可能,完全应该去做。

    我们也不讨论性能测试。性能测试几乎总是要自动化的,但它和功能测试有太多的不一样。

    当然,也不要包括单元测试(white-box),虽然它的概念可以在功能测试中借鉴,也就是功能单元测试。

    下面说到自动化测试框架,都指针对功能的自动化测试框架。

    1. 定义

    如果让我来定义自动化测试框架,那么它的核心应该是这样一个东西:它提供一套API,这套API对产品的操作做一个包装并提供一个的精简的集合。通过这套操作集合,几乎所有(当然不可能100%)的testcase对产品的操作步骤都可以对应到对这套API的调用。也就是框架提供了一个 ‘产品’ 和 ‘测试脚本’ 的中间层,。一个好的测试框架,可以让自动化脚本看起来象人类语言描述的testcase一样清晰。

    2. 好处

    假设我们已经有了这样一个测试框架,我们来看它能带来那些好处
        1) 首先得益的是smoke test, smoke test总是和DailyBuild联系起来。有严重问题的build当天早上就能发现,不会release给QA。
        2) 如果大部分的功能测试都可以被自动化。那么regression test就可以自动化起来。regression test其实是非常非常枯燥但有不得不做的。也许一整天的测试,只是验证了“新的build通过了所有的这些testcase”。如果以bug的数量来衡量QA的工作的话,那么就更令人伤心了。
        3) 新功能的测试被翻译为自动化脚本,并加入regression test suite中。庞大的regression test会给QA巨大的信心。
     

    3. 自动化永远不能代替手工测试

    一定要认识到: 自动化永远不能代替手工测试。
        1) 有些验证根本没法通过程序来验证,或验证起来非常困难,比如程序启动的速度。
        2) 在新feature的测试阶段,testcase只能发现30%的defect。但自动化脚本只能基于testcase,所以对新feature的测试,我觉得应该禁止自动化测试。
     

    4. 你需要自动化测试框架吗?

    我认为,只要是做产品,都需要这样一个测试框架。测试框架连同它的测试脚本和产品一样都有一个一个的release。好的测试框架会一直伴随产品,为它保驾护航。

    5. 失败

    有很多搞自动化测试最后失败的公司,这里的失败是指最后放弃了自动化测试。我看到的原因一般是
        1) 测试员抱怨框架太难用,在上面开发自动化脚本很耗时
        2) 产品变化太大(GUI衰退,学术名字*_~),要花很多精力和时间来改写测试框架
        3) 产品变化太大,很多辛苦开发的自动化脚本在下一个release的产品中不能跑起来
       

    6. 避免失败

    那就是高质量的自动化测试框架。
    好的框架必须是易于使用的。
    好的框架,作为中间层,会尽量减少产品UI的变化对测试脚本的影响。有一句话说在自动化框架的设计中:即使点击一个button都需要写一个函数来包装。这种极端的指导思想来源于下面的考虑:也许这个button会再下一个版本会改成一个link。
    好的框架,自身抵抗变化的能力也很强,这要求框架本身是一个设计优良的软件。

    7. 如何才能获得高质量的自动化测试框架

    如果我们准备开发一个自动化测试框架,我认为需要注意

        1) 选择一位精通程序设计和软件测试的开发人员。

        一种误解是自动化测试开发只需要普通的测试人员参与就可以了。我不是否定测试人员的能力,而是测试框架开发完完全全是软件的开发。甚至更高一级,相对于软件库的开发,比如它对接口设计和易用性上的要求就相当高。
        然后这位开发人员还必须是本软件测试方面的专家,也就是领域专家。有很多软件失败于需求分析,你当然不希望这种事再这里重演。

        2) 合适的开发工具

        现在商业和开源的开发工具都为数不少。不过我认为在选择的时候要注意两点1)用record&replay来鼓吹易用性的软件。他们也许是好软件,但不是开发测试框架的好软件。框架开发要求工具有强大的功能和灵活性。2)最好选用使用通用语言来开发的软件。通用语言一般都有各种各样的库。我就为用robot的scrīpt来访问mysql的问题相当恼火。
       

    8. 开发自动化测试框架的一些建议

    下面是我在开发框架核心API部分中的一些经验

        1) 尽量把让测试脚本访问你的API而不是直接操控UI元素。

        这即使不是绝对的,也要尽力避免。把变化留给框架。因为我们注意到,产品UI的变化频率和程度都超过功能的变化。UI变化,功能测试的逻辑不会变化,而功能测试在所有的测试中一般是占主要的部分。

        2) 针对不同类型的测试,提供多套API

        前面说过最好把测试脚本和UI操作完全隔离开来,但考虑到测试的多样性,这往往导致存在多套不同层次的API。
        比如一个添加用户的web 页面。针对这个页面的基本功能需要提供一个方法: addUser(name, pswd)。大多数testcase都会调用这个方法作为一个其中的步骤。
        假如输入的name具有非法字符检查功能,系统会用javascrīpt及时检查用户输入并在某个页面某个位置把错误信息显示出来。要测试这个功能,上面的方法就不够了。这个时候就需要针对这个页面封装一个页面模型(page model),通过这个页面模型可以访问页面的元素比如
            getNameInputBox
            getErrorMessage
        显然这是两套不同的API,应该分别属于不同的集合。而且最好两套API不要有依赖。
       

        3) 保持框架和测试逻辑分离

        API不要包含任何测试逻辑的代码。API是精简,正交的。不要试图提供一些和测试逻辑相关的功能来使得某几个测试脚本更好开发,它反而真正伤害了API。
        比如我曾经把验证from里各项数据的方法加到了API里。其实我应该做的是把form里的数据收集出来,以Map的形式返回给调用者。

        4) 最好边开发框架,边开发一个精简的regression test suite。

        这样能让你早发现框架存在的问题
       

        5) 在release你的API之前,一定要给QA 小规模试用

        API发布后就不能更改,所以发布API要谨慎小心。


    8. 提外话:关键字驱动的自动化测试

    套用一句话是:Make everything as simple as possible, but not simpler. -- Albert Einstein 

    我曾经开发过一个测试框架,试图提供一种能力把测试逻辑用xml的tag表达出来。我做了大量的工作来把测试中的action用tag封装起来,在给team member使用后发现,它并不那么受欢迎。原因是
        1)熟悉tag是一个负担,特别是tag多了以后,
        2)tag的表达能力有限,有些情况不能满足需要,如果要满足需要,tag变得非常复杂。甚至需要引入条件判断的tag。后来我想,如果要使用一个<if> tag,为什么不直接使用编程语言呢?

    所以,我认为一门高效易学的脚本语言来写测试脚本是在灵活性和易用性之间的最佳平衡点,python, ruby都是上上之选。关键字驱动?太过了,且不说实现它需要而外的投入,学习这些越来越多的关键字会让QA失去对他最后的耐性。

    数据驱动是一个非常棒的测试方法。但要在数据里加上 关键字或者流程控制。。。过犹不及也。
  • Ruby+Watir经验谈: 设计RUTF的TestRunner以产生格式化的测试结果

    2007-05-29 09:57:50

    对一个测试框架来说,测试结果报告是很重要的部分。但是ruby unit test framework并没有提供一个testrunner来输出格式化的结果,所以必须自己写一个。

    RUTF的架构

    在开始写自己的testrunner之前,我们需要理解ruby unit test framework(简写为RUTF)的结构。

    RUTF执行TestCase的过程简单明了,它里面有4个概念
        原料:TestCase/TestSuite
        执行配置者:TestRunner
        执行体:TestRunMediator
        产品:TestResult

    处在中枢位置的是TestRuner,它会创建和配置 执行体,把原料输入给执行体,并让执行体跑起来。

    TestRunner和TestRunMediator的关系

    需要说明一下 TestRunner和TestRunMediator的关系。这是一个 监听器 模式的应用。被监听者是TestRunMediator,可以为下列事件向一个TestRunMediator注册listener。
            事件的宿主      事件类型
            TestRunMediator :STARTED, :FINISHED, :RESET     
            TestCase    :STARTED, :FINISHED    
            TestSuite    :STARTED, :FINISHED
            TestResult      :FAULT, :CHANGED

    而TestRunner正是通过提供不同的listener,而获得不同形式的结果报告。所以为了获得格式化的测试结果报告,我们需要提供自己的TestRunner。

    !!!TestCase的含义,在定义时和运行时是不一样的
    另外一个需要关注的地方是,对于RUTF来说,一个最小的执行单位(我叫它测试单元TestUnit,--注:这个不是RUTF的概念)不是一个 TestCase 实例,而是一个TestCase实例的一个test方法。所以在定义时有下面的事实
        一个TestCase是TestUnit的集合
        一个TestSuite是TestCase的集合

    但在运行时,在RUTF的看来,一个test方法就是一个'testcase',而一个TestCase,作为'testcase'的集合,叫做'testsuite'。所以事件类型TestCase::STARTED表示的其实是一个test方法开始执行的事件。同理,TestSuite::STARTED表示的是我们定义的一个TestCase开始执行的事件。

    转换xml形式的结果到Html

    比如有下面一个testcase
    # test case
    class TC_Test < Test::Unit::TestCase
        def test_ok
            assert_equal(2, 1+1, "2=1+1")
        end

        def test_error
            assert_true(3, 1+1, "3=1+1")
        end

        def test_failure
            raise "failed"
        end
    end
    # 中间结果
          <suite name='TC_Test' error_count='0' failed_count='2' test_count='3' assertion_count='0' failure_count='0'>
            <test name='test_error(TC_Test)' time='0.062' passed='false'>
              <result error_count='1' assertion_count='0' failure_count='0'>
                <fault msg='Error:
        test_error(TC_Test):
        ArgumentError: wrong number of arguments (3 for 2)'><![CDATA[
        tc_test_2.rb:18:in `assert_true'
        tc_test_2.rb:18:in `test_error'
        F:\lifr\myRuby\everbright/lib/test/testrunner.rb:159:in `start_mediator'
        F:\lifr\myRuby\everbright/lib/test/testrunner.rb:155:in `start'
        ]]></fault>
              </result>
            </test>


    xml的结果适合作为中间数据保存,但其可视性和可理解性还不好,还需要把xml转换为html。in ruby way,最方便的是用erb模版,在加上一个css。我们就能得到漂亮的报表了。

     
  • Ruby+Watir经验谈: javascript popup box

    2007-05-28 10:42:40

    Watir对Dom Tree有超强的能力,但一旦超出了它的势力范围,比如 javascrīpt popup box,它就无能为力了。

    解决这个问题的思路是启动另外一个进程,利用第三方工具(AutoIt),来完成对这些对象的操作。


    Watir安装已经自带了AutoIt,但它的辅助方法还不太好。主要是缺少判断是否有box弹出的方法。

    附件里提供有两个scrīpt,一个是作为另外一个进程运行的脚本clickJsDialogButton.rb,它的功能描述如下

        1) 探测pop box
        2) 如果发现了popup box,click "enter" key, or "escape" key.
        "enter" key 和click OK button效果一样
        "escape" key 和click Cancel button效果一样


    1 # argument: {ENTER}, or{ESCAPE}
    2 # return 0(true) if there is a box pops up, 1(false) if not.
    3 require 'watir/WindowHelper'
    4
    5 AutoItRetTimeout = 0
    6 AutoItRetSucceed = 1
    7
    8 DefaultTimeOut = 30
    9
    10 IETitle = "Microsoft Internet Explorer"
    11 PRESSKEY = ARGV.shift
    12
    13 autoit = WIN32OLE.new('AutoItX3.Control')
    14 foundAndClicked = false
    15
    16 ret = autoit.WinWait IETitle, "", DefaultTimeOut
    17 if ret==AutoItRetSucceed # 0 timeout, 1 succeed
    18     autoit.Send PRESSKEY
    19     foundAndClicked=true
    20 end
    21
    22
    23 ShellExitTrue = 0
    24 ShellExitFalse = 1
    25
    26 exit foundAndClicked ? ShellExitTrue : ShellExitFalse

    还有一个是提供操纵popup box的jsDlg.rb

    module Everbright
        module Util
        # module JsDialog helps to handle javascrīpt popup box.
        #
        # Javascrīpt popup box is out of the control of Watir::IE,
        # so we cannot detect/click an javascrīpt box by a Watir::IE instance.
        #
        # Basically, methods of JsDlg accept an block by running which there
        # will pops up an javascrīpt dialog box. Then you can click "OK" or "Cancel" button of it.
            #
            # Also you can detect whether or not there will pop up a dialog box by method JsDlg.popAlertbox?.
            # This method is especially useful to test input validation.
        module JsDlg
           
            # click button of a pop-up js dialog box
            #    button can be
            #        :OK
            #        :CANCEL
            #
            # return thread
            def clickJsDialogButton(button)
            path = File.join(File.dirname(__FILE__), "clickJsDialogButton.rb")
           
            key = button==:OK ? "{ENTER}" :
                button==:CANCEL ? "{ESCAPE}" : nil
            raise "buton can only be :OK or :CANCEL" if key.nil?

            #puts "cmd = "+"ruby \"#{path}\" \"#{key}\""
            t = Thread.new { system("ruby \"#{path}\" \"#{key}\"") }
            return t
            end

            def toClickAlertOk #:yield:
            clickJsDialogButton(:OK)   
            yield
            end

            def toClickConfirmOk #:yield:
            clickJsDialogButton(:OK)   
            yield
            end

            def toClickConfirmCancel #:yield:
            clickJsDialogButton(:CANCEL)   
            yield
            end

            # Whether running the block leads an alert dialog box pops up?
            # if yes, click "OK" button of the alert dialog box and return true
            # if no, return false
            def popAlertBox?(&block)
            path = File.join(File.dirname(__FILE__), "clickJsDialogButton.rb")

            ret = true
            t = Thread.new { ret = system("ruby \"#{path}\" \"{ENTER}\"") }
            block.call
            t.join
            return ret
            end

            module_function :toClickAlertOk, :toClickConfirmOk, :toClickConfirmCancel
            module_function :popAlertBox?, :clickJsDialogButton
        end
        end
    end



    使用JsDlg,操纵popup box就非常简单了,比如删除操作往往都会弹出一个confirm窗口
    JsDlg.toClickConfirmOK {
        ie.button(:caption, /delete/).click
    }

    在输入了一些非法数据后,测试提交form时有alertbox弹出
    assert_true(JsDlg.popAlertBox?{ ie.button(:caption, /Submit/).click })

    注意,这里并没有处理prompt窗口。因为在我的测试中一直也没有用到
  • Ruby+Watir经验谈: Understanding Watir

    2007-05-28 10:35:05

    使用Watire,主要通过是使用Watir::IE的一个实例来获得页面上的各种元素,比如button,link...,然后你可以对这些元素进行操作,比如点击一个button,点击一个link...
            ie = IE.new
            ie.goto("http://somewhere")
            ie.button(:caption, /save/).click

    实际上,需要注意的是,Watir::IE封装了一个当前页面的DOM tree,而不是html source。比如如果用javascrīpt动态产生一个元素,在这里仍然可以访问。


    这种访问DOM tree的能力并不是Watir::IE所独有的,div,frame都具有这样的能力。实际上,这种能力是Watir::SupportedSubElements提供的。一般来说所有内部能包含任意子tag的元素都include Watir::SupportedSubElements, like Watir::Frame, Watir::Div, Watir::TableCell, Watir::Form...所以我建议把关于Watir::SupportedSubElements的API页面地址放在收藏夹,你会经常需要访问他们 :).


    获得元素的方法参数一般来说有两种
        1)how, value
        大多数element都可以通过这种方式获得。

        how给出了方式,比如:id, :name
        value给出了值,比如
        <input type="button" id="saveBtn" value="Save"/>
        
        获得button的代码是: ie.button(:id,  "myId")

        2) how, what, value
        这种方式适用于访问radio或者checkbox。我们知道可能有多个radio具有同样的name,只是value不同。
        比如一个选择男或女的的form field有两个radios,
        <input type="radio" name="gender" value="male"/>Male
        <input type="radio" name="gender" value="female"/>Male

        获得第一个radio的代码是: ie.radio(:name, "gender", "male")

    注意,如果给定条件的element不存在,方法返回的不是nil,仍旧是这个类型的元素的一个实例。要用element.exists? 来判断这个元素是否真的存在。调用一个不存在的element的任何方法,都会触发一个Exception。

       
    下面是Watir的源代码结构。

    module Watir
        # 所有的Exception都定义在另外一个文件里,
        # 且都位于Watir::Exception这个Module里面,
        # 这句话,把所有的Exception都引入Watir这个Module,方便使用
        include Watir::Exception

        # 这个module为每一个Dom Tree上的元素定义了一个访问方法
        # 比如,为button定义一个button方法,当要得到一个button时就可以用
        # ie.button(:name, 'b1')这样的方式
        #
        # IE和Frame都要include这个module,这样IE和Frame都具有这种能力
        # 理论上,任何可以包括任意别的tag的tag都可以引入这个module,
        # 比如<td>, <div>
        module SupportedSubElements
        def button(how, what=nil)
            end

            def link(how, what=nil)
            end
        end

        class IE
        # IE 拥有了访问Dom tree上element的方法
        include SupportsSubElements

        # 一个工厂方法,你总是可以用这个方法来初始化一个browser对象
        def IE.start(url)
        end
       
        # IE对象提供了很多操控IE window的方法比如
        # 前进,后退,刷新,goto, maximize, minimize
        def back
        end

        # 还提供了访问HTML code的方法
        def html
            return document.body.outerHTML
        end
        def text
            return document.body.innerText.strip
        end
        end

        class Form < IE
        #Form可以访问它内部的Dom tree。另外,From还提供了一些Form特定的方法,
        #比如submit,方法和获取form的name, action, method的方法
        def name
        end
        end

        # Element 是大多数页面元素的基类.
        # 它定义了取得 适合大多数元素的成员变量
        #   id, name, type, value, title, disabled(某个值不存在的话,返回"");
        #
        # 和成员方法
        #   click, exists?, enabled?, html, ...
        class Element
        end

        # ElementCollections是 代表某类页面元素的集合
        # 比如 links, spans,
        # ElementCollections 引入了Enumerable module
        # 所以你可以方便的操作里面的元素
        class ElementCollections
        include Enumerable
        end
    end

    Watir,不得不说是一个非常精彩的library,它提供的功能非常强大,还能保持API直观,统一,易于使用。再加上,它的原生操纵语言是强大的ruby,用Watir写对于web的测试简直就是手拿一把锋利的多功能瑞士军刀。

    Watir的不足之处是它不能处理超出Dom Tree之外的东西,比如javascrīpt popup box,不过借助第三方库,也能比较优美的解决。

     
  • Ruby+Watir经验谈: 缘起

    2007-05-28 10:05:32

    在半年前,我想很多人,包括我自己,都不知道watir是个什么东东。在国内几乎没有介绍的文章,尝鲜者聊聊。但半年后,随着用Ruby+Watir开发了一个具有较大规模的自动化测试框架,再回头看来,我个人认为,用RAW是做web自动化测试的第一选择。事实上,现在网上关于Watir的介绍和经验也多了起来。

    在用Watir之前,对于自动化测试工具,我只有一些使用Rational Robot的经验。Robot对于我来说最大的问题是,从一个C/C++/java程序员看来,它的scrīpt语言实在太不敢恭维了。包括语法'丑陋',提供的外部库功能匮乏(net, file, remote control, database ....),还有编辑器也非常简陋。总的来说,我在写robot scrīpt的时候,我都在怀念在eclipse里使用java无所不能的感觉。

    Robot并不全是坏的印象。它最cool的地方就是Record&Replay。但是,如果是对某个不断进化的产品进行测试,而且这个产品还不是那么简单。那么直接RecordReplay是远远不够的。套用一句不知道是哪个专家说的“自动化测试程序也是程序”,也需要设计,也需要架构,也需要设计模式。

    其他商业的自动化测试工具我还简单体验过Silktest,也是我所在公司曾经采用的。但它的scrīpt语言更不讨人喜欢,而且据使用它的同事说稳定性存在问题。更好的工具比如Mercury的QTP,我没有使用过。

    现在看来Ruby+Watir确实是一个正确的选择,但半年前这并不是显而易见的。公司原来的测试工具是silktest,并在上面开发了一定量的testcases。不过由于人力不够和lisense的问题,这个测试套件已经有很长一段时间停步不前了,并没有随着产品的更新而更新。silktest形如鸡肋,Robot,QTP...价格又高不可攀,而对于一个做产品的公司来说,自动化测试似乎是不可避免的,所以公司招聘了一个自动化测试Consultant,希望她能带来价廉物美的利器。而这个consultant此前就在用Ruby+Watir做项目。

    Consultant带来了Ruby+Watir,并计划做一个SmokeTest suite,集成进Nightly build。她希望能在QA组里找一个助手,因为我以前就做了一些自动互测试的工具并在team里使用,所以我也参加了进来。当然这个时候,我对Watir一无所知,而Ruby,虽然曾经进入了我的视野,但在一年前,我选择了Python作为我的第一脚本语言。

    2个人的Team建立了。计划是consultant着手搭建框架,我先学习Ruby和Watir。随后的一个月,我不得不放下放下ruby,而参加更紧迫的产品releas测试。当然我还是抽空翻看了ProgramingRuby 2Ed一书,写了一些程序来热身一下,虽然是蜻蜓点水,但也被ruby的优美和强大折服。当consultant再次来到中国,我对她准备如何实现这个框架还是一无所知。我也不知道我得职责到底是什么。

    当我提出我得问题后,consultant给我看了她已经完成的一些东西,我看了感觉比较失望。因为从她的代码看,她并不想提供一个框架,只是针对smoketest提供一些例程。对这些例程配合不同的输入数据,构成smoketest的全部。而我此时已经雄心勃勃,希望看到的是一个精心设计的面向对象框架,对QA所面对的产品的功能有一个比较完备的抽象层,-它不仅仅是为了Smoketest

    这个框架要实现的目标就是,在它基础上应该能够把大部分(>80%)的Manual test比较轻松的翻译为自动化脚步。对每一个step都能直接而且方便的翻译为一句ruby 语句。也就是说需要达到一种效果:读自动化脚本就像在读用人类语言描述的testcase。自动化脚本完全是在描述测试逻辑,这真是一个够“理想化”的目标!


    我提出了我的想法,发给她一份我对整个框架设计的构思。她显然并不愿意大动干戈。我于是试图说服我们这边的QA Manager趁此机会做一个全功能的测试框架,并获得了成功。consultant只能作出让步,但要求不能miss smoketest的目标。当然我同意了。

    接下来的一个月consultant休假,她给我了一份list,罗列了要达到的目标,然后就闪人了。我则全心投入了实现这个框架的工作。


    经过几个月的开发,重要的是在给同事们使用后并得到了好多反馈。最后我觉得是初步达到了目标。随后,我会谢谢在这个过程中遇到的问题,解决的方法和得到的经验。

1066/6<123456
Open Toolbar