问题背景

观察使用selenium进行自动化测试的过程,我们可以将它概述为:

  • 启动测试进程,在该进程中构建WebDriver
  • 启动浏览器进程,将它与WebDriver建立连接
  • 使用WebDriver向浏览器发送请求从而达到控制的效果

在实际的代码中,我们所需要做的只有构建WebDriver和通过WebDriver提供的接口控制浏览器这两件事情。对于后者,既然我们的目标是各浏览器的兼容,那么对于所有的浏览器,调用的WebDriver的接口都应该是一致的。所以这部分测试代码应该被共享,唯一的区别应该是构建WebDriver的过程。

我们是基于django的测试框架,django的测试框架的本质是使用python的unittest。python的unittest是基于TestCase这个类的,也就是一个类一族测试样例。上文中提到的两件实际代码中我们做的事情就是以TestCase为单位被编写的。每当一个TestCase类被初始化时,我们就为它构建一个WebDriver,然后在这个类中的每个测试样例中通过WebDriver来控制浏览器。这就导出一个结论:一个类一个WebDriver、一套控制逻辑。

注意到我们希望控制逻辑在各浏览器的测试中是相同的,但各浏览器使用的WebDriver是不同的。

这就很自然地会想到类的继承,一个基类写控制逻辑,然后每个浏览器派生这个基类,重载构建WebDriver的逻辑。但要注意到,我们有大量的TestCase类,如果每个TestCase都需要为它编写一族继承类的代码,而且这些代码还几乎一模一样,仅仅只是因为代码逻辑上需要所以才写的话,那太累了。

所以,我们最后的需求是:有一族基类(控制逻辑),还有一族函数(构建WebDriver),如果将派生看成乘的话,我们就是想要他两的笛卡尔积。也就是每个基类都有一族派生类,每个派生类对应于给基类添加那一族函数中的一个函数。

核心概念:动态继承

核心的概念是使用python类对象的思想在模块被加载时进行动态继承。这需要用到types module,python提供了该模块使得动态构建类对象变得简单。我们只需简单地调用types.new_class函数,并在参数中指定基类就可以动态地从该基类中继承一个新类。

在继承得到一个新类后我们需要为它添加它的继承因子,也就是给它添加方法。这也非常简单,在new_class的exec_body中指定就行了。新的方法会在new_class执行中被添加到被添加到新类的__dict__中,也就是命名空间中。

>>> import types
>>> def f(self):
print("F")



>>> A = types.new_class("A", exec_body=lambda ns: ns.update({"nf":f}))

>>> a = A()

>>> a.nf()

F

>>> A.dict

mappingproxy({'nf': <function f at 0x00000227B0AD1C80>, 'module': 'types', 'dict': <attribute 'dict' of 'A' objects>, 'weakref': <attribute 'weakref' of 'A' objects>, 'doc': None})

对于我们的问题,也就只需要两个循环,一个循环遍历基类,一个循环遍历继承因子,然后在循环中构建新类即可。

基类遍历问题

不过遍历基类本身也是一个问题。回归我们的原来的问题,我们的基类是分布在各文件中的大量TC(TestCase,下略),且这些TC之间本身就存在继承结构。方便起见,我们将基类的遍历做成一个根据继承关系的递归过程。使用所有TC的共同基类FrontBasicTC作为递归的根,然后递归遍历所有子类。

这里又会涉及一个问题,上一章中提到了我们会动态继承得到新类,这样如果递归遍历子类的话就会导致无穷递归问题了。所以我们在动态继承时要往新类的命名空间中加上一个标记,当在递归遍历时发现这个标记就停止继续对这个类进行递归。

子类识别问题

注意到我们需要访问FrontBasicTC的所有子类,我们使用的是__subclasses__方法,若一个py文件没有被执行过(也就是import过)的话,那么该方法就不会识别到该py文件中的子类。所以我们需要在test_factory.py的一开始就将所有基类都在的目录template下的所有模块都import进来。为此我们将template作为一个包,然后在它的__init__.py文件中添加了__all__方法,用于from template import *

继承因子遍历问题

这本来不应该是个问题,但我为了更无脑方便地开发,做了一些小trick。因为现在所使用的所有继承因子都是类方法,所以我做成了读入继承因子们所在的模块,然后遍历该模块的属性,对于为类方法的属性,将其看做继承因子进行操作。

这样的好处是不用维护有哪些继承因子的列表,简单地加上本来就要加的@classmethod装饰器就可以自动被识别。

新类识别问题

如前文所述,我们本质上使用的是python的unittest作为测试框架。虽然我们能够在一个模块中动态继承新类了,但还未将这些新类添加到这个模块test_factory的命名空间中,这就导致python的UnitTestRunner识别不到这些新类。

为此我们在test_factory中将__dir__和__getattr__重载,前者返回这个模块的属性的名字的列表,后者根据名字获取属性对象。我们用生成的新类作为test_factory模块的属性,在__dir__和__getattr__中对应返回。

注:这里的做法可能不太好,因为是针对UnitTestRunner所做的欺骗性工作,当python的unittest发生改变时可能会失效,而且还会导致无法读取test_factory的类似__name__这些元属性。

[技术博客]基于动态继承类、WebDriver的浏览器兼容性测试框架搭建的更多相关文章

  1. 【技术博客】 关于laravel5.1中文件上传测试的若干尝试

    关于laravel5.1中文件上传测试的若干尝试 作者:ZGJ 版本:v1.0 PM注:本人这两天也正在尝试解决这一问题,如有进展将及时更新这一博客 在我们的软工第二阶段中,我开始着手进行后端控制器的 ...

  2. [技术博客] Springboot的Controller类使用

    Springboot的Controller类使用 @Controller:处理http请求. 代码: @Controller public class QuestionController { ... ...

  3. 【技术博客】基于JsPlumb和JQuery-UI的流程图的保存和再生成

    开发组在开发过程中,都不可避免地遇到了一些困难或问题,但都最终想出办法克服了.我们认为这样的经验是有必要记录下来的,因此就有了[技术博客]. 基于JsPlumb和JQuery-UI的流程图的保存和再生 ...

  4. 【技术博客】JWT的认证机制Django项目中应用

    开发组在开发过程中,都不可避免地遇到了一些困难或问题,但都最终想出办法克服了.我们认为这样的经验是有必要记录下来的,因此就有了[技术博客]. JWT的认证机制Django项目中应用 这篇技术博客基于软 ...

  5. 【转】【技术博客】Spark性能优化指南——高级篇

    http://mp.weixin.qq.com/s?__biz=MjM5NjQ5MTI5OA==&mid=2651745207&idx=1&sn=3d70d59cede236e ...

  6. 【软工】[技术博客] 用Monaco Editor打造接近vscode体验的浏览器IDE

    [技术博客] 用Monaco Editor打造接近vscode体验的浏览器IDE 官方文档与重要参考资料 官方demo 官方API调用样例 Playground 官方API Doc,但其搜索框不支持模 ...

  7. [技术博客] 软工-Ruby on Rails 后端开发总结分享

    [技术博客] 软工-Ruby on Rails 后端开发总结分享 在这次软件编写中,我们的后端使用了Ruby on Rails (RoR)框架. Rails框架是用Ruby编写的.这意味着当我们为Ru ...

  8. 【新版】Android技术博客精华汇总

    [新版]Android技术博客精华汇总(原文链接内持续更新) http://www.apkbus.com/thread-313856-1-1.html Kotlin Kotlin学习资料汇总 http ...

  9. 一文搞定scrapy爬取众多知名技术博客文章保存到本地数据库,包含:cnblog、csdn、51cto、itpub、jobbole、oschina等

    本文旨在通过爬取一系列博客网站技术文章的实践,介绍一下scrapy这个python语言中强大的整站爬虫框架的使用.各位童鞋可不要用来干坏事哦,这些技术博客平台也是为了让我们大家更方便的交流.学习.提高 ...

随机推荐

  1. 【转载】C#如何往DataTable中新增一个数据列

    在C#中的Datatable数据变量的操作过程中,有时候我们需要往现有的DataTable中新增一个自定义数据列,该列在原有的DataTable变量中并不存在,属于用户手工自定义新增的数据列,在往Da ...

  2. jQuery常用方法(五)

    一.jQuery中常用方法相关方法参数说明:a.无参,获取值b.参数param,设置值c.参数function(index,oldVal){}回调函数[返回我们所要使用的新值]    回调函数的两个参 ...

  3. springboot脚手架liugh-parent源码研究参考

    1. liugh-parent源码研究参考 1.1. 前言 这也是个开源的springboot脚手架项目,这里研究记录一些该框架写的比较好的代码段和功能 脚手架地址 1.2. 功能 1.2.1. 当前 ...

  4. 给router-link 标签添加事件@click 、@mouseover等无效

    需要加上native修饰符. 所以如果在想要在router-link上添加事件的话需要@click.native这样写 所以如果要事件有效的话,改成如下: <router-link v-for= ...

  5. CSS 多列布局

    CSS3 新增多列布局适合排版很长的文字内容,让其多列显示. 一.多列布局 语法格式: columns:column-width | column-count; column-width:定义每列的宽 ...

  6. Spring MVC 上传、下载、显示图片

    目录 1. 准备工作 1.1 数据库表准备 1.2 实体类 User 和 Mapper(DAO) 1.3 pom.xml 依赖包 1.4 SSM 框架的整合配置 2. 控制器 UserControll ...

  7. Centos 7配置阿里云yum源

    1. 禁用 yum插件 fastestmirror 1)修改插件的配置文件 # cp /etc/yum/pluginconf.d/fastestmirror.conf /etc/yum/pluginc ...

  8. linux虚拟机网络配置

    环境:虚拟机-最小化安装  centos7   主机:win10 参考配置文件: TYPE=EthernetPROXY_METHOD=noneBROWSER_ONLY=noBOOTPROTO=stat ...

  9. Junit测试入门

    junit测试的6大注解 @BeforeClass    最先执行,在整个测试类中只会执行一次,所以它只能声明一次,并且被它标注的方法必须声明为static @Before       可以声明多个方 ...

  10. lvs+keepalived高可用负载均衡

    一.实验环境和网络拓扑图 本实验需要5台虚拟机,一台客户机,2台lvs调度器,两台web服务器. 客户机:192.168.0.6/24 lvs1:192.168.0.201/24 lvs2:192.1 ...