- 高效自动化测试平台:设计与开发实战
- 徐德晨 茹炳晟
- 4380字
- 2021-03-29 07:05:12
3.2 资源选择器
面对所提供的测试环境,测试用例开发者需要通过特定的方法来选择所需测试资源。一种简单的做法是,让测试用例开发者自己遍历测试资源池,再用大量的循环语句和条件判断语句来获取所需测试资源。这是一个典型的反面教材,本节我们将会介绍资源选择器的概念,通过封装一系列的方法来方便测试用例开发者选择所需测试资源。
3.2.1 设计资源选择器的目的
通常在测试用例中,我们需要对被测对象进行一些预判断,比如被测对象是否存在,是否满足测试要求,所需要的测试设备是否足够等。然后,从测试资源池中选择合适的资源进行测试。
先来看一个混合测试设备的应用场景。在系统测试中,一个场景会有很多不同类型的测试设备。比如,某一测试平台上有很多类型的手机终端——Android的、Apple的,等等。其中有一个测试用例是针对某个特定设备进行测试的,需要Android的手机,并且其操作系统的版本要小于8.0版本,如果这个测试用例是健壮的,就需要从测试资源中过滤出满足上述条件的手机资源,实现代码如下:
![](https://epubservercos.yuewen.com/EF7118/18096060308238406/epubprivate/OEBPS/Images/39042_85_1.jpg?sign=1739686702-SNvR3Yks9o9xcEtNGJsGzpXQsuF0Mq2y-0-5033f55ba716ca5961e91574bcfca3c4)
再来看一个WIFI AP设备的测试场景:获取一个WIFI AP作为被测设备,以及三个STATION作为测试设备,通过管理WIFI AP上所连接的测试仪表接口和STATION上所连接的测试仪表接口来发送测试流量。
根据上面的需求,我们定义如表3-1所示的流程。
表3-1
![](https://epubservercos.yuewen.com/EF7118/18096060308238406/epubprivate/OEBPS/Images/39042_85_2.jpg?sign=1739686702-lzyKg4bj5muxKcyGJZEaf1ZjwPrjnViz-0-30f9678d811620ad2aaca947da8dad4d)
在表3-1的流程中,只有所有条件都满足才能继续测试,否则这个测试用例就将被标为跳过。具体实现代码如下:
![](https://epubservercos.yuewen.com/EF7118/18096060308238406/epubprivate/OEBPS/Images/39042_85_3.jpg?sign=1739686702-W55Enw7vBCbysC6w8ePL1uNNC3ZzQg6k-0-40550cf5e32fe73cc81e14a365e34c36)
![](https://epubservercos.yuewen.com/EF7118/18096060308238406/epubprivate/OEBPS/Images/39042_86_1.jpg?sign=1739686702-VaKoWbOfAyKX6Cvxaxdk0rNqBmdh8IDO-0-c392a7ef9aa6007a2f5004163220da51)
上述代码中有大量的if和for相互嵌套,层次非常深,难以理解和维护。我们是否有一种更好的方法,能使测试用例开发者根据条件选择所需资源呢?
接下来,我们设计一个资源选择器,给测试用例的开发者提供一个统一的接口,使用户可以通过调用资源选择器的方法来获取想要的测试资源。具体方法是,提交获取资源的请求,资源选择器处理这个请求,并返回所需的测试资源。
在提交获取资源的请求前,我们要确定三个静态条件:
• 资源类型:所请求的资源类型。
• 资源名称:所请求的资源名称。
• 请求数量:所请求的资源数量。
如果请求资源的方法为collect_device,那么我们可以将这三个静态条件作为参数,分别是resource_type(资源类型)、resource_name(资源名称)、resource_count(请求数量),通过这些参数对资源进行获取。比如调用collect_device(resource_type=”AP”)方法,就能获取所有类型为AP的资源,同样我们可以通过collect_device(resource_type=”AP”,resource_count=3)来限制数量。
下面,我们为ResourcePool类添加两个新方法,用于资源获取,代码如下:
![](https://epubservercos.yuewen.com/EF7118/18096060308238406/epubprivate/OEBPS/Images/39042_87_1.jpg?sign=1739686702-VV0I28hJeZ0McGVBYup9LpVkj7XIatD7-0-95864b62052e7826b42e54167b0a26b1)
上面讲的三个静态条件比较简单,接下来我们还要讲一些复杂的条件。比如在本节获取满足条件的手机资源的案例中,请求的资源类型是手机,限制条件是Android版本要小于8.0版本。再比如,在WIFI AP的测试案例中,除了请求特定资源,还需要根据请求的特定资源和其连接的拓扑关系,获取相应的连接拓扑对端的设备(比如A P所连接的S TA设备、A P所连接的数据流量测试端口、S TA所连接的数据流量测试端口等)。
由于这些请求条件和具体的资源属性相关,我们不能将其预定义在collec_device方法的参数中,而是要通过某种方法来封装这些资源获取的限制条件。
3.2.2 资源限制条件机制
对于限制条件,不同的测试资源会有不同的定义,所以无法用一个简单的表达式来定义,也无法用一组变量来描述,更何况不同的测试团队会根据他们自己的产品定义专属的测试资源,所以限制条件也必须被设计成可扩展的。
我们引入限制(Constraint)的概念,这个“限制”是一个名词,这意味着一个“限制”可以被描述成一个类,所有的限制条件都有一个方法——是否满足(is_meet),以此来判断某个资源是否满足此限制。所以“限制”的设计类似于一个过滤器,将符合条件的设备保留,去除不符合条件的设备,限制条件的过滤流程如图3-7所示。
![](https://epubservercos.yuewen.com/EF7118/18096060308238406/epubprivate/OEBPS/Images/39042_88_1.jpg?sign=1739686702-tacokyGw3Mav1aNL7Bs0iKjzZfY8fqlr-0-54629d02afcb59ce21a1c29956e8d05d)
图3-7 限制条件的过滤流程
3.2.2.1 限制类的设计和实现
我们设计一个“限制”的基类,包含一些抽象的方法,提供给测试用例设计者来扩展自己的资源限制条件,具体实现代码如下:
![](https://epubservercos.yuewen.com/EF7118/18096060308238406/epubprivate/OEBPS/Images/39042_89_1.jpg?sign=1739686702-pA6F2ApU3KDqaEJ82F0LuZKyUe0nkmOv-0-fb6c3110c74cfc5dde804eb1314b9c92)
如上述代码所示,这个“限制”基类是所有Constraint的基类,description用来存放对该限制的描述信息。抽象方法is_meet用来判断传入的资源对象是否满足条件,所以子类通过该方法对设备进行判断,以确定是否满足条件。
我们来设计几个简单的限制条件,以3.2.1节中手机选择的两个限制条件(手机操作系统类型,手机操作系统版本)为例,代码如下:
![](https://epubservercos.yuewen.com/EF7118/18096060308238406/epubprivate/OEBPS/Images/39042_89_2.jpg?sign=1739686702-jcN0ZoIYazYGGty1oZ31i1JOLMza9sfI-0-0378f53388fc86c0f6e28ea78cba0fc2)
![](https://epubservercos.yuewen.com/EF7118/18096060308238406/epubprivate/OEBPS/Images/39042_90_1.jpg?sign=1739686702-O5gwMtvBSv5OGpLRlpfa8BJ281mmpLEq-0-4082fc3c0ec787acc7554c15f923dde8)
从类的命名上看,PhoneMustBeAndroidConstraint类很好理解,被测手机必须是Android手机,用户将必要的参数作为资源限制子类的属性,在实例化的过程中,将这些属性赋值,在is_meet方法中进行具体的判断,最后返回True或False,表示资源是否满足该限制条件。在该类中,version和version_op作为可选的属性,除了判断资源类型是否为Android,还可以对Android的版本进行判断。
在处理多条件判断的时候,我们甚至可以将Constraint嵌套使用,比如3.2.1节中第二个无线AP测试的例子,我们首先用3.1.2节中所设计的资源描述方法建立一个测试拓扑,代码如下:
![](https://epubservercos.yuewen.com/EF7118/18096060308238406/epubprivate/OEBPS/Images/39042_90_2.jpg?sign=1739686702-vlqfzXG6BBiBd5u3UevQpaQasdAIBKio-0-6be9fc7920a48ec021eb47432892f3bc)
![](https://epubservercos.yuewen.com/EF7118/18096060308238406/epubprivate/OEBPS/Images/39042_91_1.jpg?sign=1739686702-vbiX40DIawHVP8SdDdKPe1CVyhRNozzi-0-90ddbe84ecfd15745556c095388dd710)
![](https://epubservercos.yuewen.com/EF7118/18096060308238406/epubprivate/OEBPS/Images/39042_92_1.jpg?sign=1739686702-psLzj6bNCPQZ5jn7aK1RSejDUDjAvj5O-0-6147d372bcd2c65ffdc7b754968a65ef)
上述代码描述了如图3-8所示的拓扑结构。
![](https://epubservercos.yuewen.com/EF7118/18096060308238406/epubprivate/OEBPS/Images/39042_92_2.jpg?sign=1739686702-kLY8DKG8z2RL0Ei651GzgAlPuWB7A79f-0-650a32f4f1667c337d27d1bcd1258069)
图3-8 一个AP测试的拓扑结构
接着,我们根据案例中所需要的判断,定义限制条件:
• AP必须有STA连接,并且规定连接的数量的大小。
• AP必须有流量测试仪表的连接,并且规定流量仪表端口速率的大小。
• S TA必须有流量测试仪表的连接,并且规定流量测试仪表端口速率的大小。
总结一下,我们可以定义如下类来描述这些限制条件:
• ApMustHaveStaConnected.
• DeviceMustHaveTrafficGeneratorConnected.
• TrafficGeneratorSpeedMustGraterThan.
我们从最简单的TrafficGeneratorSpeedMustGraterThan类来实现,代码如下:
![](https://epubservercos.yuewen.com/EF7118/18096060308238406/epubprivate/OEBPS/Images/39042_93_1.jpg?sign=1739686702-Fhe3y2eljP2TkDayCtVv7JXdIKwk18D5-0-8dd56d6e519cf7266e244f8136b4ce54)
从上述代码中可以看到,在这个限制条件方法中传入的资源类型必须是端口,通过端口判断其所处的设备是否为流量测试仪器,然后判断其速率是否大于等于限制条件所规定的速率。
接下来,我们来实现DeviceMustHaveTrafficGeneratorConnected类,代码如下:
![](https://epubservercos.yuewen.com/EF7118/18096060308238406/epubprivate/OEBPS/Images/39042_93_2.jpg?sign=1739686702-r3HMYtYdSdAcPj0SbcP8EkPiAP9spq7O-0-eed9b739bb362163944f2f73c84001b2)
![](https://epubservercos.yuewen.com/EF7118/18096060308238406/epubprivate/OEBPS/Images/39042_94_1.jpg?sign=1739686702-8IsVFJS5Onq6RjxqQcWYhXWRg3NbgPlg-0-11a34fc0765588a34ccf88c8e1681175)
从上述代码中可以看到,该方法的speed属性是之前定义的TrafficGeneratorSpeedMustGraterThan类的实例(当然也可以是其他限制类型)。port_count是与测试仪表相连接的端口数。在is_meet方法的具体实现中遍历所有ETH类型的端口,判断每一个远端端口是否满足测试仪表端口类型的条件,如果speed属性被设置了期望的限制条件,则使用speed属性中的资源限制类实例的is_meet方法,来判断该端口的速率是否满足条件。
最后,我们实现ApMustHaveStaConnected类,代码如下:
![](https://epubservercos.yuewen.com/EF7118/18096060308238406/epubprivate/OEBPS/Images/39042_94_2.jpg?sign=1739686702-39xUxeqJ1ZZY6hoY9szneeN8g9ZZjhgl-0-a75d26dc6bee83b800bacbe5466f8510)
![](https://epubservercos.yuewen.com/EF7118/18096060308238406/epubprivate/OEBPS/Images/39042_95_1.jpg?sign=1739686702-HUFLw9jNVvQJDB1nxnb1tmUnRtuJHMhG-0-756139dea18610097b27e79589f9a93f)
如上代码所示,sta_constraints表示STA设备必须满足的限制条件,sta_count表示至少需要多少STA连接在AP上。在is_meet方法的实现中,对AP的WIFI端口上的远端端口所处设备类型进行判断,如果是STA类型,则通过sta_constraints中提供的资源限制类实例,逐一判断该设备是否符合所有的限制条件。
下面我们对上述资源限制条件类做一下测试,代码如下:
![](https://epubservercos.yuewen.com/EF7118/18096060308238406/epubprivate/OEBPS/Images/39042_95_2.jpg?sign=1739686702-aezfK1nfvAVrbweZbxYY3PyIj3ohfd8y-0-fcae02e543a18d0e36de5282c9d24e16)
![](https://epubservercos.yuewen.com/EF7118/18096060308238406/epubprivate/OEBPS/Images/39042_96_1.jpg?sign=1739686702-BN3yxogzVopFVHOn9tyHQjMqFXB0xHCy-0-e26fff2364d9cd7d7c2331cbc1e511a7)
我们定义了6个限制条件类的实例,代码注释中已经说明了这些实例的作用,代码的运行结果为True、True、False、True、False、True。所以我们再次回顾3.2.1节AP测试环境中资源获取的判断过程,在实现了constraint机制之后,我们就可以使用这段测试代码中的constraint4和constraint6实例来判断测试资源对象,从而实现获取满足条件的AP功能。
3.2.2.2 资源获取方法的改造
在设计了限制条件类之后,我们就可以对ResourcePool类中的collect_device方法进行改造,将限制条件类作为参数传入该方法,具体代码如下:
![](https://epubservercos.yuewen.com/EF7118/18096060308238406/epubprivate/OEBPS/Images/39042_96_2.jpg?sign=1739686702-z8mAGp6HsMannMUMmpBHxthPqORwuVls-0-3803c1832343d128cc8cfa38e8978f6f)
![](https://epubservercos.yuewen.com/EF7118/18096060308238406/epubprivate/OEBPS/Images/39042_97_1.jpg?sign=1739686702-lcfvjnMBycZqnJR80C8fBXEzSKMLZQX2-0-b547b876e0378cd8a36f2400c6085009)
当我们获取相应类型的资源设备之后,将该资源和传入的限制条件进行逐一判断,只有满足所有条件才作为符合条件的资源返回。所以我们可以继续扩展3.2.2.1节中的测试代码,将获取3.2.1节的AP环境的实现代码修改如下:
![](https://epubservercos.yuewen.com/EF7118/18096060308238406/epubprivate/OEBPS/Images/39042_97_2.jpg?sign=1739686702-KdMytFsmXKl3CeUU5AGqZ0sshDzIh0LL-0-eccb3fcc6c12248c16f697037ff9deb5)
如上代码所示,通过这种将条件封装成类的方法,极强地增加了代码的可读性,我们在设计Constraint类的时候,尽可能地使用自然语言来描述,这样在测试用例中挑选资源的代码的可读性将会非常强。而这些条件类的封装又极强地增加了代码的可复用性,一些公共的限制条件可以下沉到业务代码的底层,提供给不同的开发者甚至不同团队来使用和维护。
3.2.3 资源获取路由
上节中,我们通过Constraint机制获取了符合条件的设备,并通过AP测试环境获取了资源。但是除了AP,我们还需要和AP连接的测试仪表端口、与AP相连的STA设备及其所连接的端口。换句话说,如果我们想获取相关资源还要写新的代码,即便这个资源已经满足了所有的条件——这显然又增加了工作量。
继续来看图3-8,假设我们找到了符合条件的AP1,事实上符合条件的STA设备及流量测试仪的端口,在资源限制条件类的is_meet方法的执行过程中也被相应地确定了。
下面我们来讲解资源查找路由的拓扑,如图3-9所示。
![](https://epubservercos.yuewen.com/EF7118/18096060308238406/epubprivate/OEBPS/Images/39042_98_1.jpg?sign=1739686702-hXigXYgT5vplRfiZdRP9qBWtfwaD6JLX-0-ceb969d1bf9757f7712e13c1404d33c8)
图3-9 资源查找路由的拓扑
在图3-9中,为了简化操作,我们针对一个资源来获取其所符合条件的连接拓扑。在这个连接拓扑中,假设我们获取了设备1,限制条件是“设备1必须与设备5相连”,并且在测试过程中我们同时需要用到设备1和设备5,这种情况是设备到设备的直接连接。
下面我们考虑两种情况:
第一,假设获取设备1的条件是“设备1与设备7相连”,暂不考虑中间隔了多少其他设备。并且,测试中我们需要用到设备1和设备7,以及所有中间经过的设备。
第二,设备1必须与设备X连接,设备X必须与设备7连接。
这两种情况都需要返回设备1和设备7之间的所有设备或端口。
笔者把这种情况称为资源获取路由。既然我们在做资源查询条件判断的时候,已经确定了符合条件的资源,那么我们是不是可以在确定资源限制的过程中,就将这些符合条件的设备通过某种方法返回给测试用例开发者呢?
3.2.3.1 Constraint类的扩展
Constraint类的is_meet方法用于判断当前资源是否符合其限制条件,如果is_meet方法是对对端的设备进行判断的,那么在判断的过程中我们可以得到满足相应条件的资源。所以我们可以将Constraint类进行扩展,以获取满足条件的连接,具体实现代码如下:
![](https://epubservercos.yuewen.com/EF7118/18096060308238406/epubprivate/OEBPS/Images/39042_99_1.jpg?sign=1739686702-XwPY31gJunN9Zc8a5ILfHx1XnM5EysjN-0-c28e023da2432cc50bbd42fc2de5ad20)
ConnectionConstraint类继承自Constraint类,并且添加了新的方法get_connection,用于返回满足条件的对端端口。
我们以3.2.2.1节中的DeviceMustHaveTrafficGeneratorConnected类为例,修改代码如下:
![](https://epubservercos.yuewen.com/EF7118/18096060308238406/epubprivate/OEBPS/Images/39042_99_2.jpg?sign=1739686702-k5TOSR6xGnvPPF6mU8r8Za9Go757tiBc-0-4e4cd49dd6316776d74e93ab5e272392)
![](https://epubservercos.yuewen.com/EF7118/18096060308238406/epubprivate/OEBPS/Images/39042_100_1.jpg?sign=1739686702-slkEaFPXFnd1lOlVnTu5e5BgFfQ1lu85-0-338fcab58b08bd74eedb008dc11c88b3)
我们把原来的判断逻辑从is_meet方法中移至get_connection方法中,然后将符合条件的远端端口返回。而is_meet方法巧妙地使用get_connection的返回来判断是否符合条件。这是一种简单的情况,通过Constraint获取被请求设备的直接连接。
我们以同样的方法来改造ApMustHaveStaConnected类,但这个类相对复杂些,因为我们可能给所连接的STA设备设置connection限制条件,比如针对3.2.1节的WIFI AP测试案例,我们使用了DeviceMustHaveTrafficGeneratorConnected这个connection限制赋值给sta_constraint属性。这样,如果给ApMustHaveStaConnected添加了get_connection方法,那么返回的connection中,就必须添加DeviceMustHaveTrafficGeneratorConnected的get_connection方法返回的结果,具体实现代码如下:
![](https://epubservercos.yuewen.com/EF7118/18096060308238406/epubprivate/OEBPS/Images/39042_100_2.jpg?sign=1739686702-mevsYqCSJooD4iK4MCBczHrFPjIziNo7-0-9efd6d43e4a4c14d2ee5b2901ff87f7b)
![](https://epubservercos.yuewen.com/EF7118/18096060308238406/epubprivate/OEBPS/Images/39042_101_1.jpg?sign=1739686702-iX0kfihZEaWmrXlrblyBg05H0Tb6Mr6b-0-6dda9c20b37a24e5e2e936b1e5b6e520)
![](https://epubservercos.yuewen.com/EF7118/18096060308238406/epubprivate/OEBPS/Images/39042_102_1.jpg?sign=1739686702-LZcVkedJcMya8FysZl9IegDUZAjpgUAY-0-7d2862cbdf670e3df169e3de633a1039)
在构造方法__init__中,我们将传入的sta_constraints进行分类,分成普通的限制(Constraint)和连接限制(ConnectionConstraint),然后在get_connection操作中对普通的限制进行判断,判断对端设备是否满足一般限制条件,当所有的一般限制条件满足后,再判断连接限制,并返回所有连接限制get_connection所返回的连接,最后将这些返回的连接一起返回。
这种复杂的获取路由的方法并没有绝对的connection返回的格式,比如这个类中我们定义返回的是一个元组类型的列表,每个元组的第一个元素表示找到的S TA的端口,第二个元素表示该S TA满足条件的所有连接的对端端口。
3.2.3.2 资源获取路由的方法实现
我们在3.2.2.2节中实现的collect_device方法可以获取单个资源,接下来我们设计另一个获取资源的方法——collect_connection_route方法,代码如下:
![](https://epubservercos.yuewen.com/EF7118/18096060308238406/epubprivate/OEBPS/Images/39042_102_2.jpg?sign=1739686702-i4yDZJ0NnHcEgcmFYEqFGRyYaxtqef0k-0-bea96d4b58cf4e18f329fb2304e5df8f)
![](https://epubservercos.yuewen.com/EF7118/18096060308238406/epubprivate/OEBPS/Images/39042_103_1.jpg?sign=1739686702-0jUaxdalwQuiJ7jQRBkupHXml15z1xt0-0-9b0f4866b1793413229241cb5c415c75)
该方法由于用来获取连接,所以constraints参数必须全部为连接限制。遍历所有的连接限制,判断每个连接限制是否返回了所需要的连接端口,然后将所有的端口返回。
我们继续以3.2.2节中测试资源限制类的代码来说明这个方法的使用:
![](https://epubservercos.yuewen.com/EF7118/18096060308238406/epubprivate/OEBPS/Images/39042_103_2.jpg?sign=1739686702-Kv0ASEwolGOOG5wX91pIggjR7p18KN1H-0-8fbe663dc9094e78f3847ed454450ff2)
输出结果如下:
![](https://epubservercos.yuewen.com/EF7118/18096060308238406/epubprivate/OEBPS/Images/39042_103_3.jpg?sign=1739686702-0hpIrVooPSmwDaANTKctC3vZMJ5ho06L-0-861d9bbbfe0ff41788c6bd6bc0fbfe5a)
虽然我们获取了所有的connection信息,但是由于列表中每个条目的类型都不一致,所以处理起来会很麻烦。
笔者建议,在这种情况下,不要使用过长的路由来设计限制条件,或者每次只使用一种限制条件,然后进行多次判断,相关代码如下:
![](https://epubservercos.yuewen.com/EF7118/18096060308238406/epubprivate/OEBPS/Images/39042_104_1.jpg?sign=1739686702-aYGrEyc5UaQorabnRu8bfN1RYQFjjN2q-0-8b8a14fcab6b2f1864f16841bf0e12fa)
输出结果如下:
![](https://epubservercos.yuewen.com/EF7118/18096060308238406/epubprivate/OEBPS/Images/39042_104_2.jpg?sign=1739686702-byRjIMwszut8NVMK8VLkUVCIGSTZhUYW-0-875b6ca6f2702448798d88340edcfcf8)