python selenium自动化测试框架搭建的方法步骤


Posted in Python onJune 14, 2020

设计思路

本文整理归纳以往的工作中用到的东西,现汇总成基础测试框架提供分享。

框架采用python3 + selenium3 + PO + yaml + ddt + unittest等技术编写成基础测试框架,能适应日常测试工作需要。

1、使用Page Object模式将页面定位和业务操作分开,分离测试对象(元素对象)和测试脚本(用例脚本),一个页面建一个对象类,提高用例的可维护性;

2、使用yaml管理页面控件元素数据和测试用例数据。例如元素ID等发生变化时,不需要去修改测试代码,只需要在对应的页面元素yaml文件中修改即可;

3、分模块管理,互不影响,随时组装,即拿即用。

GitHub项目地址:https://github.com/yingoja/DemoUI

测试框架分层设计

python selenium自动化测试框架搭建的方法步骤

  • 把常见的操作和查找封装成基础类,不管是什么产品,可直接拿来复用
  • 业务层主要是封装对象页面类,一个页面建一个类,业务层页面继承基础层
  • 用例层针对产品页面功能进行构造摸拟执行测试
  • 框架层提供基础组件,支撑整个流程执行及功能扩展,给用例层提供各页面的元素数据、用例测试数据,测试报告输出等

测试框架目录结构

python selenium自动化测试框架搭建的方法步骤

如下思维导图目录结构介绍:

python selenium自动化测试框架搭建的方法步骤

编写用例方法

login.yaml

testinfo:
    - id: test_login001
     title: 登录测试
     info: 打开抽屉首页
 testcase:
    - element_info: login-link-a
     find_type: ID
     operate_type: click
     info: 打开登录对话框
    - element_info: mobile
     find_type: ID
     operate_type: send_keys
     info: 输入手机号
    - element_info: mbpwd
     find_type: ID
     operate_type: send_keys
     info: 输入密码
    - element_info: //input[@class='keeplogin']
     find_type: XPATH
     operate_type: click
     info: 单击取消自动登录单选框
    - element_info: //span[text()='登录']
     find_type: XPATH
     operate_type: click
     info: 单击登录按钮
    - element_info: userProNick
     find_type: ID
     operate_type: perform
     info: 鼠标悬停账户菜单
    - element_info: //a[@class='logout']
     find_type: XPATH
     operate_type: click
     info: 选择退出
 check:
    - element_info: //div[@class='box-mobilelogin']/div[1]/span
     find_type: XPATH
     info: 检查输入手机号或密码,登录异常提示
    - element_info: userProNick
     find_type: ID
     info: 成功登录
    - element_info: reg-link-a
     find_type: ID
     info: 检查退出登录是否成功

例如,我们要新增登录功能测试用例:

首先,只需在testyaml目录下新增一个页面对象yaml文件,参考login.yaml格式编写即可。这些文件是提供给封装页面对象类调用并执行定位识别操作。

login_data.yaml

-
  id: test_login001.1
  detail : 手机号和密码为空登录
  screenshot : phone_pawd_empty
  data:
   phone: ""
   password: ""
  check :
    - 手机号不能为空
 -
  id: test_login001.2
  detail : 手机号为空登录
  screenshot : phone_empty
  data :
   phone: ""
   password : aa
  check :
   - 手机号不能为空
 -
  id: test_login001.3
  detail : 密码为空登录
  screenshot : pawd_empty
  data :
   phone : 13511112222
   password: ""
  check :
   - 密码不能为空
 -
  id: test_login001.4
  detail : 非法手机号登录
  screenshot : phone_error
  data :
   phone : abc
   password: aa
  check :
   - 手机号格式不对
 -
  id: test_login001.5
  detail : 手机号或密码不匹配
  screenshot : pawd_error
  data :
   phone : 13511112222
   password: aa
  check :
   - 账号密码错误
 -
  id: test_login001.6
  detail : 手机号和密码正确
  screenshot : phone_pawd_success
  data :
   phone : 13865439800
   password: ********
  check :
   - yingoja
 
 login_data.yaml

login_data.yaml

其次,在testdata目录下新增一个login_data.yaml文件提供给登录接口传参的测试数据,编写格式参考login_data.yaml文件。

loginPage.py

#!/usr/bin/env python
 # _*_ coding:utf-8 _*_
  __author__ = 'YinJia'
  
  import os,sys
  sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
  from config import setting
  from selenium.webdriver.support.select import Select
  from selenium.webdriver.common.action_chains import ActionChains
 from selenium.webdriver.common.by import By
 from public.page_obj.base import Page
 from time import sleep
 from public.models.GetYaml import getyaml
 
 testData = getyaml(setting.TEST_Element_YAML + '/' + 'login.yaml')
 
 class login(Page):
   """
   用户登录页面
   """
   url = '/'
   dig_login_button_loc = (By.ID, testData.get_elementinfo(0))
   def dig_login(self):
     """
     首页登录
     :return:
     """
     self.find_element(*self.dig_login_button_loc).click()
     sleep(1)
 
   # 定位器,通过元素属性定位元素对象
   # 手机号输入框
   login_phone_loc = (By.ID,testData.get_elementinfo())
   # 密码输入框
   login_password_loc = (By.ID,testData.get_elementinfo())
   # 取消自动登录
   keeplogin_button_loc = (By.XPATH,testData.get_elementinfo())
   # 单击登录
   login_user_loc = (By.XPATH,testData.get_elementinfo())
   # 退出登录
   login_exit_loc = (By.ID, testData.get_elementinfo())
   # 选择退出
   login_exit_button_loc = (By.XPATH,testData.get_elementinfo())
 
   def login_phone(self,phone):
     """
     登录手机号
     :param username:
     :return:
     """
     self.find_element(*self.login_phone_loc).send_keys(phone)
 
   def login_password(self,password):
     """
     登录密码
     :param password:
     :return:
     """
     self.find_element(*self.login_password_loc).send_keys(password)
 
   def keeplogin(self):
     """
     取消单选自动登录
     :return:
     """
     self.find_element(*self.keeplogin_button_loc).click()
 
   def login_button(self):
     """
     登录按钮
     :return:
     """
     self.find_element(*self.login_user_loc).click()
 
   def login_exit(self):
     """
     退出系统
     :return:
     """
     above = self.find_element(*self.login_exit_loc)
     ActionChains(self.driver).move_to_element(above).perform()
     sleep(2)
     self.find_element(*self.login_exit_button_loc).click()
 
   def user_login(self,phone,password):
     """
     登录入口
     :param username: 用户名
     :param password: 密码
     :return:
     """
     self.open()
     self.dig_login()
     self.login_phone(phone)
     self.login_password(password)
     sleep(1)
     self.keeplogin()
     sleep(1)
     self.login_button()
     sleep(1)
 
  phone_pawd_error_hint_loc = (By.XPATH,testData.get_CheckElementinfo(0))
  user_login_success_loc = (By.ID,testData.get_CheckElementinfo(1))
   exit_login_success_loc = (By.ID,testData.get_CheckElementinfo(2))
 
   # 手机号或密码错误提示
   def phone_pawd_error_hint(self):
     return self.find_element(*self.phone_pawd_error_hint_loc).text
 
   # 登录成功用户名
   def user_login_success_hint(self):
     return self.find_element(*self.user_login_success_loc).text
 
   # 退出登录
   def exit_login_success_hint(self):
     return self.find_element(*self.exit_login_success_loc).text

然后,在page_obj目录下新增一个loginPage.py文件,是用来封装登录页面对象类,执行登录测试流程操作。

login_sta.py

#!/usr/bin/env python
 # _*_ coding:utf-8 _*_
 __author__ = 'YinJia'
 
 
 import os,sys
 sys.path.append(os.path.dirname(os.path.dirname(__file__)))
 import unittest,ddt,yaml
 from config import setting
 from public.models import myunit,screenshot
 from public.page_obj.loginPage import login
 from public.models.log import Log
 
 try:
   f =open(setting.TEST_DATA_YAML + '/' + 'login_data.yaml',encoding='utf-8')
   testData = yaml.load(f)
 except FileNotFoundError as file:
   log = Log()
   log.error("文件不存在:{0}".format(file))
 
 @ddt.ddt
 class Demo_UI(myunit.MyTest):
   """抽屉新热榜登录测试"""
   def user_login_verify(self,phone,password):
     """
     用户登录
     :param phone: 手机号
     :param password: 密码
     :return:
     """
     login(self.driver).user_login(phone,password)
 
   def exit_login_check(self):
     """
     退出登录
     :return:
     """
     login(self.driver).login_exit()
 
   @ddt.data(*testData)
   def test_login(self,datayaml):
     """
     登录测试
     :param datayaml: 加载login_data登录测试数据
     :return:
     """
     log = Log()
    log.info("当前执行测试用例ID-> {0} ; 测试点-> {1}".format(datayaml['id'],datayaml['detail']))
     # 调用登录方法
     self.user_login_verify(datayaml['data']['phone'],datayaml['data']['password'])
     po = login(self.driver)
     if datayaml['screenshot'] == 'phone_pawd_success':
       log.info("检查点-> {0}".format(po.user_login_success_hint()))
       self.assertEqual(po.user_login_success_hint(), datayaml['check'][0], "成功登录,返回实际结果是->: {0}".format(po.user_login_success_hint()))
       log.info("成功登录,返回实际结果是->: {0}".format(po.user_login_success_hint()))
       screenshot.insert_img(self.driver, datayaml['screenshot'] + '.jpg')
       log.info("-----> 开始执行退出流程操作")
       self.exit_login_check()
       po_exit = login(self.driver)
       log.info("检查点-> 找到{}元素,表示退出成功!".format(po_exit.exit_login_success_hint()))
       self.assertEqual(po_exit.exit_login_success_hint(), '注册',"退出登录,返回实际结果是->: {0}".format(po_exit.exit_login_success_hint()))
       log.info("退出登录,返回实际结果是->: {0}".format(po_exit.exit_login_success_hint()))
     else:
       log.info("检查点-> {0}".format(po.phone_pawd_error_hint()))
       self.assertEqual(po.phone_pawd_error_hint(),datayaml['check'][] , "异常登录,返回实际结果是->: {}".format(po.phone_pawd_error_hint()))
       log.info("异常登录,返回实际结果是->: {0}".format(po.phone_pawd_error_hint()))
       screenshot.insert_img(self.driver,datayaml['screenshot'] + '.jpg')
 
 if __name__=='__main__':
   unittest.main()

最后,在testcase目录下创建测试用例文件login_sta.py,采用ddt数据驱动读取yaml测试数据文件

综上所述,编写用例方法只需要按以上四个步骤创建->编写即可。

执行如下主程序,可看输出的实际结果。

#!/usr/bin/env python
# _*_ coding:utf-8 _*_
__author__ = 'YinJia'

import os,sys
sys.path.append(os.path.dirname(__file__))
from config import setting
import unittest,time
from package.HTMLTestRunner import HTMLTestRunner
from public.models.newReport import new_report
from public.models.sendmail import send_mail

# 测试报告存放文件夹,如不存在,则自动创建一个report目录
if not os.path.exists(setting.TEST_REPORT):os.makedirs(setting.TEST_REPORT + '/' + "screenshot")

def add_case(test_path=setting.TEST_DIR):
  """加载所有的测试用例"""
  discover = unittest.defaultTestLoader.discover(test_path, pattern='*_sta.py')
  return discover

def run_case(all_case,result_path=setting.TEST_REPORT):
  """执行所有的测试用例"""
  now = time.strftime("%Y-%m-%d %H_%M_%S")
  filename = result_path + '/' + now + 'result.html'
  fp = open(filename,'wb')
  runner = HTMLTestRunner(stream=fp,title='抽屉新热榜UI自动化测试报告',
              description='环境:windows 7 浏览器:chrome',
              tester='Jason')
  runner.run(all_case)
  fp.close()
  report = new_report(setting.TEST_REPORT) #调用模块生成最新的报告
  send_mail(report) #调用发送邮件模块

if __name__ =="__main__":
  cases = add_case()
  run_case(cases)

测试结果展示

HTML报告日志

python selenium自动化测试框架搭建的方法步骤

HTML报告点击截图,弹出截图

python selenium自动化测试框架搭建的方法步骤

测试报告通过的日志

python selenium自动化测试框架搭建的方法步骤

自动截图存放指定的目录

python selenium自动化测试框架搭建的方法步骤

邮件测试报告

python selenium自动化测试框架搭建的方法步骤

到此这篇关于python selenium自动化测试框架搭建的方法步骤的文章就介绍到这了,更多相关python selenium自动化测试框架搭建内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

作者:YinJia
出处:http://www.cnblogs.com/yinjia/

Python 相关文章推荐
Python内置的字符串处理函数整理
Jan 29 Python
详解Python的单元测试
Apr 28 Python
基于python脚本实现软件的注册功能(机器码+注册码机制)
Oct 09 Python
Python爬虫DNS解析缓存方法实例分析
Jun 02 Python
详谈Python中列表list,元祖tuple和numpy中的array区别
Apr 18 Python
python面向对象入门教程之从代码复用开始(一)
Dec 11 Python
pymongo中group by的操作方法教程
Mar 22 Python
tf.concat中axis的含义与使用详解
Feb 07 Python
python爬虫实现POST request payload形式的请求
Apr 30 Python
Python连接HDFS实现文件上传下载及Pandas转换文本文件到CSV操作
Jun 06 Python
BeautifulSoup中find和find_all的使用详解
Dec 07 Python
yolov5返回坐标的方法实例
Mar 17 Python
Python如何使用paramiko模块连接linux
Mar 18 #Python
Python selenium页面加载慢超时的解决方案
Mar 18 #Python
Python Selenium 设置元素等待的三种方式
Mar 18 #Python
Python多线程threading join和守护线程setDeamon原理详解
Mar 18 #Python
Python semaphore evevt生产者消费者模型原理解析
Mar 18 #Python
Python中remove漏删和索引越界问题的解决
Mar 18 #Python
Python集成开发工具Pycharm的安装和使用详解
Mar 18 #Python
You might like
一个可以删除字符串中HTML标记的PHP函数
2006/10/09 PHP
从wamp到xampp的升级之路
2015/04/08 PHP
变量在 PHP7 内部的实现(一)
2015/12/21 PHP
php表单加入Token防止重复提交的方法分析
2016/10/10 PHP
JS查看对象功能代码
2008/04/25 Javascript
jQuery判断元素是否是隐藏的代码
2011/04/24 Javascript
php对mongodb的扩展(初识如故)
2012/11/11 Javascript
Javascript小技巧之生成html元素
2014/05/15 Javascript
javascript操作符"!~"详解
2015/02/10 Javascript
jQuery原理系列-css选择器的简单实现
2016/06/07 Javascript
详解JSON1:使用TSQL查询数据和更新JSON数据
2016/11/21 Javascript
vue使用vue-cli快速创建工程
2017/07/28 Javascript
微信小程序基于本地缓存实现点赞功能的方法
2017/12/18 Javascript
Bootstrap 模态框自定义点击和关闭事件详解
2018/08/10 Javascript
微信小程序canvas.drawImage完全显示图片问题的解决
2018/11/30 Javascript
微信小程序学习笔记之表单提交与PHP后台数据交互处理图文详解
2019/03/28 Javascript
layer.js open 隐藏滚动条的例子
2019/09/05 Javascript
JavaScript实现文件下载并重命名代码实例
2019/12/12 Javascript
[00:34]TI7不朽珍藏III——地穴编织者不朽展示
2017/07/15 DOTA
python使用wmi模块获取windows下硬盘信息的方法
2015/05/15 Python
Python开发之快速搭建自动回复微信公众号功能
2016/04/22 Python
java中两个byte数组实现合并的示例
2018/05/09 Python
Flask框架信号用法实例分析
2018/07/24 Python
python ChainMap的使用和说明详解
2019/06/11 Python
django 通过URL访问上传的文件方法
2019/07/28 Python
Python利用FFT进行简单滤波的实现
2020/02/26 Python
Pycharm安装Qt Design快捷工具的详细教程
2020/11/18 Python
美国儿童玩具、装扮和玩偶商店:Magic Cabin
2018/09/02 全球购物
亚马逊巴西站:Amazon.com.br
2019/09/22 全球购物
个人收入证明范本
2014/01/12 职场文书
公司门卫岗位职责范本
2014/07/08 职场文书
安全生产先进个人总结
2015/02/15 职场文书
教师求职信怎么写
2015/03/20 职场文书
《惊弓之鸟》教学反思
2016/02/20 职场文书
python 使用Tensorflow训练BP神经网络实现鸢尾花分类
2021/05/12 Python
MySQL系列之六 用户与授权
2021/07/02 MySQL