爱陪小樱桃

导航

 

标签(空格分隔): 让自动化测试脚本更好地描


如何把控操作函数的粒度?

操作函数的粒度是指,一个操作函数到底应该包含多少操作步骤才是最合适的

  • 如果粒度太大,就会降低操作函数的可重用性。极端的例子就是,前面文章中涉及的百度搜索的案例,把“登录”“搜索”“登出”的操作作为一个操作函数。
  • 如果粒度太小,也就失去了操作函数封装的意义。极端的例子就是,把每一个步骤都作为一个操作函数。
  • 更糟糕的是,在企业实际自动化测试开发中,每个测试工程师对操作函数的粒度理解也不完全相同,很有可能出现同一个项目中脚本粒度差异过大,以及某些操作函数的可重用性低的问题。

那么,操作函数的粒度到底应该如何控制呢?其实这个问题,在很大程度上取决于项目的实际情况,以及测试用例步骤的设计,并没有一个放之四海而皆准的绝对标准。

但是,脚本粒度的控制还是有设计依据可以遵循的,即往往以完成一个业务流程(business flow)为主线,抽象出其中的“高内聚低耦合”的操作步骤集合,操作函数就由这些操作步骤集合构成。
比如,对于“用户注册”这个业务流程,其中的“信用卡绑定”操作就会涉及多个操作步骤,而这些操作在逻辑上又是相对独立的,所以就可以包装成一个操作函数。也就是说,业务流程会依次调用各个操作函数,来完成具体的业务操作。

如何衔接两个操作函数之间的页面?

完成一个业务流程操作,往往会需要依次调用多个操作函数,但是操作函数和操作函数之间会有页面衔接的问题,即前序操作函数完成后的最后一个页面,必须是后续操作函数的第一个页面。如果连续的两个操作函数之间无法用页面衔接,那就需要在两个操作函数之间加入额外的页面跳转代码,或者是在操作函数内部加入特定的页面跳转代码。

业务流程抽象

在解决如何把控操作函数的粒度,以及如何衔接两个操作函数之间的页面这两个问题的过程中,我引入了业务流程的概念。
这个概念有点拗口,难以理解。但是,没关系,我举个例子,你就豁然开朗了。
假设,某个具体的业务流程是:已注册的用户登录电商平台购买指定的书籍。那么,基于业务流程抽象的测试用例伪代码,如图 1 所示。
image.png-564.8kB

这 4 个业务流程都是作为独立的类封装的,可以被很方便的重用并灵活组合,类的内部实现通常是调用操作函数。而操作函数内部,则是基于页面对象模型完成具体的页面控件操作。
然后,对于每一个业务流程类,都会有相应的业务流程输入参数类与之一一对应。具体的步骤通常有这么几步:
初始化一个业务流程输入参数类的实例;给这个实例赋值;用这个输入参数实例来初始化业务流程类的实例;执行这个业务流程实例。

执行业务流程实例的过程,其实就是调用操作函数来完成具体的页面对象操作的过程。

代码的第 2-6 行,调用的是 LoginFlow,完成了用户登录的操作。2: LoginFlowParameters loginFlowParameters = new LoginFlowParameters();3: loginFlowParameters.setUserName("username");4: loginFlowParameters.setPassword("password");5: LoginFlow loginFlow = new LoginFlow(loginFlowParameters);6: loginFlow.execute();

第 2 行,初始化了 LoginFlow 对应的 LoginFlowParameters 的实例。第 3-4 行,通过 setUserName 和 setPassword 方法将用户名和密码传入该参数实例。第 5 行,用这个已经赋值的参数实例来初始化 LoginFlow。第 6 行,通过 execute 方法发起执行。执行之后,LoginFlow 会调用内部的操作函数,或者直接调用页面对象方法,完成用户登录的操作。

伪代码的第 9-12 行,用和 2-6 行类似的方式调用了 SearchBookFlow,完成了书籍搜索的操作。

9: SearchBookFlowParameters searchBookFlowParameters = new SearchBookFlowParameters();
10: searchBookFlowParameters.setBookName("bookname");
11: SearchBookFlow searchBookFlow = new SearchBookFlow(searchBookFlowParameters);
12: searchBookFlow.withStartPage(loginFlow.getEndPage()).execute();

需要特别注意的是,第 12 行中 withStartPage(loginFlow.getEndPage()) 的含义是,SearchBookFlow 的起始页面将会使用之前 loginFlow 的结束页面。显然,通过这种方式可以很方便地完成两个业务流程之间的页面衔接。同时,从中还可以看出,其实每个业务流程都可以接受不同的起始页面。以 SearchBookFlow 为例,它的起始页面既可以是书籍首页,也可以是其他页面,但是需要在它的内部对不同的初始页面做出相应的处理,以保证这个业务流程真正开始的页面是在书籍搜索页面。同样,由于业务流程存在分支的可能性,每个业务流程执行完成的最终页面也不是唯一的,你可以使用 getEndPage 方法拿到这个业务流程执行结束后的最后页面。通过这段代码的解读,你可以很清楚地理解,业务流程之间的页面衔接是如何实现的。

伪代码的第 15-18行,调用了CheckoutBookFlow,完成了书籍购买操作

15: CheckoutBookFlowParameters checkoutBookFlowParameters = new CheckoutBookFlowParameters();
16: checkoutBookFlowParameters.setBookID(searchBookFlow.getOutPut().getBookID());
17: CheckoutBookFlow checkoutBookFlow = new CheckoutBookFlow(checkoutBookFlowParameters);
18: checkoutBookFlow.withStartPage(searchBookFlow.getEndPage()).execute();

第 15 行,初始化了 CheckoutBookFlow 对应的 checkoutBookFlowParameters 的实例。第 16 行,通过 setBookID(searchBookFlow.getOutPut().getBookID()),将上一个业务流程 searchBookFlow 的输出参数,作为了当前业务流程的输入参数。这是典型的业务流程之间如何传递参数的示例,也是很多测试场景中都要用到的。第 17 行,用 checkoutBookFlowParameters 参数实例来初始化 checkoutBookFlow。第 18 行,通过 execute 方法发起执行。这里需要注意的是,checkoutBookFlow 的起始页面将会使用之前 searchBookFlow 的结束页面。开始执行后,checkoutBookFlow 会调用内部的操作函数,或者直接调用页面对象方法,完成书籍的购买操作

伪代码的第 21-22 行,调用 LogoutFlow,完成了用户登出操作。

21: LogoutFlow logoutFlow = new LogoutFlow();
22: logoutFlow.withStartPage(checkoutBookFlow.getEndPage()).execute();

第 21 行,由于 LogoutFlow 不带参数,所以直接初始化了 LogoutFlow。第 22 行,通过 execute 方法发起执行。这里 LogoutFlow 的起始页面将会使用之前 CheckoutBookFlow 的结束页面。开始执行后,LogoutFlow 会调用内部的操作函数,或者直接调用页面对象方法,完成用户登出操作。通过对这些代码的解读,我解释了业务流程是什么,并从使用者的角度分析了它的主要特点。比如,如何实现不同业务流程间的页面衔接,如何在不同的业务流程间传递参数等。为了加深印象,我再来总结一下业务流程的优点:业务流程(Business Flow)的封装更接近实际业务;基于业务流程的测试用例非常标准化,遵循“参数准备”、“实例化 Flow”和“执行 Flow”这三个大步骤,非常适用于测试代码的自动生成;由于更接近实际业务,所以可以很方便地和 BDD 结合。BDD 就是 Behavior Driven Development,即行为驱动开发,我会在后续文章中详细讲解。

posted on 2020-03-29 16:35  cherry小樱桃  阅读(361)  评论(0编辑  收藏  举报