webUI入门及单例模式
回归测试
版本迭代,进行优化,重新部署了新环境,只是前端页面发生了改变,api没有变化,只需要把host更换成新地址就可以
重新执行用例,用例通过
自动化类型分配
- 什么情况下使用api,什么情况下使用ui?
- 前后端分离项目,API是针对后端的,可能前端开发速度没有那么快,后端已经开发完成,就可以使用API进行逻辑验证
- 只有后端,比如数据交互中心,必须使用API进行测试
- 一般来说,通过ui实现的都可以使用api来完成,一般前端都是需要调用后端来完成的,但是通过api测试,就绕过了前端了,前端的问题就无法发现了,比如页面的布局,文字提示之类的
- ui既可以验证前端,也可以验证后端,因为通过操作前端,前端调用后端,可以对后端的逻辑进行测试
- 如果前端测试发现了问题,再去分析是前端的还是后端的问题
- 通过抓包来判断,如果发现数据发送没有问题,后端返回有异常,就是后端的问题,那后端没有问题,前端有异常,那就是前端的问题了(排除网络其他因素影响),一般优先判断后端的问题
- UI比较适合测试流程,比如订单类的业务,经过好几个页面,很复杂,一系列的操作,如果使用api来测试,那我们就需要写几十个api,各种调用,写得很复杂,那如果我们通过ui来实现,通过页面上的点点点,填写表单,选择一下,就可以实现了;如果使用api,那就要先查询,再操作,先查询,再操作,要写很多api,很麻烦,如果通过ui来实现,那就简单了,因为UI就是模拟人的操作,点击一下,选择一下,输入一下就可以完成了
- API的侧重点是对数据的校验,而UI的侧重点是针对流程
了解业务场景
- 新增日程:填写日程信息,新增成功,分派日程给其他用户
- 其他用户登录系统,首页查看日程
webUI设计模式
PO模式一般适用于大型系统,我们只需要把页面的元素封装好放到对应的配置文件里面就可以了,页面比较多,页面元素频繁变动
PO库的实现
- 以面向对象形式封装常用UI操作
- 合理规划层级结构
- 抽取元素定位到配置文件
在pylib的webUI目录下,新建一个文件common.py,里面定义通用类BasePage,页面通用操作方法
class BasePage:
def click(self):
pass
def input_text(self):
pass
在pylib的webUI目录下,新建一个文件business.py,里面定义业类
#定义业务操作类
class LoginPage(BasePage):
pass
点击和输入,都需要对页面元素进行操作,对页面操作就需要知道元素的定位方法,使用什么定位方法呢,id,name,css,xpath
我们不能给别人规定死,那怎么办呢,就需要写一个通用的定位方法
同时对元素进行操作,需要通过driver进行,需要先实例化一个driver
def __init__(self):
self.driver=webdriver.Chrome()
但是这样会打开多个浏览器,每次实例化就打开一个浏览器,一个对象就是一个浏览器,多次实例化就打开多个浏览器
解决打开多个浏览器的方法
方法一:
外面实例化好driver,然后通过传参进来
class BasePage:
def __init__(self,driver):
self._driver=driver
def click(self,locator):
self._driver.find_element(*locator).click()
def input_text(self,locator,text):
self._driver.find_element(*locator).send_keys(text)
#定义业务操作类
class LoginPage(BasePage):
pass
class MainPage(BasePage):
pass
if __name__ == ‘__main__‘:
from selenium import webdriver
driver=webdriver.Chrome()
LoginPage(driver)
MainPage(driver)
方法二:
单例模式
单例模式
非单例模式
日常见到的init方法是否产生对象?初始化方法并不产生对象
实例化就像新生儿一样,初始化就像给新生儿穿衣服一样,并不会产生新的
init方法必须先有对象才能使用,那什么方法可以产生对象呢?
new 方法来产生对象,同时init方法不能有返回值,new必须有返回值
如果我们需要实现单例模式,就必须从new这个地方下手
首先判断当前类是否有实例存在,存在就返回当前实例
hasattr()#相当于python的反射,如果当前对象存在某个属性(方法或者字段)就返回true,反之就是false
cls也是一个对象,由元类来产生,类控制对象,元类控制类
object类是所有类继承的祖先类
class Single:
def __new__(cls, *args, **kwargs):#new产生对象,并且需要定义返回值
#首先判断当前类是否有实例存在,如果存在就直接返回,否则就创建一个新的实例再返回
if hasattr(cls,‘_isinstace‘):#hasattr相当于python的反射,如果当前类存在某个属性就返回true,否则返回false
return cls._isinstace
else:
cls._isinstace=object.__new__(cls)
return cls._isinstace
def __init__(self):#初始化是否产生对象?不产生对象,init方法不可以有返回值
pass
if __name__ == ‘__main__‘:
assert Single()==Single()
看看子类是不是也具有相同特性?子类也具有父类的特性
class Child(Single):
pass
if __name__ == ‘__main__‘:
assert Child()==Child()
如果else里面没有其他语句,else可以省略,因为我们用了return,后面的语句就不会执行了
class Single:
def __new__(cls, *args, **kwargs):#new产生对象,并且需要定义返回值
#首先判断当前类是否有实例存在,如果存在就直接返回,否则就创建一个新的实例再返回
if hasattr(cls,‘_isinstace‘):#hasattr相当于python的反射,如果当前类存在某个属性就返回true,否则返回false
return cls._isinstace
cls._isinstace=object.__new__(cls)
return cls._isinstace
def __init__(self):#初始化是否产生对象?不产生对象,init方法不可以有返回值
pass
class Child(Single):
pass
if __name__ == ‘__main__‘:
assert Child()==Child()
至此单例模式已经调试完成,我们可以把当前代码放到pulgins模块下面
接着在common模块下面创建一个生成webdriver类,继承Single类来实现创建浏览器
class BasePage:
def __init__(self):
self._driver=WebdriverCreater().get_browser()
def click(self,locator):
self._driver.find_element(*locator).click()
def input_text(self,locator,text):
self._driver.find_element(*locator).send_keys(text)
class WebdriverCreater(Single):
def get_browser(self):
self._driver=webdriver.Chrome()
self._driver.implicitly_wait(10)
return self._driver
到business中测试
if name == ‘main‘:
LoginPage()
MainPage()
发现还是打开多个浏览器
分析发现,类已经是单例模式,但是get_browser(),不是单例模式,调用一次就生成一个浏览器
解决:使用单例模式的思想继续改造
class WebdriverCreater(Single):
def get_browser(self):
#如果存在就直接返回,没有就创建一个再返回
if hasattr(self,‘_driver‘):
return self._driver
self._driver=webdriver.Chrome()
self._driver.implicitly_wait(10)
return self._driver
查看BY方法
class By(object):
"""
Set of supported locator strategies.
"""
ID = "id"
XPATH = "xpath"
LINK_TEXT = "link text"
PARTIAL_LINK_TEXT = "partial link text"
NAME = "name"
TAG_NAME = "tag name"
CLASS_NAME = "class name"
CSS_SELECTOR = "css selector"