自动化测试,包括Web自动化、移动端自动化、接口自动化等,目前暂时先把web自动化的笔记放在这里,而且只放到PO模式的笔记,这些笔记很多知识点都是有实例的,不想就这样子丢了,还是先保存好。后面还有一些,过两天补上:
 
 
 一、什么样的项目需要做自动化测试:
 1、需求变动不频繁
 2、项目周期长
 3、项目需要回归测试
 
 二、什么阶段开始:
 功能测试完毕(手工测试)
 
 三、web自动化测试所属分类:
 1、黑盒测试(功能)-----自动化属于这个
 2、白盒测试(单元)
 3、灰盒测试(接口)
 
 web自动化测试用例:跟着功能测试的用例后面加上“是否自动化”
 
 
 四、安装selenium包
 
 1、****很重要的:pip包管理工具
 
 按Ctrl+r跑出cmd的运行窗口,然后输入pip install selenium  就会安装最新版本的selenium
 安装完后可以输入pip show selenium来查看版本等信息
 可以输入 pip uninstall selenium来卸载
 
 扩展:
 pip install selenium==版本号来指定安装的版本
 在==号后面输入一个错误的版本号按回车,会提示所有可以安装的版本号
 ==号后面抄一个正确的版本就可以了
 
 pip是python中的包管理工具(可以安装、卸载、查看python工具)-------需要先安装好python3的运行环境
 必须连网才能使用Pip
 pip list:查看通过pip工具安装的插件或工具
 
 
 2、可以通过pycharm来安装
 推荐原因:安装到当前工程环境里
 操作:File--setting--Project:当前工程名称---Project Interpreter--点击+号安装相应包和版本
 提示:如果使用pip install 插件名  安装过后,打开pycharm,导包操作时,提示找不到此包,那就
 说明使用pip install 默认安装 的路径和当前工程所有的环境路径不是同一个环境,进行以上处理即可
 安装浏览器驱动
 firefox  48以上版本   需要selenium 3.x+firefox驱动(geckodriver)
 下载地址:https://github.com/mozilla/geckodriver/releases/
 里面有各个版本的驱动,有相对应的firefox版本
 
 驱动和浏览器版本是对应的,下载哪一个要去官网查看
 google 
 http://chromedriver.storage.googleapis.com/index.html
 
 
 应用:
 将浏览器驱动放到指定文件夹
 将浏览器驱动所在目录添加到系统path环境变量中
 
 科普path:
 如果在cmd里面输入命令时提示不是内部命令,一般都是path这里没设置好
 说明:指定系统搜索的目录
 dos命令默认搜索顺序:1、检测是否为内部命令    2、检测是否为当前目录下可执行文件
 3、检测path环境变量指定的目录
 提示:
 1、如果 以上搜索目录都检测不到输入的命令或或执行文件,系统会抛出不是内部或外部命令。。。
 2、在web环境中,如果不将浏览器驱动添加到path中,selenium在运行的时候会提示浏览器驱动有误
 
 
 # 第一个selenium案例:
 # 需求:打开百度首页并停留3秒后关闭浏览器驱动
 #1、导包
 from selenium import webdriver
 from time import sleep
 #获取浏览器驱动
 driver=webdriver.Chrome()
 #打开url
 driver.get("http://baidu.com")
 #暂停3秒
 sleep(3)
 #关闭浏览器驱动
 driver.quit();
 
 
 五、元素定位
 工具:
 Firefox        Firebug(F12获取)
 谷歌    F12
 
 元素定位依赖于:1、标签名 2、属性 3、层级 4、路径
 定位方式:
 元素属性
 1、id
 2、name
 3、class_name
 标签
 4、tag_name
 a标签 (可点击文本)
 5、link_text(定位超连接 a标签)
 6、partial_link_text(同上,可模糊)
 
 7、XPath(基于元素路径)
 8、CSS(元素选择器)
 
 
 1、id定位
 element=driver.find_element_by_id(id)
 
 例子:
 from selenium import webdriver
 from time import sleep; #如果直接import time会把time的所有方法都导进来,不好
                         #快速导包:输入sleep后,按ctrl+alt+空格,然后选中想要的方法后回车,就直接导包成功了
 driver=webdriver.Firefox();
 #如果输入的是本地文件的url,例如E:\aaa\bbb\ccc\index.html
 #里面有\反斜杆,其在python中是转义字符。此时可以写成url=r"E:\aaa\bbb\ccc\index.html"  r是装饰的字符串,如果字符串中有转义字符,不进行转义使用
 #也可以不用r,直接写2个\\      E:\\aaa\\bbb\\ccc\\index.html
 #使用本地浏览器模式 前缀必须添加file:///
 #url="file:///E:/aaa/bbb/ccc/index.html"
 url='http://www.baidu.com'
 driver.get(url);
 driver.maximize_window();
 driver.find_element_by_class_name('engine-key-wrapper').send_keys('python');
 driver.find_element_by_id('search-submit').click();
 sleep(3)
 driver.quit();
 
 
 2、name定位
 element=driver.find_element_by_name(name)  #name是用引号括起来的
 
 3、class_name定位
 element=driver.find_element_by_class_name(classname)
 
 4、tag_name定位    标签即是元素  例如input  div a 等      很少用
 element=driver.find_element_by_tag_name(tag_name)
 
 如果找到多个同样的的标签,默认返回第一个
 
 5、link_text定位和partial_link_text定位        封装的时候一般使用后者
 都是定位a标签,但是partial_link_text可以模糊定位,即不用全部匹配,前者需要
 element=driver.find_element_by_link_text(text)        # 引号内部的文字如果不是全部匹配则会报错
 element=driver.find_element_by_partial_link_text('123').click();  #只有是包含123的字符的就可以click了。但是最好使用能代表唯一性的词,如果有多个,还是返回第一个
 
 
 7、XPath     XML Path
 
 element=driver.find_element_by_xpath(xpath)
 
 定位策略(方式)
 1)路径     看7.1路径定位
 2)利用元素属性     比如//input[@id='password']    这是相对路径定位时,可能会找到多个input,此时考虑结合属性来定位,需要[@]
 3)属性加上逻辑           比如//input[@class='login' and @name='user-test']         当使用一个属性class找到的还不是唯一时,需要使用and 来多加一个属性来找
 4)层级与属性结合       比如://p[@id='login-T2']/input[@name='user' and @type='text']    当使用2个属性都找不到唯一时,给它加上父层级路径       第一次要//,第二级就只要/
                                               一般使用子级的属性找不到唯一,就加上父级的属性,然后把子级的属性去掉:
                                               //p[@id='login-T2']/input
 
 7.1 路径定位(绝对 、相对路径)
 绝对路径:从最外层元素到指定元素之间所有经过元素层级的路径
 1)绝对路径以/html根节点开始,使用/来隔元素层级:
 /html/body/div/fieldset/p[1]/input
 2)绝对路径对页面结构要求比较严格,不建议使用
 
 相对路径:匹配任意层级的元素,不限制元素的位置
 1)相对路径以//开始
 2)格式://input       或者  //*
 
 7.2  XPath延申
 //*[text()='xxx']           文本内容是xxx的元素
 driver.find_element_by_xpath("//*[text()='开课/合作']").click();
 
 //*[contains(@attribute,'xxx')]      属性中含有xxx的元素        移动端全是这样子写,重要
 driver.find_element_by_xpath("//*[contains(@id,'keyword')]").send_keys('python');
 
 
 //*[starts-with(@attribute,'xxx')]      属性以xxx开头的元素
 driver.find_element_by_xpath("//*[starts-with(@id,'js_sea')]").click();
 
 
 
 8、CSS定位方法
 element=driver.find_element_by_css_selector(css_selector)
 8.1、常用策略
 1) id选择器        #id
 2)class选择器   .class
 3)元素选择器    input       直接写属性名称
 4)属性选择器    [name='passwordA']
 5)层级选择器   p>input      或者 p input          两者的区别:p一定是input的直接上级,后者则不一定是直接上级,不管中间隔多少级
                           p#idname>input       id名称为idname的p下面的input
 
 
 driver.find_element_by_css_selector('.header-index-text p').click();      层级与元素选择器、class选择器结合
 
 使用CSS 元素选择器 定位span(这个是任意标签名)标签获取文本值
 下面就是打印出class=mnav的标签的文本              
 ps=driver.find_element_by_css_selector('.mnav').text;
 print('文本值是:',ps)       #文本值是: 抗击肺炎
 
 
 8.2  CSS 延申
 input[type^='p']   type属性以p字母开头的元素
 input[type$='d']   type属性以d字母结束的元素
 input[type*='w']    type属性包含d字母的元素
 
 
 8.3 定位一组元素
 dirver.find_elements_by_id
 
 #以下是定位到一组以mnav为class名的元素列表,打印其长度为7
 elements=driver.find_elements_by_css_selector('.mnav')
 print(len(elements));
 #那么要对其中一个进行操作,需要以下标的方式
 elements[1].click();
 
 #通过遍历来操作,假设是文本框,全部输入同一个文字信息
 for el in elements:
     el.send_keys('我是同一个文字信息');
 
 
 8.4  find_element方法封装
  # 需求:使用driver_element方法来定位
  driver.find_element(By.ID,'kw').send_keys('python');
  driver.find_element(By.CSS_SELECTOR,'#su').click();
 
 By类:需要导包位置   输入By之后,按ctrl+alt+空格可以快速导包
  from selenium.webdriver.common.by import By
 
 六、元素操作方法
 1、目的:
 1)需要让脚本模拟用户级指定元素插入值
 2)模拟人为删除元素的内容
 3)模拟点击操作
 
 2、方法
 1) click()    单击元素
 2)send_keys(value)  模拟输入
 3)clear() 清除文本
 
 3、案例
 需求:输入文本后清空、修改再提交
 driver.find_element_by_css_selector('#kw').send_keys("python");
 sleep(3);
 driver.find_element_by_css_selector('#kw').clear();
 driver.find_element_by_css_selector('#kw').send_keys("java");
 sleep(3)
 driver.find_element_by_css_selector('#su').click();
 
 七、浏览器操作方法
 最大化  maximize_window()
 设置大小 set_window_size(width,height)
 窗口位置  set _window_position(x,y)
 后退   back()
 前进   forward()
 刷新 F5  refresh()      
 关闭当前窗口 close() 
 关闭由driver启动的所有窗口  quit()
 获取页面的title  title
 获取当前的url  current_url
 提示:最后两个,没有括号,应用场景:一般为判读上步操作是否成功
 
 实例:
 
 from selenium import webdriver;
 from time import sleep;
 driver=webdriver.Firefox();
 # driver._is_remote = False
 url='http://www.baidu.com';
 driver.get(url);
 #将浏览器最大化
 driver.maximize_window();
 sleep(5)
 # 设置固定大小300、200
 # driver.set_window_size(300,200);
 # sleep(2)
 # 移动浏览器窗口位置 x:320   y:150
 # driver.set_window_position(320,150)
 # sleep(2)
 
 # 最大化
 # driver.maximize_window();
 # 点击访问另一个访问,演示后退功能
 driver.find_element_by_name('tj_trnews').click();
 # driver.find_element_by_css_selector('').click();
 sleep(2)
 driver.back();
 sleep(2)
 # 然后演示前进功能
 #driver.forward();
 #sleep(3);
 driver.find_element_by_css_selector('#kw').send_keys('python');
 sleep(3);
 #刷新
 driver.refresh();
 #获取当前页面title
 title=driver.title;
 print('当前页面的title是:',title);
 sleep(3)
 #获取当前页面url
 current_url=driver.current_url;
 print('当前页面的url是:',current_url);
 sleep(3);
 driver.find_element_by_partial_link_text('关于百度').click();
 sleep(3);
 driver.close();
 sleep(3);
 driver.quit();
 
 
 八、获取元素信息
 1、元素的文本
 2、元素的属性值    作用:检测属性值的正确与否、  利用属性值判断是否我们要的元素 
 3、判断元素是否可见状态
 
 常用方法
 1、size  返回元素大小
 2、text  获取元素的文本          size和text是属性,调用时无括号:xxx.size
 3、get_attribute('xxx') 获取属性值,传递的参数为元素的属性名
 4、is_displayed()    判断元素是否可见
 5、is_enabled()    判断元素是否可用
 6、is_selected()     判断元素是否选中,用来检查复选框或单选按钮是否被选中
 
 
 实例:
 from selenium import webdriver;
 from time import sleep;
 driver=webdriver.Firefox();
 # driver._is_remote = False
 url='http://www.baidu.com';
 driver.get(url);
 #将浏览器最大化
 driver.maximize_window();
 #获取输入框的大小
 # 获取页面上某个超连接的文本内容
 # 获取页面上第一个超链接的地址
 # 判断页面中的某标签是否可见
 # 判断页面中的某按钮是否可用
 # 判断页面f中的复选框是否为选中的状态
 size=driver.find_element_by_css_selector('#kw').size;
 print('输入框的大小是:',size);
 texts=driver.find_element_by_css_selector('#virus-2020').text;
 print('页面中id为virus-2020的元素的文字为:',texts);
 att=driver.find_element_by_css_selector('a').get_attribute('text')
 print('页面中id是kw的属性是:',att);
 display=driver.find_element_by_css_selector('#su').is_displayed();
 print('搜索按钮是否可见:',display);#结果是True或者False
 enabled=driver.find_element_by_css_selector('#su').is_enabled();
 print('搜索按钮是否可用:',enabled);#结果是True或者False
 # selected=driver.find_element_by_css_selector('xxx').is_selected();
 # print('xxx是否被选中',selected);
 driver.quit();
 
 
 
 结果是:
 输入框的大小是: {'width': 548.0, 'height': 44.0}
 页面中id为virus-2020的元素的文字为: 抗击肺炎
 页面中id是kw的属性是: 百度首页
 搜索按钮是否可见: True
 搜索按钮是否可用: True
 
 九、键盘和鼠标操作
 鼠标:点击、右击、双击、悬停、拖拽
 1、鼠标操作的方法
 说明:在Selenium中将操作鼠标的方法封闭在ActionChains 类中
 实例化对象:action=ActionChains(driver)
 方法:
 1) context_click(element)  右击
 2)   double_click(element)   双击
 3)drag_and_drop(source,target) 拖动
 4)  move_to_element(element)  悬停
 5)  perform()   执行                 ------>此方法用来执行以上所有鼠标操作
 
 提示:selenium框架中虽然提供了右击鼠标的方法,但是没有提供选择右击菜单方法,可以通过发送快捷键的方式解决(但是chrome浏览器不支持这种方法)
 
 
 实例:
 from selenium import webdriver;
 from time import sleep;
 
 from selenium.webdriver import ActionChains
 
 driver=webdriver.Chrome();
 # driver._is_remote = False
 url='http://www.baidu.com';
 driver.get(url);
 #将浏览器最大化
 driver.maximize_window();
 #首先实例化并获取ActionChains类           先输入ActionChains然后按ctrl+alt+空格键导包from selenium.webdriver import ActionChains
 action=ActionChains(driver);
 #在文本框里右击,  预期:出现“粘贴”功能
 # action.context_click(driver.find_element_by_css_selector('#kw')).perform();
 # sleep(5)
 
 
 #先手动复制一个文本,然后使用发送右键快捷键的方式粘贴一个内容
 serchtext=driver.find_element_by_css_selector('#kw');
 action.context_click(serchtext).perform();
 serchtext.send_keys('p');#p是右键菜单粘粘的快键键
 #在文本框里输入文字后双击   预期:全选中
 ele=driver.find_element_by_css_selector('#kw');
 action.context_click(ele).perform();
 #移动到超连接后悬停     预期:超连接文字变色
 action.move_to_element(driver.find_element_by_css_selector('#virus-2020')).perform();
 sleep(5);
 action.double_click(driver.find_element_by_css_selector('#kw').send_keys('python')).perform();
 sleep(5)
 driver.find_element_by_css_selector('#virus-2020').click();
 sleep(5)
 
 #拖拽
 sourse=driver.find_element_by_css_selector('#div1');#源元素;假设是一个红色盒子
 target=driver.find_element_by_css_selector('#div2');#目标元素
 action.drag_and_drop(sourse,target).perform();
 #拖拽扩展
 action.drag_and_drop_by_offset(sourse,xoffset=360,yoffset=180);#拖动往x轴360个像素,y轴180个像素
 
 driver.quit();
 
 
 2、键盘操作
 #Selenium中把键盘按钮都封装在keys类中
 
 1)删除键(BackSpace)      send_keys(Keys.BACK_SPACE)
 2)空格键(Space)    send_keys(Keys.SPACE)
 3)制表键(Tab)        send_keys(Keys.TAB)
 4)回退键(Esc)        send_keys(Keys.ESCAPE)
 5)  回车键(Enter)        send_keys(Keys.ENTER)
 6)全选(Ctrl+A)        send_keys(Keys.CONTROL,'a')
 7)复制(Ctrl+C)        send_keys(Keys.CONTROL,'c')
 
 实例:
 from selenium import webdriver;
 from time import sleep;
 from selenium.webdriver.common.keys import Keys;
 driver=webdriver.Chrome();
 url='http://www.baidu.com'
 driver.get(url);
 """
 需求
 输入框里输入python1
 删除1
 全选 复制 python
 粘贴
 """
 serchtext=driver.find_element_by_css_selector('#kw');
 serchtext.send_keys('python1');
 sleep(3);
 serchtext.send_keys(Keys.BACK_SPACE);
 sleep(3)
 serchtext.send_keys(Keys.CONTROL,'a');
 serchtext.send_keys(Keys.CONTROL,'c')
 sleep(3)
 serchtext.send_keys(Keys.CONTROL,'v')
 sleep(3)
 driver.find_element_by_css_selector('#su').click();
 sleep(5)
 driver.quit();
 
 十一、元素等待
 由于电脑配置或网络原因,在查找元素时,元素代码未在第一时间内被 加载出来,而抛出未找到元素异常
 概念:元素在第一次未找到时,元素等待设置的时长被激活,如果在设置的有效时长内找到元素,继续执行代码,如果超出设置的时间未找到元素,抛出未找到元素异常
 分类:
 隐式等待:定位元素时,如果能定位到元素则直接返回元素,不触发等待;如果不能定位到该元素,则间隔一段时间后再去定位元素;如果在达到最大时长还没找到指定元素,则抛出不存在的异常 NoSuchElementException
 现实方式:
 driver.implicitly_wait(timeout)            单位是秒。实际工作中一般设置为30秒,但是如果是新浪网实种网页,直接设置80秒
 实例:
 from selenium import webdriver;
 from time import sleep;
 from selenium.webdriver.common.keys import Keys;
 driver=webdriver.Chrome();
 #设置隐式等待10秒。
 # 特色:1、设置在全局中,针对所有元素有效 2、一般情况下为前置必写代码(1)获取浏览器驱动对象;2)最大化浏览器;3)设置等待)
 driver.implicitly_wait(10);
 url='http://www.baidu.com'
 driver.get(url);
 """
 需求
 隐式等待使用
 给一个错误的Id,不能知道,如果直接抛出异常,说明等待失败,如果在设置的时间外抛出异常则说明等待设置成功
 """
 serchtext=driver.find_element_by_css_selector('#kw');
 serchtext.send_keys('python1');
 sleep(3);
 driver.find_element_by_css_selector('#su').click();
 sleep(5)
 driver.quit();
 
 
 显式等待:
 定位元素时,如果能定位到元素则直接返回元素,不触发等待;如果不能定位到该元素,则间隔一段时间后再去定位元素;如果在达到最大时长还没找到指定元素,则抛出异常 TimeoutException
 在Selenium中把显式等待的相关方法封装在WebDriverWait 类中
 
 现实方式:
 1、导包  等待类
 from selenium.webdriver.support.wait import WebDriverWait;
 2、实例化
 WebDriverWait(driver,timeout=30,poll_frequency=0.5)
 1)driver:浏览器驱动对象
 2)timeout:超时时长,单位:秒
 3)poll_frequency:检测间隔时间:默认为0.5秒,可以改为任意秒
 3、调用方法
 until(method):直到。。。时
 1)method:函数名称,该函数用来实现对元素的定位
 2)一般使用匿名函数来实现:lanbda x:x.find_element_by_id('kw')
 4、element=WebDriverWait(driver,10,1).until(lambda x:x.find_element_by_id('kw'))
 #x:x为driver,它是webDriverWait类将传入的driver赋值给类self._driver,until方法调用了self._driver;
 提示:WebDriverWait(driver,timeout=30,poll_frequency=0.5).until(lambda x:x.find_element_by_id('kws'))返回一个元素
 实例:
 from selenium import webdriver;
 from time import sleep;
 from selenium.webdriver.common.keys import Keys;
 driver=webdriver.Chrome();
 #导显式等待的包
 from selenium.webdriver.support.wait import WebDriverWait;
 url='http://www.baidu.com'
 driver.get(url);
 #实例化WebDriverWait()并调用
 #注意:调用until方法返回的一定是一个元素
 el=WebDriverWait(driver,timeout=30,poll_frequency=0.5).until(lambda x:x.find_element_by_id('kws'));
 el.send_keys('python');#注意:此时el还不是元素,只有代码运行起来的时候才是元素,所以写el.不会弹出send_keys();
 sleep(3)
 """
 需求
 显式等待使用
 给一个错误的Id,不能知道,如果直接抛出异常,说明等待失败,如果在设置的时间外抛出异常则说明等待设置成功
 """
 
 driver.find_element_by_css_selector('#su').click();
 sleep(5)
 driver.quit();
 
 显式等待和隐式等待的区别:
 1、显式等待:针对单个元素生效
 2、隐式等待:针对全局元素生效
 
 十二、扩展:上传文件
 
 from selenium import webdriver;
 from time import sleep;
 from selenium.webdriver.common.keys import Keys;
 driver=webdriver.Chrome();
 url='http://www.baidu.com'
 driver.get(url);
 """
 需求:点击上传文件按钮,可以选择文件上传的
 错误思路:
 假设【浏览】按钮的name是upfile
 driver.find_element_by_css_selector("[name='upfile']").click()
 这种思路,点开浏览按钮后弹出的窗口不是浏览器了,无法操作上传文件
 """
 #正确的实现方式:使用send_keys('文件路径及文件名')
 driver.find_element_by_css_selector("[name='upfile']").send_keys('D:\hello123.txt');#这样子就能上传成功了
 sleep(5)
 driver.quit();
 
 
 
 十三、使用CSS定位下拉框  select标签   option 
 实例:
 方式一:
 from selenium import webdriver;
 from time import sleep;
 driver=webdriver.Chrome();
 url='https://mail.163.com/register/index.htm?from=163mail';
 driver.get(url);
 driver.maximize_window();
 driver.implicitly_wait(30);
 """
 需求:163邮箱注册页面,下拉选择框默认选中163.com
 暂停2秒
 1、定位 126.com 然后暂停2秒
 2、定位yeah.net
 """
 driver.find_element_by_css_selector('#username').send_keys('wushuirong_123');
 driver.find_element_by_css_selector('.domain').click();
 sleep(2);
 driver.find_element_by_css_selector("[data-value='126.com']").click();
 sleep(2);
 driver.find_element_by_css_selector('.domain').click();
 driver.find_element_by_css_selector("[data-value='yeah.net']").click();
 sleep(2)
 driver.quit();
 
 方式二、使用Select方法
 步骤:
 1、导包 Select类  from selenium.webdriver.support.select import Select;
 2、获取Select对象
      匿名:Select(element).select_by_index()   #通过下标
      实名:select=Select(element)
              调用:select.select_by_index();
 注意:
 1、Select类是通过select标签来控制其下的option元素
 2、element:只能是select标签
 3、实例化select时,需要的参数为select标签元素
 4、调用Select类下面的方法,是通过索引、value值或显示文本去控制,而不需要click()事件。
 
 element:标签对应的元素,通过元素定位方式获取,例如:
 driver.find_element_by_id('selectA')
 
 操作方式:
 1、select_by_index(index)              根据option索引来定位,从0开始
 2、select_by_value(value)            根据option属性value值定位
 3、select_by_visible_text(text)       根据option显示文本来定位
 
 实例:
 from selenium import webdriver;
 from time import sleep;
 from selenium.webdriver.support.select import Select;
 driver=webdriver.Chrome();
 url='暂时没有实例连接';
 driver.get(url);
 driver.maximize_window();
 driver.implicitly_wait(30);
 """
 假设代码是这样子的:
 
    北京
    上海
    广州
    重庆
 
 需求是:
 暂停2秒
 1、定位 上海
 2、暂停2秒
 3、定位广州
 """
 sleep(2)
 #通过下标形式访问
 Select(driver.find_element_by_css_selector('#selectA')).select_by_index(1);
 sleep(2)
 Select(driver.find_element_by_css_selector('#selectA')).select_by_index(2);
 sleep(2)
 #通过value值形式访问
 Select(driver.find_element_by_css_selector('#selectA')).select_by_value('sh');
 sleep(2)
 Select(driver.find_element_by_css_selector('#selectA')).select_by_value('gz');
 #通过文本值形式访问
 Select(driver.find_element_by_css_selector('#selectA')).select_by_visible_text('上海');
 sleep(2)
 Select(driver.find_element_by_css_selector('#selectA')).select_by_visible_text('广州');
 driver.quit();
 
 十四、弹出框
 1、alert      警告
 2、confirm  确认
 3、prompt  提示
 
 弹出框如果不关闭会使得后面的操作不生效
 处理方法:Selenium中对处理弹出框的操作,有专用的处理方法,并且处理的方法是同一个,即alert\confirm\prompt均使用以下方法
 
 1、获取弹出框对象:
 alert=driver.switch_to.alert
 2、调用
 alert.text       #返回alert/confirm/prompt中的文字信息
 alert.accept()   #接受对话框选选
 alert.dismiss()  # 取消圣诞框选项
 
 提示:无论以上哪个对话框,都可以使用取消,同意,因为调用的是后台的事件,跟页面显示的按钮数量无关。
 注意:
 1、driver.switch.to.alert   方法适合以上三种对话框,调用时没有括号。
 2、获取文本的方法,调用时没有括号,如alert.text
 3、在项目中不是所有的小窗口都是以上三种对话框
 实例:
 from selenium import webdriver;
 from time import sleep;
 driver=webdriver.Chrome();
 url='F:/python/hmtlfile/alert.html';
 driver.get(url);
 driver.maximize_window();
 """
 需求:
 1、点击alert按钮
 2、关闭警告框
 3、输入用户名admin
 """
 # driver.find_element_by_css_selector("[value='alert警告框']").click();
 #driver.find_element_by_css_selector("[value='confirm确认框']").click();
 driver.find_element_by_css_selector("[value='prompt提示框']").click();
 sleep(3)
 #必须切换到弹出框并将其关闭
 alert=driver.switch_to.alert;
 print('警告框文本是:',alert.text);  
 alert.accept();
 #alert.dismiss();  #取消
 alert.
 driver.find_element_by_css_selector('.user').send_keys('admin');
 sleep(3);
 driver.quit();
 
 十五、滚动条操作
 1、在HTML页面中,由于前端技术框架的原因,页面元素为动态显示,元素根据滚动条的下拉而被加载
 2、页面注册同意条款,需要滚动条到最底层,才能点击同意
 
 实现方式:
 selenium中并没有直接提供操作滚动条的方法,但是可以通过js脚本来达到,例如:
 1、设置JavaScript脚本控制滚动条
 js='window.scrollTo(0,1000)'; 
 2、然后设用js方法
 driver.execute_script(js);
 
 实例:
 from selenium import webdriver;
 from time import sleep;
 driver=webdriver.Chrome();
 url='F:/python/hmtlfile/alert.html';
 driver.get(url);
 driver.maximize_window();
 """
 需求:
 1、打开网页,停留2秒
 2、拉到最底下
 """
 js='window.scrollTo(0,1000)';   #这个0是左边距,即水平滚动条,1000是上边距,即垂直滚动条,一般设到1000像素都会拉到最底下了,如果页面特别长则需要设得大一点,10000都行
 driver.execute_script(js);
 sleep(3)
 driver.quit();
 
 
 十六、frame切换、多窗口切换(特别重要,每个项目都要用到)
 1、frame切换
 frame:HTML页面中的一种框架,主要作用是在当前页面中指定区域显示另一个页面的元素:
 形式一:[了解]
 
     
      
 
 形式二:
 
 
 2、切换方法
 1)drivier.switch_to.frame(frame_reference)            ---->切换到指定frame的方法
     frame_reference:   可以为frame框架的name、id或者定位到frame元素
 2)driver.switch_to.default_content();    -->切换回默认页面
 
 在frame中操作其他页面,必须先回到默认页面,才能进行下一步操作
 
 实例:
 from selenium import webdriver;
 from time import sleep;
 driver=webdriver.Chrome();
 url='F:/python/hmtlfile/iframe.html';
 driver.get(url);
 driver.maximize_window();
 """
 需求:
 1、在主页面输入用户名
 2、在frame1页面中输入用户名
 3、在frame2页面中输入用户名
 """
 #在主页面输入用户名
 driver.find_element_by_css_selector(".names").send_keys('admin');
 sleep(2)
 #切换到第一个frame并输入用户名
 driver.switch_to.frame('frame1');          #这个参数可以是name\id,也可以是iframe元素,例如:
 #driver.switch_to.frame(driver.find_element_by_css_selector("[name='frame2']"));
 driver.find_element_by_css_selector('.user').send_keys('我是frame1');
 sleep(2)
 #需要返回主页面才能切换到另一个frame
 driver.switch_to.default_content();
 #切换到第二个frame
 driver.switch_to.frame('frame2');
 driver.find_element_by_css_selector('#username').send_keys('我是frame2');
 sleep(3)
 driver.quit();
 
 2、多窗口切换
 在Selenium中封装了获取当前窗口句柄、获取所有窗口句柄和切换到指定句柄窗口的方法:
 句柄:handle,窗口的唯一识别码
 方法:
 1)driver.current_window_handle   --->获取当前窗口句柄
 2)driver.window_handles        --->获取 所有窗口句柄
 3)driver.switch_to.window(handle)  --->切换指定句柄窗口
 
 步骤:
 1、获取当前窗口句柄
 2、点击连接,启动另一个窗口
 3、获取当前所有窗口句
 4、遍历所有窗口句柄
         5、判断当前遍历的窗口句柄不等于当前窗口句柄
         6、切换
 7、返回主窗口
 8、再次进入别的窗口
 
 实例
 from selenium import webdriver;
 from time import sleep;
 driver=webdriver.Chrome();
 url='F:/python/hmtlfile/windows.html';
 driver.get(url);
 driver.maximize_window();
 """
 需求:
 1、在主页面输入用户名
 2、在新窗口页面1中输入用户名
 3、在新窗口页面2中输入用户名
 """
 #获取当前窗口句柄     --->目的:判断只要不是当前主窗口句柄,就一定是新开的窗口句柄
 curren_handle=driver.current_window_handle;
 #在主页面输入用户名
 driver.find_element_by_css_selector(".names").send_keys('admin');
 sleep(2)
 driver.find_element_by_partial_link_text('弹窗页面').click();
 #获取所有窗口句柄     这句话的位置不能变
 handles=driver.window_handles;
 #判断  不是 当前窗口句柄
 for h in handles:   #  h是随便起的变量,表示句柄,该句柄如果包含在所有句柄(handles)中
     if h !=curren_handle:   #如果不是当前句柄,则:
         #切换
         driver.switch_to.window(h);
 
 #切换到第二个窗口并输入用户名
 driver.find_element_by_css_selector('.user').send_keys('我是frame1');
 sleep(3)
 #driver.close();
 driver.switch_to.window(curren_handle);
 driver.find_element_by_css_selector(".names").send_keys('admin1');
 driver.find_element_by_partial_link_text('登录注册页面').click();
 handles=driver.window_handles;
 for h in handles:   #  h是随便起的变量,表示句柄,该句柄如果包含在所有句柄(handles)中
     if h !=curren_handle:   #如果不是当前句柄,则:
         #切换
         driver.switch_to.window(h);
 
 driver.find_element_by_css_selector('#username').send_keys('我是第三个窗口的')
 sleep(3)
 driver.quit();
 
 
 
 切换窗口的方法函数封装起来,要用的时候调用的版本:
 
 from selenium import webdriver;
 from time import sleep;
 driver=webdriver.Chrome();
 url='F:/python/hmtlfile/windows.html';
 driver.get(url);
 driver.maximize_window();
 """
 需求:
 1、在主页面输入用户名
 2、在新窗口页面1中输入用户名
 3、在新窗口页面2中输入用户名
 """
 #获取当前窗口句柄     --->目的:判断只要不是当前主窗口句柄,就一定是新开的窗口句柄
 curren_handle = driver.current_window_handle;
 def handless():
     handles = driver.window_handles; #获取所有窗口句柄     这句话的位置不能变
     # 判断  不是 当前窗口句柄
     for h in handles:  # h是随便起的变量,表示句柄,该句柄如果包含在所有句柄(handles)中
         if h != curren_handle:  # 如果不是当前句柄,则:
             # 切换
             driver.switch_to.window(h);
 
 #在主页面输入用户名
 driver.find_element_by_css_selector(".names").send_keys('admin');
 sleep(2)
 driver.find_element_by_partial_link_text('弹窗页面').click();
 handless();
 
 #切换到第二个窗口并输入用户名
 driver.find_element_by_css_selector('.user').send_keys('我是frame1');
 sleep(3)
 #切回主窗口
 driver.switch_to.window(curren_handle);
 driver.find_element_by_css_selector(".names").send_keys('admin1');
 driver.find_element_by_partial_link_text('登录注册页面').click();
 #再次调用函数
 handless();
 driver.find_element_by_css_selector('#username').send_keys('我是第三个窗口的')
 sleep(3)
 driver.quit();
 
 
 十七、  截屏
 如果自动化测试脚本运行时出现了异常,选择截屏保存当时的信息
 因为自动化脚本是由程序去执行的,因此有时候打印的错误信息并不十分明确,如果在执行出错的时候
 对当前窗口截图保存,那么通过图片就可以非常直观地看到出错的原因
 
 方法:
 driver.get_screenshot_as_file(imgpath)
           imgpath:图片保存路径
 
 实例:
 import time;
 from selenium import webdriver;
 from time import sleep;
 
 driver=webdriver.Chrome();
 url='F:/python/hmtlfile/windows.html';
 driver.get(url);
 driver.maximize_window();
 """
 需求:
 1、在页面输入用户名
 2、截屏保存在当前路径下(扩展:保存到上一级的image目录下、使用时间戳来动态保存图片)
 """
 driver.find_element_by_css_selector('.names').send_keys('admin');
 #driver.get_screenshot_as_file('./jiepin.png');
 #driver.get_screenshot_as_file('../image/jiepin.png');
 driver.get_screenshot_as_file('../image/%s.png'%(time.strftime('%Y_%m_%d %H_%M_%S')));#想要使用time,要先导入time包。ctrl+alt+空格导包。
 #strftime是将时间转为字符串
 sleep(3);
 driver.quit();
 
 
 十八、验证码处理
 验证码是一种随机生成的信息(数字、字母、汉字、图片、算术题等)为了防止恶意的请求行为,增加应用的安全性
 
 处理方式:Selenium中并没有对验证码处理的方法,只能根据项目情况使用以下方式
 1、去掉验证码(仅能够在测试环境下使用)
 2、设置万能验证码(生产环境和测试环境使用):例如在代码中加入生成的验证码后面加一句“or  888888"之类的,设置一下复杂一点的验证码并且在脚本中提交的一直是这个验证码
 3、验证码识别技术(通过python-tesseract来识别图片类型验证码,但成功率很低)
 4、记录cookie(通过记录cookie进行跳过登录 :这是最靠谱的一种办法,项目中建议使用这种方法
 
 十九、Cookie
 1、说明:
 Cookie是由Web服务器生成,并且保存在用户浏览器上的小文本文件,它可以包含用户相关的信息。
 数据格式:键值对组成
 产生:客户端请求服务器,如果服务器需要记录该用户状态,就向客户端浏览器颁发一个Cookie数据
 使用:当浏览器再次请求该网站时,浏览器把请求的数据和Cookie数据一同提交给服务器,服务器检查 该
 Cookie,以此来辨认用户状态
 
 2、应用场景:
 
 
 3、Selenium操作cookie
 1)get_cookie(name)           --->获取指定cookie        括号里的name是cookie的名称
 2)get_cookies()                  ---->获取本网站所有本地cookies
 3)add_cookie(cookie_dict)   ---->添加cookie       cookie_dict:一个字典对象,必选的键包括:“name" and "value"
 
 3.1 案例:
 需求:使用cookie实现跳过登录 
 1)手动登录百度,获取cookie
 2)使用获取到的cookie,达到登录目的,然后就可以执行登录之后的操作
 
 import time;
 from selenium import webdriver;
 from time import sleep;
 driver=webdriver.Chrome();
 driver.maximize_window();
 url='https://www.baidu.com';
 driver.get(url);
 driver.add_cookie({'name':'BDUSS','value':'g1a2J2N35RNENxakRQQnpiY3N1WlExNXk3WWtnNFNTcDlLSHNyYXVneTZWTnhlRVFBQUFBJCQAAAAAAAAAAAEAAACwstAKc2h1aXFpbmdmdQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALrHtF66x7ReUW'})
 #上面这个value值,是去firefox浏览器复制方便一点,步骤:1、打开百度,点击登录,在输入用户名密码后先清空所有cookie,然后点击【登录】按钮,然后
 # 在“网络--->域名为www.baidu.com,类型为html的person文件里,找到Cookie名字为BDUSS后面的一长串内容,复制过来就行了
 """
 需求:
 使用cookie绕过百度登录
 步骤:
 1、手动登录百度网站
 2、手动获取登录后的cookies 'BDUSS'
 3、使用selenium内的add_cookie({'name':'BDUSS','value':'根据实际填写'})           #这个BDUSS是百度的,其他网站的不是这样
 """
 sleep(3);
 #需要刷新才能看到效果
 driver.refresh();
 sleep(5);
 driver.quit();
 
 
 扩展:
 可以先使用这种方法来获取上面那一长串的value
 cookie=driver.get_cookie('BDUSS');
 print('cookie:',cookie)
 
 
 另外:获取所有的cookie信息:
 cookies=driver.get_cookies()
 
 
 
 二十、UnitTest框架
 framework:框架、为解决一类事情的功能集合
 UnitTest框架:是python自带的一个单元测试框架,用它来做单元测试。
 为什么使用UnitTest框架:
 1、能够组织多个用例去执行
 2、提供丰富的断言方法
 3、能够生成测试报告
 
 UnitTest核心要素
 1、TestCase       测试用例
 2、TestSuite    套件(批量用例)
 3、TestRunner    运行(以文本形式 text TestRunner)
 4、TestLoader    运行
 5、Fixture    (工厂装置)两个函数,前置、后置必运行函数
 
 1、TestCase  测试用例详解:
 
 案例:定义一个实现加法操作的函数,并对该函数进行测试
 
 定义测试用例:
 1)、导包:import unittest
 2)、定义测试类:新建测试类必须继承unittest.TestCase
 3)、定义测试方法:测试方法名称必须以test开头
 
 执行测试用例:
 方式一:使用pycharm在代码上点击鼠标右键,选择使用UnitTest运行
 方式二:调用unittest.main()来运行
 
 实例:
 import unittest;
 """
 需求:
 定义一个实现加法\减法操作的函数,并对该函数进行测试
 步骤:
 1)、导包:import unittest
 2)、定义测试类:新建测试类必须继承unittest.TestCase
 3)、定义测试方法:测试方法名称必须以test开头
 """
 #编写求和函数
 def add(x,y):
     return x+y;
 #编写减法函数
 def sub(x,y):
     return x-y;
 
 #定义测试类并继承
 class Test01(unittest.TestCase):
     #定义测试方法  注意:以test字母开头
     def test_add(self):
         #调用 要用的函数
         result=add(1,1)
         print('结果为:',result);
     #定义第二个测试方法:
     def test_sub(self):           #假设光标定在这里或上面的test_add方法后面,运行unittest,运行结果 那里是显示Test passed:1。
                                     # 如果想运行多个用例,则需要把光标定在classrg dm
         result=sub(10,5)
         print('结果为:',result);
 
 运行结果为:
 Ran 2 tests in 0.003s
 
 OK
 结果为: 2
 结果为: 5
 
 
 2、TestSuite  测试套件:
 说明:多条用例集合在一起,就是一个TestSuite
 使用:
 1、实例化:suite=unittest.TestSuite()            #suite为TestSuite实例化的名称
 2、添加用例:suite.addTest(ClassName("MethodName"))         #添加的用例是另外一个py文件写好的UniteTest文件里的类名和方法名
 3、添加扩展:suite.addTest(unittest.makeSuite(ClassName))    #搜索指定ClassName内以test开头的方法并添加到测试套件中
 
 实例:
 """
 目标:UnitTest框架      ---TestSuite使用
 
 步骤:
 1)、导包:import unittest
 2)、实例化获取 TestSuite对象
 3)、调用addTest() 方法添加用例到指定套件中
 """
 #导包:import unittest
 import unittest;
 from UnitTest.UnitTest_01 import Test01;
 #实例化获取 TestSuite对象
 suite=unittest.TestSuite()
 #调用addTest 方法添加用例到指定套件中
 #写法1:类名(”方法名“)
 suite.addTest(Test01("test_add"));
 suite.addTest(Test01("test_sub"));
 
 #扩展  写法2 添加测试类中所有test开头的方法
 suite.addTest(unittest.makeSuite(Test01));
 
 #执行测试套件
 runner=unittest.TextTestRunner();
 runner.run(suite);
 
 
 3、TextTestRunner是用来执行测试用例和测试套件的          执行后会有文本显示通过或没通过
 使用:
 1、先导包
 2、实例化:runner=unittest.TextTestRunner();
 3、执行:runner.run(suite)   #suite为测试套件名称
 
 4、TestLoader 类
 说明:用来加载TestCase到TestSuite中,即加载满足条件的油荒用例(默认是以test开头的方法,这里可以按实际情况设置以什么开头),并
           把测试用例封装成测试套件。
           使用unittest.TestLoader,通过该类下面的discover()方法自动搜索指定目录下指定开头的.py文件, 
           并将查找到的测试用例组装到测试套件;
 用法:
          suite=unittest.TestLoader().discover(test_dir,pattern='test*.py')
                    test_dir :指定的测试用例目录
                    pattern:为查找的.py文件的格式,默认为'test*.py'
        也可以使用unittest.defaultTestLoader 代替 unittest.TestLoader();
 运行:
 
 
 实例:
 假设在UnitTest文件夹写UnitTest_Run_testloader.py文件来运行case文件夹下面的用例们,执行其中所有以test开头的.py文件里的用例
 """
 目标:使用TestLoader()类
 作用:搜索指定目录下指定开头的.py文件,在py文件中搜索test开头的测试方法,并将这些方法添加到测试套件中
 需求:运行case目录下test01.py至test05.py文件
 
 步骤:
 1)、导包:import unittest
 2)、调用方法
 3)、执行 套件
 """
 #导包:import unittest
 import unittest;
 #调用方法
 #写法一
 suite=unittest.TestLoader().discover("../case",pattern='test*.py');#如果不需要指定文件以什么开头,执行case目录下所有模块,则不需要pattern参数
 #写法二、扩展,使用defaultTestLoader,不需要括号,推荐使用:
 #suite=unittest.defaultTestLoader.discover('../case');         #直接调用case目录下所有模块文件
 #执行测试套件
 unittest.TextTestRunner().run(suite);
 
 
 以上实例,假设case目录下的文件列表是这样子的:
 test01.py
 test02.py
 test03.py
 test04.py
 test05.py
 aaaaa.py
 
 则根据pattern='test*.py'来筛选,以a开头的最后一个文件是不会被添加到测试套件中的,仅执行前5个文件,如果不加pattern参数,则第六个也执行
 
 TestLoader与TestSuite的区别(它们都是测试套件,只是实现方式不同)
 1、TestSuite需要手动添加测试用例(可以添加测试类,也可以添加测试类中的某个测试方法)
 2、TestLoader是搜索指定目录下的指定开头的.py文件,并添加测试类中的所有以test开头的测试方法到测试套件中,不能指定添加测试方法;
 
 工作中建议使用TestLoader
 
 5、Fixture:是一个概述,对一个测试用例环境的初始化和销毁就是一个Fixture
 控制级别:
 1)函数级别(实例1)       setUp(self)  每一个方法之前都运行一次,执函数执行之后就会执行一次tearDown()
 2)类级别(实例2)       setUpClass 只在类之前运行一次,比如执行十个用例,只打开一次浏览器和最大化浏览器和设置隐式等待,如果用函数级别的就会特别烦
             注意:类方法必须使用@classmethod修饰
 3)模块级别(仅作了解,项目中很少用到):setUpModule()\tearDownModule()
 
 提示:无论使用函数级别还是类级别,最常用的场景就是:
           初始化:
     1、获取浏览器实例化对象
     2、最大化浏览器
     3、设置隐式等待
           结束:
     关闭浏览器驱动
 
 实例1:
 """
 目标:unittest 中的fixture用法
     fixture其实就是两个函数,这2个函数可以一起使用也可以单独使用
         1、初始化函数:def setUp()
         2、结束函数:def tearDown()
 
 需求:每个方法之前都要运行一次setUp,之后都要运行一次tearDown
 """
 #导包:import unittest
 import unittest;
 class Test03(unittest.TestCase):    #需要继承,写在括号里
     def setUp(self):
         print("setUp被执行");
     def tearDown(self):
         print("tearDown被执行")
     def test01(self):
         print("test01被执行")
     def test02(self):
         print("test02被执行")
 
 结果是:
 setUp被执行
 test01被执行
 tearDown被执行
 setUp被执行
 test02被执行
 tearDown被执行
 
 
 实例2、
 """
 目标:unittest 中的fixture用法
     fixture其实就是两个函数,这2个函数可以一起使用也可以单独使用
         1、初始化函数:def setUp()
         2、结束函数:def tearDown()
 
 需求:每个方法之前都要运行一次setUp,之后都要运行一次tearDown
 """
 #导包:import unittest
 import unittest;
 class Test03(unittest.TestCase):    #需要继承,写在括号里
     @classmethod
     def setUpClass(cls):          #执行类方法,需要使用@classmethod来修饰,否则报错
         print("setUpClass被执行");
     @classmethod
     def tearDownClass(cls):
         print("tearDownClass被执行");
     def setUp(self):
         print("setUp被执行");
     def tearDown(self):
         print("tearDown被执行")
     def test01(self):
         print("test01被执行")
     def test02(self):
         print("test02被执行")
 
 结果是:
 setUpClass被执行
 setUp被执行
 test01被执行
 tearDown被执行
 setUp被执行
 test02被执行
 tearDown被执行
 tearDownClass被执行
 
 
 二十一、断言     assert
 让程序代替人为判断测试程序执行结果 是否符合预期结果的过程
 
 自动化脚本在执行的时候一般是无人值守状态,我们不知道执行结果是否符合预期结果,所以我们需要让程序
 代替人为检测程序执行的结果是否符合预期结果。这就需要使用断言。
 
 UnitTest断言方法:(常用的)
 1、assertTrue(expr,msg=None)    验证expr是true,如果是false,则fail
 2、assertFalse(expr,msg=None)    验证expr是false,如果是true,则fail    
 3、assertEqual(expected,actual,msg=None)    验证expected==actual,不等则fail(比较重要,需要掌握)
 4、assertNotEqual(first,second,msg=None)    验证first!=second,相等则fail
 5、assertIsNone(obj,msg=None)    验证obj是None,不是则fail
 6、assertIsNotNone(obj,msg=None)    验证obj不是None,是则fail
 7、assertIn(member,container,msg=None)    验证是否member in container(需要掌握)
 8、assertNotIn(member,container,msg=None)    验证是否member not in container
 
 实例:
 """
 目标:unittest 中的常用断言
     1、assertTrue:如果结果为True通过,否则失败
 
 """
 import unittest;
 def add(x,y):
     return x+y;
 
 class Test03(unittest.TestCase):    #需要继承,写在括号里
     def test01(self):
         result=add(4,4);
         flat=result>=8;
         self.assertTrue(flat);
         self.assertEqual("wq","wq");#应用场景,登录成功后,获取用户名称看是否等于预期的名称
         #判断后面的字符串是否包含前面的字符串
         self.assertIn('hello','helloword');
         #判断是否为None
         flag=None;
         self.assertIsNone(flag);
 
 
 实例2:
 """
 需求:登录百度
     1、输入:输入正确的用户名和不输入密码就点击登录
     2、断言:提示信息为“请您输入密码”
     3、要求:如果断言出错,截屏保存
 
 """
 import unittest;
 from selenium import webdriver;
 from time import sleep;
 import time;
 
 
 #定义测试类 并继承 unittest.TestCase
 class TestBaiduLogin(unittest.TestCase):
     #定义初始化方法
     def setUp(self):
         #获取浏览器驱动并打开url并最大化浏览器和设置隐式等待
         self.driver=webdriver.Chrome();
         url="https://baidu.com"
         self.driver.get(url);
         self.driver.maximize_window();
         self.driver.implicitly_wait(30);
     #定义结束方法
     def tearDown(self):
         sleep(3);
         self.driver.quit();
     #关闭浏览器驱动
     #定义登录测试方法  密码输入错误
     def test_login_error_psw(self):
         driver=self.driver;
         # 点击登录连接
         driver.find_element_by_css_selector(".s-top-login-btn").click();
         driver.find_element_by_css_selector('.tang-pass-footerBarULogin').click();
 
 
         #输入用户名
         driver.find_element_by_css_selector('.pass-text-input-userName').send_keys('shuiqingfu')
         #输入密码
         driver.find_element_by_css_selector('.pass-text-input-password').send_keys('');
         #点击登录
         driver.find_element_by_css_selector('.pass-button-submit').click();
         sleep(3)
         #获取登录后提示信息
         result=driver.find_element_by_css_selector(".pass-generalError-error").text;
         print('提示信息:',result);
         #定义预期结果
         expect_result='请您输入密码2';
         try:
             #断言
             self.assertEqual(result,expect_result)
         except AssertionError:
             # 截图   使用时间戳来动态生成图片名称,不会重复
             driver.get_screenshot_as_file("../image/{}.png".format(time.strftime("%Y_%m_%d %H_%M_%S")));
         #抛出异常
         raise
 
 运行结果:
 Tests failef :1
 
 提示信息: 请您输入密码
 
 
 Ran 1 test in 22.700s
 
 FAILED (errors=1)
 
 Error
 Traceback (most recent call last):
   File "C:\Program Files\JetBrains\PyCharm Community Edition 2019.3.4\plugins\python-ce\helpers\pycharm\teamcity\diff_tools.py", line 32, in _patched_equals
     old(self, first, second, msg)
   File "C:\Users\wushuirong\AppData\Local\Programs\Python\Python35\lib\unittest\case.py", line 817, in assertEqual
     assertion_func(first, second, msg=msg)
   File "C:\Users\wushuirong\AppData\Local\Programs\Python\Python35\lib\unittest\case.py", line 1190, in assertMultiLineEqual
     self.fail(self._formatMessage(msg, standardMsg))
   File "C:\Users\wushuirong\AppData\Local\Programs\Python\Python35\lib\unittest\case.py", line 662, in fail
     raise self.failureException(msg)
 AssertionError: '请您输入密码' != '请您输入密码2'
 - 请您输入密码
 + 请您输入密码2
 ?       +
 
 
 During handling of the above exception, another exception occurred:
 
 Traceback (most recent call last):
   File "F:\python\UnitTest\UnitTest_Run_assert断言实例.py", line 51, in test_login_error_psw
     self.assertEqual(result,expect_result)
   File "C:\Program Files\JetBrains\PyCharm Community Edition 2019.3.4\plugins\python-ce\helpers\pycharm\teamcity\diff_tools.py", line 38, in _patched_equals
     raise error
 teamcity.diff_tools.EqualsAssertionError:  :: 请您输入密码 != 请您输入密码2
 
 
 
 扩展:
 """
 目标:python自带的断言
 
 """
 #判断2个字符串是否相等
 # assert 'hello'=='hello';
 #assert 'hello'=='hallo';  #错误类型:AssertionError
 #第二个字符串是否包含第一个字符串
 # assert 'h' in 'hello';
 # assert 'ha' in 'hello';
 #判断是否为True\False       1/0
 # assert True;
 # assert False;
 
 二十二、参数化
 根据需求动态获取参数并引用的过程
 应用场景:解决相同业务逻辑,不同测试数据的问题
 如何实现:
 需要安装unittest扩展插件parameterized来实现
 File-->Settings-->Project:python-->Project Interpreter--->点击+号:输入parameterized---->Install Package来进行安装
 安装完之后在cmd里面输入pip list可以找到
 
 import unittest
 from parameterized import parameterized;
 """
 目标:parameter插件应用
 步骤:
 1、导包   from parameterized import parameterized;
 2、修饰测试函数   @parameterized.expand(列表类型数据)
 3、在测试函数中使用变量接收,传递过来的值
 
 语法:
     1、单个参数:值为列表
     2、多个参数:值为列表嵌套元组  如:[(1,2,3),(2,3,4)]
 
 """
 
 class Test01(unittest.TestCase):
     #单个参数
     # @parameterized.expand(["1","2","3"])
     # def test_add_one(self,num):
     #     print("num:",num)
     #多个参数  写法一:一般调试的时候才这样子写,实际项目中,数据是分离出去的
     # data=[(1,2,3),(2,3,5)]
     # @parameterized.expand(data)
     # def test_add_more(self,a,b,result):
     #     print('{}+{}={}:'.format(a,b,result))
     #多个参数  写法二:实际工作中用这种
     #先定义一个获取数据的函数
     def get_data():
         return [(1,2,3),(3,3,6)]
     @parameterized.expand(get_data())
     def test_add_more2(self,a,b,result):
         print("{}+{}={}:".format(a,b,result));
 
 
 
 实例:
 import unittest
 from parameterized import parameterized;
 """
 
 
 """
 def add(x,y):
     return x+y;
 def get_data():
     return [(1,2,3),(3,3,5)]       #这里的5改成6,则会通过测试,这里3+3=5则会提示出错
 class Test01(unittest.TestCase):
     @parameterized.expand(get_data())
     def test_add_more2(self,a,b,expect):
         result=add(a,b);
         assert result==expect;
 
 二十三、UnitTest的跳过
 对于一些未完成的或者不满足测试条件的测试函数和测试类,可以跳过执行
 使用方法:
 #直接将测试函数标记成跳过
 @unittest.skip('代码未完成')
 场景:一般适合功能未完成用例
 #根据条件判断测试函数是否跳过
 @unitest.skipIf(condition,reason)
 场景:一般判断条件满足就不执行,如:达到指定版本,此功能失效
 实例:
 import unittest
 version=20;         #这个数字如果大于25就会跳过,小于25就会执行test03
 class Test01(unittest.TestCase):
     @unittest.skip("此方法功能暂未完成")
     def test01(self):
         """此方法功能暂未完成"""
     def test02(self):
         print('test02');
     @unittest.skipIf(version>25,"大于25,跳过")
     def test03(self):
         print("test03");
 
 二十四、html报告
 1、首先去下载一个插件
 HTMLTestRunner下载
 
 HTMLTestRunner下载地址: http://tungwaiyip.info/software/HTMLTestRunner.html
 2、HTMLTestRunner修改
 
 因为这个模块原本给python2.0用的,我用的python是3.x,所以下载后需要做些修改。
 
 下载后修改:(Ctrl+G可以跳转到指定行)
 
     94行引入的名称要改,从 import StringIO 改成import io。
     539行 self.outputBuffer = StringIO.StringIO() 要改成self.outputBuffer=io.StringIO()
     631行 print >>sys.stderr, ‘\nTime Elapsed: %s’ % (self.stopTime-self.startTime)
     修改为:print (sys.stderr, ‘\nTime Elapsed: %s’ %(self.stopTime-self.startTime))
     642行,if not rmap.has_key(cls): 需要换成 if not cls in rmap:
     766行的uo = o.decode(‘latin-1’),改成 uo=o
     772行,把 ue = e.decode(‘latin-1’) 直接改成 ue = e
 ————————————————
 
 3、HTMLTestRunner存放路径
 
 修改好的模块存放在…\python\tools下
 
 4、把用例放在case文件夹下
 5、建一个report文件夹存放报告
 6、建立执行代码的文件:
 import unittest
 import time
 from tools.HTMLTestRunner import HTMLTestRunner;
 """
 基于unittest框架执行生成hmtl报告
 操作:
 1、复制HtmlTestRunner.py文件到指定目录
 2、导包
 3、获取报告存放文件流,并实例化HTMLTestRunner类,执行run方法
 
 """
 #定义测试套件
 suite=unittest.defaultTestLoader.discover('../case',pattern='test*.py');
 #定义报告存放路径及文件名称
 report_dir='../report/{}.html'.format(time.strftime("%Y_%m_%d %H_%M_%S"))
 #获取报告文件流 并执行
 with open(report_dir,'wb') as f:
     HTMLTestRunner(stream=f,verbosity=2,title='XX项目自动化测试报告',description='操作系统 win10').run(suite);
 
 7、运行后在report里就有报告了
 
 
 二十五、PO实践
 PO:page(页面)  object(对象)
 v1:不采用任何模式(线性模型)
 v2:unittest
 v3:业务代码和页面对象分离:
 v4:实际项目中的PO模式编写:
 
 测试用例(使用TPShop项目的登录页):
 1、账号不存在
       进入登录页面
       输入一个不存在的用户名
       输入密码
       输入验证码
       点击登录按钮
       获取错误提示信息
 
 2、密码错误
       进入登录页面
       输入用户名
       输入一个错误的密码
       输入验证码
       点击登录按钮
       获取错误提示信息
 
 
 V1实例:
  #导包
 from selenium import webdriver;
 import time
  #获取driver对象
 driver=webdriver.Chrome()
  #最大化浏览器
 driver.maximize_window();
  #隐式等待
 driver.implicitly_wait(3);
  #打开url
 url='http://localhost/index.php/Admin/Admin/login.html'
 driver.get(url)
  # 输入不存在的用户名
 driver.find_element_by_name('username').send_keys('wushuirong');
  #输入密码
 driver.find_element_by_name('password').send_keys('123456')
  #输入验证码
 driver.find_element_by_css_selector('#vertify').send_keys('123456')
 driver.find_element_by_name('submit').click();
  #获取登录后的错误提示信息
 tips=driver.find_element_by_css_selector('.error').text;
 print('提示信息是:',tips);
 expect='用户名不存在';
 try:
     assert tips==expect;
 except AssertionError:
     driver.get_screenshot_as_file('../image/{}.png'.format(time.strftime("%Y_%m_%d %H_%M_%S")));
 
 V2实例:
 #导包
 import unittest;
 import time;
 from selenium import webdriver;
 #新建测试类 并继承
 class TestLogin(unittest.TestCase):
     driver=None;
     #初始化
     @classmethod
     def setUpClass(cls):
         cls.driver=webdriver.Chrome();
         cls.driver.maximize_window();
         cls.driver.implicitly_wait(3);
         url='http://localhost/index.php/Admin/Admin/login.html'
         cls.driver.get(url);
     #结束
     @classmethod
     def tearDownClass(cls):
         cls.driver.quit();
     #新建测试方法
     #用户名不存在
     def test_login_username_not_exist(self):
         # 输入不存在的用户名
         driver=self.driver;
         driver.find_element_by_name('username').send_keys('wushuirong');
         # 输入密码
         driver.find_element_by_name('password').send_keys('123456')
         # 输入验证码
         driver.find_element_by_css_selector('#vertify').send_keys('123456')   #因为没有改源代码,所以这个验证码是错的,下面的提示信息永远是验证码错误,暂时先这样
         driver.find_element_by_name('submit').click();
         # 获取登录后的错误提示信息
         tips = driver.find_element_by_css_selector('.error').text;
         print('提示信息是:', tips);
         expect = '用户名不存在';
         try:
             assert tips == expect;
         except AssertionError:
             driver.get_screenshot_as_file('../image/{}.png'.format(time.strftime("%Y_%m_%d %H_%M_%S")));
     time.sleep(3);
     def test_login_password_error(self):
         # 输入正确的用户名
         driver = self.driver;
         driver.find_element_by_name('username').clear();
         driver.find_element_by_name('username').send_keys('admin');
         # 输入错误的密码
         driver.find_element_by_name('password').send_keys('123456')
         # 输入验证码
         driver.find_element_by_css_selector('#vertify').send_keys('123456')  # 因为没有改源代码,所以这个验证码是错的,下面的提示信息永远是验证码错误,暂时先这样
         driver.find_element_by_name('submit').click();
         # 获取登录后的错误提示信息
         tips = driver.find_element_by_css_selector('.error').text;
         print('提示信息是:', tips);
         expect = '密码错误';
         try:
             assert tips == expect;
         except AssertionError:
             driver.get_screenshot_as_file('../image/{}.png'.format(time.strftime("%Y_%m_%d %H_%M_%S")));
 
 V3实例:页面层和业务层分离
 首先在V3文件夹里再建两个文件夹page和scripts分别装页面层和业务层的代码,然后页面层的文件名称必须以page开头,业务层的文件名称以test开头
 以下是page_login.py
 """
 页面对象编写技巧
 类名:使用大驼峰将模块名称抄进来,有下划线就去掉下划线
 方法:根据业务需求,每个操作步骤单独封装一个方法
     方法名:page_xxx
 """
 from selenium import webdriver;
 class PageLogin:
     def __init__(self):
         self.driver = webdriver.Chrome();
         self.driver.maximize_window();
         self.driver.implicitly_wait(3);
         url = 'http://localhost/index.php/Admin/Admin/login.html'
         self.driver.get(url);
 
 # 页面层
     #输入用户名
     def page_input_username(self,username):
         self.driver.find_element_by_name('username').send_keys(username);
     #输入密码
     def page_input_password(self,pwd):
         self.driver.find_element_by_name('password').send_keys(pwd);
     #输入验证码
     def page_input_verify_code(self,code):
         self.driver.find_element_by_css_selector('#vertify').send_keys(code)
     #点击登录
     def page_input_submit(self):
         self.driver.find_element_by_name('submit').click();
     #获取异常提示信息
     def page_input_error(self,error):
         return self.driver.find_element_by_css_selector('.error').text;
 
 #业务层
     #组装登录业务方法,给业务层调用
     def page_login(self,username,pwd,code):
         self.page_input_username(username);
         self.page_input_password(pwd);
         self.page_input_verify_code(code);
         self.page_input_submit();
 
 以下是test_login.py:
 # 导包
 import unittest;
 from v3.page.page_login import PageLogin;       #把页面层的模块和类导进来
 import time;
 from parameterized import parameterized;
 
 # 新建测试类
 class TestLogin(unittest.TestCase):
     # 初始化方法
     def setUp(self):
         # 获取登录页面对象
         self.login = PageLogin();
 
     # 结束方法
     def tearDown(self):
         self.login.driver.quit();
 
     # 新建测试方法
     @parameterized.expand([('wushuirong','123456','8888','账号不存在'),('admin','123455','8888','密码错误')])
     def test_login(self, username,pwd,code,expect):
         #调用测试登录方法
         self.login.page_login(username,pwd,code)
         #获取登录后的提示信息
         msg=self.login.page_input_error(expect);
         #断言
         try:
             self.assertEqual(msg,expect)
         except AssertionError:
             self.driver.get_screenshot_as_file('../image/{}.png'.format(time.strftime("%Y_%m_%d %H_%M_%S")));
 
 
 
 运行结果是:
 Tests failed:2
 AssertionError: '验证码错误!' != '账号不存在'
 - 验证码错误!
 + 账号不存在
 
 AssertionError: '验证码错误!' != '密码错误'
 - 验证码错误!
 + 密码错误
 
 因为没有改过源代码,没有设置万能验证码,所以这里的验证码一直是错的,没办法验证登录名是否存在和密码错误,
 实际项目中需要设置万能验证码,或者直接用cookies来跳过登录这一步,或者直接去掉验证码。 
 
 
 V4实例    比V3多了基类,公共的方法适用于任何页面任何项目
 建三个文件夹
 base (基类):      存放page页面里一些公共的方法,给page用
 例如:class Base:
     #初始化方法
     def __init__(self):
         pass
     #查找元素方法(暂时提供下面三个方法用)
     def base_find_element(self):
         pass
     #点击方法
     def base_click(self):
         pass
     #输入方法
     def base_input(self):
         pass
     #获取文件方法
     def base_get_text(self):
         pass
     #截图方法
     def base_get_image(self):
         pass
 
 注意:
        1、以上方法,解包只需要一次,在查找元素解包(详细看代码)
        2、driver为虚拟,谁调用base时,谁传入,无需关注从哪里来
        3、loc:真正使用loc的方法只有查找元素
     
 page(页面对象):一个页面封装成一个对象
          应用:继承base;(不用导包的方式)
          实现:
     1、模块名:page+实际操作模块名称        page_login.py
     2、页面对象名:以大驼峰的方法将模块名抄进来,有下划线就去掉下划线
     3、方法:涉及元素,将每个元素操作单独封装一个操作方法
     4、组装:根据需求组装以上操作步骤。
 
 例如:
 class PageLogin():
     #输入用户名
     def page_input_username(self):
         pass
     #输入密码
     def page_input_password(self):
         pass
     #输入验证码
     def page_input_verity_code(self):
         pass
     #点击【登录】
     def page_click_login_btn(self):
         pass
     #获取登录异常信息
     def page_get_err_info(self):
         pass
     #截图
     def page_get_screenshot(self):
         pass
 
     #组合业务方法
     def page_login(self):
         pass
 
 然后还有一件事情就是页面配置数据,例如登录用户名、密码、验证码等信息,需要另外起一个文件存放,把一个项目里所有类似的信息存放在同一个文件里比较方便管理
 就是page文件夹下面建一个__init__.py文件,即利用文件包(文件包比文件夹多一个init文件)的机制,在同一个page文件里的文件可以通过文件夹名.来获取__init__文件下的变量
 实际操作看下面的“page文件夹里的详情”:
 
 scripts(业务层):导包调用page页面
     实现:
          1、模块:test+实际操作模块名称   test_login.py
          2、测试业务名称:以大驼峰方法将模块名抄进来,有下划线就去掉下划线。TestLogin
          3、方法:
               1)初始化方法setUp()   注:在unittest框架中,不能使用def __init__()初始化方法
         #实例化页面对象
         #前置操作   如:打开登录页
               2)结束方法 tearDown()
         #关闭浏览器驱动
               3)测试方法
         #根据要操作的业务来实现
 
 base.py详细代码:
 from selenium.webdriver.support.wait import WebDriverWait
 import time;
 
 class Base:
     #初始化方法
     def __init__(self):
         # self.driver=driver;
         #临时代替driver
         self.driver=webdriver.Chrome();
         self.driver.maximize_window();
         self.driver.get("http://localhost/index.php/Admin/Admin/login.html");
     
     #查找元素方法(暂时提供下面三个方法用)
     def base_find_element(self,loc,timeout=30,poll=0.5):  #设置后面两个参数的目的是为了有些场景的等待时间可能 会是2秒或其他时长,如果不传参就默认30秒,如果传参就按实际参数来等待
         #使用显式等待找到一个元素
         return WebDriverWait(self.driver,timeout=timeout,poll_frequency=poll).until(lambda x:x.find_element(*loc)); #loc是位置
     #点击方法
     def base_click(self,loc):
         self.base_find_element(loc).click();
     #输入方法
     def base_input(self,loc,value):
         el=self.base_find_element(loc);
         el.clear();
         el.send_keys(value);
     #获取文件方法
     def base_get_text(self,loc):
         return self.base_find_element(loc).text;
     #截图方法
     def base_get_image(self):
         self.driver.get_screenshot_as_file('../image/{}.png'.format(time.strftime("%Y_%m_%d %H_%M_%S")));
 
 
 page文件夹里的详情:
 1、__init__.py的详细代码:
 from selenium.webdriver.common.by import By;
 """以下为登录页面元素的配置信息"""
 #用户名
 login_username=By.NAME,'username';
 
 #密码
 login_pwd=By.NAME,'password'
 #验证码
 login_verify_code=By.CSS_SELECTOR,'#vertify';
 #登录按钮
 login_btn=By.NAME,'submit';
 #获取登录后的错误提示信息
 login_err_info=By.CSS_SELECTOR,'.error';
 
 """以下为订单页面元素的配置信息"""
 #暂时没有
 
 page_login.py文件详细代码:
 from v4 import page;   #在这里包入page包,就可以直接page.    点了之后就会出现刚才在init文件里写的全部变量
 from v4.base.base import Base
 
 
 class PageLogin(Base):   #继续Base的方法,在这里输入后按ctrl+alt+空格导包
     #输入用户名
     def page_input_username(self,username):
         self.base_input(page.login_username,username);    #这一行代码,首先self.后显示在base里设置的“输入方法  base_input",选择后
                                                          #再在括号里输入loc,即页面元素,在init文件里设置的“用户名  login_username",
                                                         #这里输入page.之后就会跳出来可以选择该变量;最后的username是参数,需要在外面存
                                                         #放的txt等文件里准备好测试数据,再传进来
     #输入密码
     def page_input_password(self,pwd):
         self.base_input(page.login_pwd,pwd)
     #输入验证码
     def page_input_verity_code(self,code):
         self.base_input(page.login_verify_code,code)
     #点击【登录】
     def page_click_login_btn(self):
         self.base_click(page.login_btn);
     #获取登录异常信息
     def page_get_err_info(self):
         return self.base_get_text(page.login_err_info); #获取一定要返回
     #截图
     def page_get_screenshot(self):
         self.base_get_image();
 
     #组合业务方法
     def page_login(self,username,pwd,code):
         self.page_input_username(username);
         self.page_input_password(pwd);
         self.page_input_verity_code(code);
         self.page_click_login_btn();
 
 
 scripts文件夹下的test_login.py 详细代码:
 
 #导包
 import unittest;
 
 from parameterized import parameterized
 
 from v4.page.page_login import PageLogin;
 def get_data():
     return [('wushuirong','123456','8888','账号不存在'),('admin','123455','8888','密码错误')];
 #新建测试类并继承
 class TestLogin(unittest.TestCase):
     #setUp
     def setUp(self):
         #实例化 获取页面对像PageLogin
         self.login=PageLogin();
     #tearDown
     def tearDown(self):
         self.login.driver.quit();
     #登录测试方法
     @parameterized.expand(get_data())
     def test_login(self,username,pwd,code,expect_result):
         #调用登录方法
         self.login.page_login(username,pwd,code);
         #获取登录提示信息
         msg=self.login.page_get_err_info();
         try:
             #断言
             self.assertEqual(msg,expect_result);
         except AssertionError:
             self.login.page_get_screenshot();
             raise;           #加上这句话,会使得如果断言有错就会抛异常,同时截图。如果
         #不加上这句话,只会截图,用例还是会pass
 
 PS:断言出错时截图,同时抛异常,用例不是pass:加上raise
 
 最后,在这个方法的类处定点光标,然后运行,即可以进行完整的测试。
 
 二十六、数据驱动(重中之重)
 以数据来驱动整个测试用例的执行,也就是测试数据决定测试结果 
 比如测试加法,测试数据是1和1,那结果就是2,如果测试数据是1和2,测试结果就是3
 
 特点:自动化脚本写完之后基本不会再动,维护的重点在测试数据上,实现它要依赖于参数化的技术
 
 传入数据的方式(测试数据的来源)
 1)直接定义在测试脚本中(简单直观,但代码和数据未实现真正的分离,不方便后期维护)
 2)从文件读取数据,如JSON、excel、xml、txt等格式文件
 3)从数据库中读取数据
 4)直接调用接口获取数据源
 5)本地封装一些生成数据的方法
 
 1、JSON
 是JS对象表示法,基本文本,独立于语言的轻量级数据交换格式
 
 语法规则:
 - 大括号保存对象
 - 中括号保存数组
 - 对象数组可以互相嵌套
 - 数据采用键值对表示
 - 多个数据由逗号分隔
 
 JSON值
 - 数字(整数或浮点数)
 - 字符串(在双引号中)
 - 逻辑值(true 或 false)
 - 数组
 - 对象
 - null
 
 数据操作:
 1、首先要导入依赖包
 import json
 2、python字典与JSON之间的转换
 a) 把python字典类型转换为JSON字符串
 实例:
 """
 目标: 把python字典类型转换为JSON字符串
 案例:data={"name":"张三","age":18}
 操作:
 1、导包
 2、调用dumps()方法 将字典转换成json字符串
 注意:json中还有一个方法dump()  写入json  不要写错
 
 """
 #导包
 import json
 #定义字典
 data={"name":"张三","age":18}
 #调用dumps()方法 将字典转换成json字符串
 print("转换之前的数据类型:",type(data))
 d2=json.dumps(data);
 print("转换之后的数据类型:",type(data))
 print(d2);
 
 结果是:
 转换之前的数据类型: 
 转换之后的数据类型: 
 {"name": "\u5f20\u4e09", "age": 18}
 
 b)将json转换成Python字典
 """将字符串转换成字典"""
 #定义字符串
 data='{"name":"张三","age":18}';
 print("转换之前的数据类型:", type(data))
 d3=json.loads(data);
 print("转换之后的数据类型:", type(d3))
 print(data);
 
 结果:
 转换之前的数据类型: 
 转换之后的数据类型: 
 {"name":"张三","age":18}
 
 注意:
 错误写法:data="{'name':'张三'}"
 这样子写会报错,说属性名必须用双引号,因此一定要用单引号包双引号来写
 3、JSON文件读写
 a)读取json文件
 with open('data.json',encoding='UTF-8') as f:
         data=json.load(f); #返回的数据类型为字典或列表
 实例:读取的是下面例子写入的json文件
 """
 目标: 读取road
 案例:
 之前写入的文件
 data={"name":"tom","age":18}
 """
 #导包
 import json
 #打开要读取的文件流 并调用load方法
 with open("./data/write2.json",encoding="utf-8") as f:
    data=json.load(f);
    print(data);
 
 b)写入JSON文件
 param={'name':'tom', 'age':20}
 with open('data2.json', 'w', encoding='UTF-8') as f:
       json.dump(param,f);   #写什么东西,往哪写  f就是上面那个文件流
 
 """
 目标: 写入json
 案例:
 1、data={"name":"tom","age":18}
 2、data={"name":"张三","age":18}
 操作:
 1、导包
 2、调用dump()方法
 
 """
 #导包
 import json
 #定义字典
 data={"name":"tom","age":18}
 data2={"name":"张三","age":18}
 #获取文件流 并写入数据
 with open("./data/write.json","w",encoding="utf-8") as f:
     #调用dump()方法
     json.dump(data,f);
 with open("./data/write2.json","w",encoding='utf-8') as f2:
     json.dump(data2,f2,ensure_ascii=False);   #这里如果没有加最后一个ensure_ascii=False,则write2文件里的中文会以阿斯卡玛的方式显示
 
 
 结果是在data文件夹里生成两个文件write.json和write2.json,内容分别是:
 
 {"age": 18, "name": "tom"}
 
 {"age": 18, "name": "张三"}
 
 
 
 综合实例一:网页计算器安全
 1、采用po模式 的分层思想对页面进行封装
 2、编写测试脚本
 3、使用参数化传入测试数据
 PS:input的值不是获取text,而是获取其value属性的值
 
 分析:
 结构:
      base
     #初始化方法
     #查找元素
     #点击
     #获取value属性方法封装
     #截图
      page
     #点击数字
          for n in str(num):
                           loc=By.ID,"simple{}".format(n);
               #调用base内的方法
     #点击加号
     #点击等号
     #获取结果
     #组装业务方法
      scripts
     #初始化方法
         #获取计算页面页面对象
         #获取driver
     #结果方法
                     #关闭driver
     #调用测试方法
          #调用加法运算业务方法
          #断言
          #截图
 
 
 
 driver封装
    类名:
     #定义类变量
     driver=None;
     #获取driver方法
     @classmethod
     def get_driver(cls):
         if cls.driver is None:
             #实例化浏览器
             #最大化
             #打开浏览器
             cls.driver.get(page.url);
         return cls.driver;
     #退出driver
     @classmethod
     def quit_driver(cls):
         if cls.driver:
             cls.driver.quit();
             #注意:此处一定要置空
             cls.driver=None;
 
 读取json数据封装
 1、读取工具封装
       #导包
       #打开json获取文件流  并调用load方法
 2、组装读取出来的数据格式封装
       #预期格式:[(),()]
       #默认实际格式{"":"","":""}
        思路:
     1、新建空列表
     2、使用字典.values()方法获取所有的字典值;
     3、使用列表.append((字典.get("键名")))
     4、返回列表arrs
 项目实际:
 文件夹与文件层级:
 网页计算器实例:
       base:
     base.py
     get_driver.py
       data:
     calc.json
       image:
       page:
     __init__.py
     page_calc.py
       scripts:
     test_calc.py
        tool:
     read_json.py
 
 整体思路:
 1、首先建好文件夹和文件名后,先写base文件里的基础方法
 2、封装好get_driver
 3、page文件里先把所有用例涉及的元素写好配置信息,在__init__.py文件里
 4、page_calc.py写好全部操作的方法
 5、tool里写好read_json.py的读取json文件的方法,以便在写test_calc.py的时候直接调用
 6、写test_calc.py文件
 7、准备测试用例数据
 
 具体源码:
 base.py:
 import time;
 from selenium.webdriver.support.wait import WebDriverWait
 class Base:
     #初始化方法
     def __init__(self,driver):
         self.driver=driver;
     #查找元素
     def base_find_element(self,loc,timeout=30,poll=0.5):
         """          #输入三对双引号后按一下回车键就会显示该方法下的所有参数
         :param loc:
         :param timeout:
         :param poll:
         :return:
         """
         return WebDriverWait(self.driver,timeout=timeout,poll_frequency=poll).until(lambda x:x.find_element(*loc));
     #点击
     def base_click(self,loc):
         self.base_find_element(loc).click();
     #获取value属性方法封装
     def base_get_vlaue(self,loc):
         return self.base_find_element(loc).get_attribute('value');
     #截图
     def base_get_image(self):
         self.driver.get_screenshot_as_file('../image/{}.png'.format(time.strftime("%Y_%m_%d %H_%M_%S")));
 
 get_driver.py:
 from selenium import webdriver;
 from 网页计算器实例 import page;
 class GetDriver:
     #设置类属性
     driver=None;
     #获取driver
     @classmethod
     def get_driver(cls):
         if cls.driver is None:
             #实例化浏览器
             cls.driver=webdriver.Chrome();
             #最大化
             cls.driver.maximize_window();
             #打开浏览器
             cls.driver.get(page.url);
         return cls.driver;
     #退出driver
     @classmethod
     def quit_driver(cls):
         if cls.driver:
             cls.driver.quit();
             #注意:此处有一个大坑,关闭后driver不置空,跑多条用例是跑不起来了,因为上面判断了if cls.driver is None,所以要加下面这句
             cls.driver=None;
 
 __init__.py
 """以下为计算器配置数据"""
 from selenium.webdriver.common.by import By
 #由于数字键有一定的规律,所以暂时不用定位此键,用到的时候再考虑此键怎么解决
 # calc_num=By.CSS_SELECTOR,"simple9";
 #加号
 calc_add=By.CSS_SELECTOR,"#simpleAdd";
 #=号
 calc_eq=By.CSS_SELECTOR,'#simpleEqual';
 #获取结果
 calc_result=By.CSS_SELECTOR,'#resultIpt';
 #清屏
 calc_clear=By.CSS_SELECTOR,'#simpleClearAllBtn';
 
 """以下为服务器域名配置地址"""
 url="http://cal.apple886.com/"
 
 page_calc.py:
 from selenium.webdriver.common.by import By
 from 网页计算器实例.base.base import Base;
 from 网页计算器实例 import page;
 class PageCalc(Base):
     #点击数字方法
     def page_click_num(self,num):
         for n in str(num):
             loc=By.ID,"simple{}".format(n);
             self.base_click(loc);
     #点击加号
     def page_click_add(self):
         self.base_click(page.calc_add)
     #点击=号
     def page_click_eq(self):
         self.base_click(page.calc_eq);
     #获取结果方法
     def page_get_value(self):
         return self.base_get_vlaue(page.calc_result);
     #点击清屏
     def page_click_clear(self):
         self.base_click(page.calc_clear);
     #截图
     def page_get_image(self):
         self.base_get_image();
     #组装加法运算方法
     def page_add(self,a,b):
         self.page_click_num(a);
         self.page_click_add();
         self.page_click_num(b);
         self.page_click_eq();
 
 test_calc.py:
 import unittest;
 from parameterized import parameterized;
 import time;
 from 网页计算器实例.base.get_driver import GetDriver
 from 网页计算器实例.page.page_calc import PageCalc
 from 网页计算器实例.tool.read_json import read_json
 def get_data():
     datas=read_json('calc.json');
     #新建空列表
     arrs=[]
     for data in datas.values():
         arrs.append((data['a'],data['b'],data['expect']));
     return arrs;
 class TestCalc(unittest.TestCase):
     driver=None;
     #setUpClass
     @classmethod
     def setUpClass(cls):
         #获取driver
         cls.driver=GetDriver().get_driver();
         #初始化页面对象
         cls.calc=PageCalc(cls.driver);
     #tearDown
     @classmethod
     def tearDownClass(cls):
         GetDriver().quit_driver();
     #测试方法 加法
     @parameterized.expand(get_data())
     def test_add_calc(self,a,b,expect):
         #调用加法运算业务方法
         self.calc.page_add(a,b);
         time.sleep(3)
         try:
             #断言
             self.assertEqual(self.calc.page_get_value(),str(expect)); #这里如果不加str会断言出错“3”!=3
             #截图
         except:
             self.calc.page_get_image();
             raise;
 
 read_json.py:
 #导包
 import json;
 #调用load方法
 def read_json(filename):
     filepath='../data/'+filename;
     with open(filepath,'r',encoding='utf-8') as f:
         return json.load(f);
 
 
 calc.json:
 {
 "calc_001":{"a": 1,"b": 2,"expect": 3},
   "calc_002":{"a": 1001,"b": 2,"expect":1002},
   "calc_003":{"a": 99,"b": 2,"expect": 101}
 }
 
 以上测试用例数据,可以看到第二条的预期结果是故意写错的。因此最终运行结果是:
 Tests failed:1,passed:2
 002 != 1003
 
 Expected :1003
 Actual   :1002      
 
 
 综合实例二:某网站的登录模块的单元测试实例
 1.1 实现步骤
 1、绽测试用例
 2、采用PO模式的分层思想对页面进行封装
 3、编写测试脚本
 4、定义数据文件,实现参数化
 
 1.2 用例设计
 ID       模块    优先级    测试标题             预置条件                    步骤描述        测试数据                                  预期结果              测试结果
 login_001   登录    P0    用户名错误        1、打开首页        1、输入错误用户名    1、用户名:123        提示框提示:
                          2、点击登录链接             2、输入密码    2、密码:123456        账号不存在
                             3、输入验证码    3、验证码:万能验证码
                             4、点击登录按钮
 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 login_002   登录    P0    密码错误           1、打开首页        1、输入用户名    1、用户名:username             提示框提示:
                          2、点击登录链接             2、输入错误密码    2、密码:123456        密码错误
                             3、输入验证码    3、验证码:万能验证码
                             4、点击登录按钮
 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 login_003   登录    P0    用户名为空        1、打开首页        1、不输入用户名    1、用户名:        提示框提示:
                          2、点击登录链接             2、输入密码    2、密码:123456        用户名不能为空
                             3、输入验证码    3、验证码:万能验证码
                             4、点击登录按钮
 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 login_004   登录    P0    密码为空        1、打开首页        1、输入用户名    1、用户名:username            提示框提示:
                          2、点击登录链接             2、不输入密码    2、密码:         密码不能为空
                             3、输入验证码    3、验证码:万能验证码
                             4、点击登录按钮
 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 login_005   登录    P0    验证码为空        1、打开首页        1、输入用户名    1、用户名:username            提示框提示:
                          2、点击登录链接             2、输入密码    2、密码:123456        验证码不能为空
                             3、不输入验证码    3、验证码:万能验证码
                             4、点击登录按钮
 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 login_006   登录    P0    正常登录           1、打开首页        1、输入用户名    1、用户名:username    登录成功
                          2、点击登录链接             2、输入密码    2、密码:123456        
                             3、输入验证码    3、验证码:万能验证码
                             4、点击登录按钮
 
 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 
 登录成功的标志:一般有"退出"这类的字眼
 
 查找元素判断是否操作成功  思路封装
  def base_if_success(self,loc):
         try:
             self.base_find_element(loc,timeout=2)
            #找到元素   assertTrue
             return True;
         except:
             return False;
 
 如何区分正向逆向用例
       思路:在测试数据中添加一个标识正向用例或逆向用例的标记,如True/False
       步骤:
     1、调用登录方法(此登录方法中,只有输入用户名、输入密码、输入验证码、点击登录按钮)   不要封装点击登录连接
     2、判断是否正向用例:
         判断是否有”退出“
         点击”退出“
         点击登录连接地址
          否则:
         获取异常登录 信息
         断言操作
         如果异常提示是弹框,则需要关闭弹窗
 
 
 
 二十八、日志:
 用于记录系统运行时的信息,对一个事件的记录;也称为Log
 A:作用:
 --调试程序
 --了解系统程序运行的情况,是否正常
 --系统程序运行故障分析与问题定位
 --用来做用户行为分析和数据统计
 
 B:日志级别:优先级、重要性或严重程度
 DEBUG      调试级别,打印非常详细的日志信息,通常用于对代码的调试
 INFO         信息级别,打印一般的日志信息,突出强调程序的运行过程
 WARNING 警告级别,打印警告日志信息,表明会出现潜在错误的情形,一般不影响软件的正常使用
 ERROR      错误级别,打印错误异常信息,该级别的错误可能会导致系统的一些功能无法正常使用
 CRITICAL   严重错误级别,一个严重的错误,这表明系统可能无法继续运行
 
 说明:一般建议只使用前四个级别
 当为程序指定一个日志级别后,程序会记录所有日志级别大于或等于指定日志级别的日志信息,而不是仅仅记录指定级别的。
 
 C:日志的基本用法:
 logging模块:python中有一个标准库模块logging可以直接记录日志
 C1、步骤:
 1、导包  如:import logging
 2、调用相应的级别方法,记录日志信息  logging.debug('debug...');
 C2、设置级别:
 logging.basicConfig(level=logging.DEBUG)    
 提示:
 1、默认级别为:logging.WARNING
 2、设置级别时调用的是logging文件夹下面的常量(DEBUG),而不是调用小写的方法
 3、切记:设置级别后,日志信息只会记录大于等于此级别的信息;
 
 C3、设置日志格式:    
 默认的日志格式为:
 日志级别:Logger名称:日志内容
 
 自定义日志格式:
 logging.basicConfig(format="%(levelname)s:%(name)s:%(message)s");
 
 format参数可能用到的:
 占位符            描述
 %(name)s               Logger的名字
 %(levelno)s        数字形式的日志级别
 %(levelname)s        文本形式的日志级别                   ***
 %(pathname)s        调用日志输出函数模块的完整路径名,可能没有
 %(filename)s         调用日志输出函数的模块的文件名              ***
 %(module)s         调用日志输出函数的模块名
 %(funcName)s          调用日志输出函数的函数名
 %(lineno)d        调用日志输出的语句所在的代码行        ***
 %(created)f        当前时间,用UNIX标准的表示 时间的浮点数表示 
 %(relativeCreated)d     输出日志信息时,自Logger创建以来的毫秒数
 %(asctime)s        字符串形式的当前时间。默认格式是“2003-07-07 16:49:45,897”   ****
 %(thread)d        线程ID。可能没有
 %(threadName)s        线程名。可能没有
 %(process)d        线程ID。可能没有
 %(message)s        用户输出的消息                ***
 
 #设置日志格式的示例代码
 fmt='%(asctime)s %(levelname)s [%(name)s] [%(filename)s(%(funcName)s:%(lineno)d)] - %(message)s'
 logging.basicConfig(level=logging.INFO,format=fmt);
 
 C4、将日志信息输出到文件中:
 默认情况下Python的logging模块将日志打印到了标准输出中(控制台)
 将日志信息输出到文件中:
 logging.basicConfig(filename='a.log');
 
 最终代码:
 #导包
 import logging;
 #设置日志格式
 fmt='%(asctime)s %(levelname)s [%(name)s] [%(filename)s(%(funcName)s:%(lineno)d)] - %(message)s'
 #设置日志级别\格式\设置日志保存到指定文件中
 logging.basicConfig(level=logging.INFO,format=fmt,filename='./logs/log01.log');  #要用大写的INFO,大写的是变量,小写的是方法
 #调用指定级别,输入日志信息     默认只显示warnning及以上的级别。在加入上面那句设置级别之后才会显示全部级别
 logging.debug('this is a debug....')
 logging.info('info....')
 logging.warning("warning");
 logging.error('error');
 logging.critical('critical');
 
 在logs文件夹里成生的log01.log文件内容是这样子的:
 2020-05-21 14:39:36,731 INFO [root] [test01_logging01.py(:12)] - info....
 2020-05-21 14:39:36,731 WARNING [root] [test01_logging01.py(:13)] - warning
 2020-05-21 14:39:36,731 ERROR [root] [test01_logging01.py(:14)] - error
 2020-05-21 14:39:36,731 CRITICAL [root] [test01_logging01.py(:15)] - critical
 
 PS:如果想要把message里的内容写成中文而不显示乱码:
 在logging.basicConfic这里按住basicConfic的ctr键进入其底层代码__init__文件里改一下底层代码,但是暂时不要这样子。
 
 日志的使用实例:
 step1、先在tool文件夹里加上get_log.py文件,源代码如下:
 #导包
 import logging;
 #定义和获取Logging函数
 def get_logging():
     fmt='%(asctime)s %(levelname)s [%(name)s] [%(filename)s(%(funcName)s:%(lineno)d)] - %(message)s';
     logging.basicConfig(level=logging.INFO,filename="../log/log01.log",format=fmt);
     return logging;
 
 step2、在base.py文件里实例化get_logging和导包,然后在每一个操作前面或后面加上log.info(),如下面源码所示:
 
 from selenium import webdriver;
 from selenium.webdriver.support.wait import WebDriverWait
 import time;
 from testwo.tool.get_log import get_logging
 log=get_logging();
 class Base:
     #初始化方法
     def __init__(self, driver):
         log.info("初始化driver{}".format(driver));
         self.driver = driver;
     def base_find_element(self,loc,timeout=30,poll=0.5):  #设置后面两个参数的目的是为了有些场景的等待时间可能 会是2秒或其他时长,如果不传参就默认30秒,如果传参就按实际参数来等待
         log.info('正在查找元素:{}'.format(loc));
         #使用显式等待找到一个元素
         return WebDriverWait(self.driver,timeout=timeout,poll_frequency=poll).until(lambda x:x.find_element(*loc)); #loc是位置
     #点击方法
     def base_click(self,loc):
         log.info('正在点击元素:{}'.format(loc));
         self.base_find_element(loc).click();
     #输入方法
     def base_input(self,loc,value):
         log.info('正在给元素{}输入内容:{}'.format(loc,value));
         el=self.base_find_element(loc);
         el.clear();
         log.info('正在清空{}的值'.format(loc));
         el.send_keys(value);
         log.info('正在给元素{}输入内容'.format(loc));
     #获取文件方法
     def base_get_text(self,loc):
         log.info('正在获取元素:{}的文本'.format(loc));
         return self.base_find_element(loc).text;
     #截图方法
     def base_get_image(self):
         self.driver.get_screenshot_as_file('../image/{}.png'.format(time.strftime("%Y_%m_%d %H_%M_%S")));
 
     # 查找元素判断是否操作成功
     # 思路封装
     def base_if_success(self,loc):
         try:
             self.base_find_element(loc,timeout=2);
             log.info("判断元素:{} 存在".format(loc));
             return True;
         except:
             log.info("判断元素:{} 不存在".format(loc))
             return False;
 
 step3、在test_login.py文件里导包和实例化,并在相应的地方加上log.error:
 
 #导包
 import unittest;
 from parameterized import parameterized
 from testwo.base.get_driver import GetDriver
 from testwo.page.page_login import PageLogin
 from testwo.tool.read_json import read_json
 from testwo.tool.get_log import get_logging
 log=get_logging();
 def get_data():
     datas=read_json('login.json');
     #新建空列表
     arrs=[]
     for data in datas.values():
         arrs.append((data['username'],data['pwd'],data['code'],data['expect'],data['success']));
     return arrs;
 class LestLogin(unittest.TestCase):
     login=None;
     @classmethod
     def setUpClass(cls):
         try:
             # 获取driver
             cls.driver = GetDriver().get_driver();
             # 初始化页面对象
             cls.login=PageLogin(cls.driver);
             cls.login.page_click_login_link();
         except Exception as e:
             log.error(e);
     @classmethod
     def tearDownClass(cls):
         cls.login.driver.quit();
     #登录测试方法
     @parameterized.expand(get_data())
     def test_login(self,username,pwd,code,expect_result,success):
         #调用登录方法
         self.login.page_login(username,pwd,code);
         if success:
             #判断“退出”是否存在
             try:
                 self.assertTrue(self.login.page_login_success());
                 #点击退出
                 self.login.page_click_logout();
                 try:
                     self.assertTrue(self.login.page_logout_success());
                 except:
                     self.login.page_get_screenshot();
                 # 点击登录连接
                 self.login.page_click_login_link();
             except Exception as e:
                 self.login.page_get_screenshot();
                 log.error(e);
         else:
             #获取登录后提示信息
             msg=self.login.page_get_err_info();
             print('错误信息是:',msg)
             try:
                 self.assertEqual(msg,expect_result);
             except Exception as e:
                 self.login.page_get_screenshot();
                 log.error(e);
             # except AssertionError:
             #     self.login.page_get_screenshot();
             raise;
 
 运行结果生成的log01.log文件如下所示(部分):
 
 在log01.log文件那里右键---show in exploer,然后再用记事本打开:(可以ctr+F查找ERROR关键字来看错误信息)
 
 2020-05-21 16:46:54,709 INFO [root] [base.py(__init__:9)] - 初始化driver
 2020-05-21 16:46:54,752 INFO [root] [base.py(base_click:17)] - 正在点击元素:('css selector', '.pull-right>a')
 2020-05-21 16:46:54,775 INFO [root] [base.py(base_find_element:12)] - 正在查找元素:('css selector', '.pull-right>a')
 2020-05-21 16:46:55,600 INFO [root] [base.py(base_input:21)] - 正在给元素('id', 'inputName')输入内容:shuiqingfu
 2020-05-21 16:46:55,601 INFO [root] [base.py(base_find_element:12)] - 正在查找元素:('id', 'inputName')
 2020-05-21 16:46:55,672 INFO [root] [base.py(base_input:24)] - 正在清空('id', 'inputName')的值
 2020-05-21 16:46:55,727 INFO [root] [base.py(base_input:26)] - 正在给元素('id', 'inputName')输入内容
 2020-05-21 16:46:55,731 INFO [root] [base.py(base_input:21)] - 正在给元素('name', 'password')输入内容:
 2020-05-21 17:14:43,055 INFO [root] [base.py(base_find_element:12)] - 正在查找元素:('id', 'user.errors')
 2020-05-21 17:14:43,566 ERROR [root] [test_login.py(test_login:59)] -  :: 验证码错误 != 账号不存在
 
 使用日志的高级用法:
 日志封装:
 #定义获取日志类
     #定义类属性 logger=None
     @classmethod
     #定义获取logger日志器的类方法
         if cls.logger is None:   #判断类属性logger是否为空
             #获取日志器对象
             #设置日志器级别
             #获取 控制台处理器
             #获取文件处理器
             #获取格式器
             #将格式器添加到处理器中
             #将格式器添加到日志器中
         return 类属性 logger
     注意:1、以上条件无论是否成立,最后都会返回类属性logger
     2、当第一次调用时,条件一定成立,将类属性logger设置不为空
     3、当第二次以上调用时,永远返回第一次设置的类属性对象。
 
 具体源码:
 #导包
 import logging.handlers;
 class GetLogger:
     logger=None;
     @classmethod
     def get_logger(cls):
         if cls.logger is None:
             #获取日志器
             cls.logger=logging.getLogger();
             #设置日志器 级别
             cls.logger.setLevel(logging.INFO);
             #获取处理器 控制台
             sh=logging.StreamHandler();
 
             #获取处理器 文件--以时间分隔
             th=logging.handlers.TimedRotatingFileHandler(filename="../log/log02.log",when='midnight',interval=1,backupCount=30,encoding='utf-8');
 
 
             #设置格式器
             fmt='%(asctime)s %(levelname)s [%(name)s] [%(filename)s(%(funcName)s:%(lineno)d)] - %(message)s';
             fm = logging.Formatter(fmt);
 
             #将格式器添加到处理器 控制台
             sh.setFormatter(fm);
 
             #将格式器添加到  处理器 文件
             th.setFormatter(fm);
             #将处理器添加到 日志器
             cls.logger.addHandler(sh);
             cls.logger.addHandler(th);
             return cls.logger;
             #测试效果,包括中文
         if __name__=='__main__':
             logger=GetLogger().get_logger();
             logger.info("info信息被执行")
             logger.error("error信息被执行");
 
 
 然后在base.py和test.login.py文件里,将:
 log=get_logger()
 改成:
 log=GetLogger().get_logger();
 并且重新导包:
 from testwo.tool.logger import GetLogger;
 
 之后运行test_login.py生成的日志就是(部分,可以正常显示中文):
 2020-05-21 20:57:15,428 INFO [root] [base.py(__init__:12)] - 初始化driver
 2020-05-21 20:57:15,429 INFO [root] [base.py(base_click:20)] - 正在点击元素:('css selector', '.pull-right>a')
 2020-05-21 20:57:15,429 INFO [root] [base.py(base_find_element:15)] - 正在查找元素:('css selector', '.pull-right>a')
 
 
 最后:项目实战了
 一、自动化测试的流程
 1、需求分析
 2、挑选适合做自动化测试的功能
 3、设计测试用例
 4、搭建自动化测试环境【可选】
 5、设计自动化测试项目的架构【可选】
 6、编写代码
 7、执行测试用例
 8、生成测试报告并分析结果
 
 实际工作中的流程:
 1、将功能用例转化自动化用例(在功能用例模板后新增一列是否自动化)
 2、搭建自动化测试环境(本机执行自动化的依赖环境:python\pycharm\浏览器、浏览器驱动、selenium\parameterized)
 3、搭建自动化框架(PO模式+数据驱动+log+报告)   ps:unittest是一个用例管理和执行的框架
 4、编写代码
 5、执行用例
 6、生成报告\分析log
 
 项目需求:
 登录商城、搜索商品并添加到购物车、下订单、支付整个流程
 用例使用正向用例
 
 自动化测试结构:
 1、base
 2、page
 3、scripts
 4、tool
 5、data
 6、log
 7、image
 8、report
 
 根据用例决定base.py文件里有哪些方法:
 from selenium import webdriver;
 from selenium.webdriver.support.wait import WebDriverWait
 
 
 class Base:
     def __init__(self,driver):
         self.driver=driver;
     #查找元素 方法 封闭
     def base_find_element(self,loc,timeout=30,poll=0.5):
         #使用显式等待查找元素
         return WebDriverWait(self.driver,timeout=timeout,poll_frequency=poll).until(lambda x:x.find_element(*loc));
     #点击元素
     def base_click(self,loc):
         self.base_find_element(loc).click();
     #输入元素
     def base_input(self,loc,value):
         el=self.base_find_element(loc);
         el.clear();
         el.send_keys(value);
     #获取文本信息
     def base_get_text(self,loc):
        return self.base_find_element(loc).text;
     #截图
     def base_get_screenshot(self):
         self.driver.get_screenshot_as_file("../image/{}.png".format("%Y_%m_%d %H_%M_%S"));
     #判断元素是否存在的方法
     def base_element_is_exist(self,loc):
         try:
             self.base_find_element(loc,timeout=2);
             return True;
         except:
             return False; 
 
 
  后面的代码,打算把整个实例一起拿出来另外写一篇,这部份的笔记就先这样子了。  
  
