本系列其他文章:
第一部分:测试开发之路 ---- 框架中数据的管理策略
第二部分:测试开发之路 ---- 数据驱动及其变种
第三部分:测试开发之路 ---- 可读性,可维护性,可扩展性
第四部分:测试开发之路 ---- 可读性,可维护性,可扩展性 (续)

  趁着放假开始这个系列的第二个话题,数据驱动。这个应该属于是个做测试的就知道也做过的话题,这里也算老生重谈了。这一篇帖子没有特别大的技术含量。就是对数据驱动的形式做一下讨论。就叙述以下我经历过的数据驱动的形式吧。 因为鄙人是 java 出身的,所以以下例子均为 java+testng 为背景。

最开始的数据驱动:例如 testng 自带的机制

  熟悉 testng 的小伙伴一定已经把 data provider 用烂了。我不细讲了,各位不用 java 的也一定对数据驱动熟的不能再熟了。直接简单介绍下。

@Test(dataProvider = "marNew", dataProviderClass = Dataprovider.class)
    public void testcase(String methodName, Params info) {

  上面的代码就是 testng 最简单的例子了,测试方法想要什么数据,直接写在参数里。然后@Test标签指定用哪个 data provider 就好了。

@DataProvider(name = "marNew")
    public static Iterator<Object[]> TestDataProvider(Method method) throws Exception {

  这个就是 data provider 的接口了。只要用注解定义一下,返回一个迭代器就好了。迭代器里的参数会自动的传递给测试方法的参数列表。不细表了,大家都知道。

改造后的数据驱动:当最原始的这个数据驱动应用的很好的时候,我们发现了一些问题。

  1. 代码里定义这些数据可读性不是很好
  2. 仍然有些代码是重复的,例如定义一个 javaBean,一个 List 等
  3. data provider 散落在测试脚本的各个包里,希望能分层出去到另一个文件里
  4. data provider 在测试的源代码中,而且是以代码形式定义,不会写代码的人想通过定义测试数据来添加 case 成为了不可能

  于是我们就想到了把参数定义在一个 excel 里。于是就变成了下面这样。

  这样就决了很多问题了,例如可读性问题,现在看起来很方便。数据分层出来了,而且最重要的是,我们现在可以有一种模式就是:自动化人员写脚本,业务人员通过数据文件写用例这样效率高多了。但是我们还是有一个问题没有解决----代码重复,我们发现通过 excel 文件搞这事有个最大的问题----无法定义复杂类型的数据。 例如说一个 list,例如说一个对象类型。 我们只能定义 String,int 这种基本数据类型。然后需要在脚本中构建这些对象。 这样就太不爽了,很多重复的代码。而且我们希望脚本里只有测试逻辑,没有构建数据的操作。

继续改造:为了定义复杂类型的参数,我们把 excel 换成 xml

  面向对象的编程语言中,一个复杂类型其实也是个树形结构。所以我们这一次使用本身就是树形结构的 XML 试一下。

<methods methodName="cancel" invokeType="Spring" className="com.bj58.daojia.ordercenter.agent.house.NurseOrderService">
    <params alias="订单不存在" dataFile="cancel.xls">
        <in type="long" name="orderId">123123</in>
        <in type="String" name ="cancelReason">cancelReason</in>
        <in type="Enum" name ="cancelType" path="com.bj58.daojia.ordercenter.enums.CancelOrderReasonEnum">DUPLICATE_ORDER</in>
        <in type="String" name ="operator">sungaofei</in>
        <in type="Enum" name ="operateSource" path="com.bj58.daojia.ordercenter.enums.OperateSourceEnum">BACK</in>
        <out type="Object" name ="OperationResult" path="com.bj58.daojia.ordercenter.dto.support.OperationResult">
            <subP type ="Boolean" name="result">false</subP>

            <subP name="message" type="Map">
                <subP name="key" type="Map">
                    <subP name="key" type="Integer">2</subP>
                </subP>
                <subP name="value" type="Map">
                    <subP name="value" type="String">订单不存在</subP>
                </subP>
            </subP>
        </out>
    </params>

    <params alias="已取消的订单无法再次取消">
        <in type="long" name="orderId">47077048011000</in>
        <in type="String" name ="cancelReason">cancelReason</in>
        <in type="Enum" name ="cancelType" path="com.bj58.daojia.ordercenter.enums.CancelOrderReasonEnum">DUPLICATE_ORDER</in>
        <in type="String" name ="operator">sungaofei</in>
        <in type="Enum" name ="operateSource" path="com.bj58.daojia.ordercenter.enums.OperateSourceEnum">BACK</in>
        <out type="Object" name ="OperationResult" path="com.bj58.daojia.ordercenter.dto.support.OperationResult">
            <subP type ="Boolean" name="result">false</subP>

            <subP name="message" type="Map">
                <subP name="key" type="Map">
                    <subP name="key" type="Integer">7</subP>
                </subP>
                <subP name="value" type="Map">
                    <subP name="value" type="String">已取消的订单无法再次取消</subP>
                </subP>
            </subP>
        </out>
    </params>

  OK,这次爽多了可以看到上面的 xml 文件中,我们可以定义Object类型,其实就是 javaBean 啦,通过递归算法我们可以定义无线复杂的数据类型。具体实现可以参考我这篇帖子NO_CODE 接口自动化测试框架. 但是我们突然又发现一个问题,我们这个机制很多都是通过 java 反射机制做的类型转换算法。既然我们都可以自动化类型转换了,那我们也可以让框架帮我们做更多的事。

接着改造:关键字驱动

  江湖传闻,关键字驱动是数据驱动进化而来。我是不知道是不是这样,当初定义这俩货的时候,我还不知道在哪呢。不过在我这,确实是这样的。接上面的例子,我们发现利用 java 反射,框架已经可以帮我们做参数类型的自动转型了。既然反射这么强大,那让它帮我们多做点事情吧。

<methods methodName="cancel" invokeType="Spring" className="com.bj58.daojia.ordercenter.agent.house.NurseOrderService">

  我们加几个属性,把方法名和类的路径告诉框架。注:invokeType中的Spring的意思是获取对象的方式。我们清一色用 spring 传递 dubbo 协议,所以写的是 Spring(HTTP 协议的也一样,就是不传类路径,传 URL 了)。OK 这样框架就可能帮我们调用接口了。我们接着来。

<params alias="订单不存在" dataFile="cancel.xls">

  记得我们上一节说的注册式数据管理么?这个dataFile就是像框架注册数据了。

<out type="Object" name ="OperationResult" path="com.bj58.daojia.ordercenter.dto.support.OperationResult">
            <subP type ="Boolean" name="result">false</subP>

            <subP name="message" type="Map">
                <subP name="key" type="Map">
                    <subP name="key" type="Integer">2</subP>
                </subP>
                <subP name="value" type="Map">
                    <subP name="value" type="String">订单不存在</subP>
                </subP>
            </subP>
        </out>

  out标签,验证返回值。框架根据接口返回值与 xml 定义的预期的结果做比较。依然支持复杂数据类型。

<database database_name="dbwww58com_emclottery">
            <table table_name="t_app_nurse_order" where="order_id=1234567890988">
                    <row>
                        <field field_name="order_id">1234567890988</field>
                        <field field_name="nanny_type">1</field>

  database标签,帮助拼 sql 验证数据库。

改造一切你想要的:通过定义 XML 标签,我们增加 skip 用例的功能,我们可以规定什么验证什么不验证。等等

  自动化测试的 4 个步骤,数据准备,调用被测功能,验证结果,销毁数据。我们发现框架都帮我们做了。OK 了,那还写个毛线的脚本。我们就关键字驱动了,没有脚本,xml 文件就是脚本。不论 http 还是 rpc 协议我们全能让业务人员做了。

  OK 至此我这篇也写完了。。。虽然我不情愿说,但是老婆又催我去睡觉了。。有关关键字驱动的实现看我第一篇帖子NO_CODE 接口自动化测试框架


↙↙↙阅读原文可查看相关链接,并与作者交流