selenium是一个针对web端项目的模拟鼠标和键盘操作的自动化测试工具,pytest是一个和unittest类似的自动化测试框架,但它比unittest更加方便,并且可以兼容unittest框架。

项目结构

  • common:存放公共方法,比如读取配置文件
  • config:存放配置文件。
  • logs:存放日志。
  • page:对selenium方法进行二次封装。
  • page_element:存放页面元素。
  • page_object:页面对象设计,将每个页面的功能均封装在这里,然后再testcase中调用,便于维护。
  • script:一些额外的脚本文件,我这里放的是检测页面元素格式的文件。
  • TestCase:存放测试用例。
  • utils:工具类。
  • conftest:pytest的配置文件。
  • run_case.py:配置生成allure报告的批处理文件,不影响整个测试用例的执行。

一、在config中创建config.ini和conf.py

config.ini写入host信息

[HOST]
HOST=http://rework.dfrobot.work/login

conf.py中配置文件目录、定位类型及邮箱信息

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import os,sys
from selenium.webdriver.common.by import By
sys.path.append((os.path.abspath(os.path.join(os.path.dirname(__file__), '../'))))
from utils.times import dt_strftimeclass ConfigManager(object):# 项目目录BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))# 页面元素目录ELEMENT_PATH = os.path.join(BASE_DIR, 'page_element')# 报告文件REPORT_FILE = os.path.join(BASE_DIR, 'report.html')# 元素定位的类型LOCATE_MODE = {'css': By.CSS_SELECTOR,'xpath': By.XPATH,'name': By.NAME,'id': By.ID,'class': By.CLASS_NAME}# 邮件信息EMAIL_INFO = {'username': 'xxxxxxxx@qq.com',  # 切换成你自己的地址'password': '开启smtp服务后的授权码',  #开启smtp服务后的授权码,在qq邮箱-设置-账户中可以开启smtp服务并获取授权码'smtp_host': 'smtp.qq.com','smtp_port': 465}# 收件人ADDRESSEE = ['xxxxxxxx@qq.com',]@property  #创建只读属性def log_file(self):"""日志目录"""log_dir = os.path.join(self.BASE_DIR, 'logs')if not os.path.exists(log_dir):os.makedirs(log_dir)return os.path.join(log_dir, '{}.log'.format(dt_strftime()))@propertydef ini_file(self):"""配置文件"""ini_file = os.path.join(self.BASE_DIR, 'config', 'config.ini')if not os.path.exists(ini_file):raise FileNotFoundError("配置文件%s不存在!" % ini_file)return ini_filecm = ConfigManager()
# if __name__ == '__main__':
#     print(cm.BASE_DIR)

二、common中创建readconfig.py和readelement.py

#readconfig.py
import configparser
import sys,os
sys.path.append((os.path.abspath(os.path.join(os.path.dirname(__file__), '../'))))from config.conf import cm HOST='HOST'class ReadConfig(object):'''配置文件'''def __init__(self):self.config=configparser.RawConfigParser()self.config.read(cm.ini_file,encoding='utf-8')def _get(self,section,option):'''获取'''return self.config.get(section,option)def _set(self, section, option, value):"""更新"""self.config.set(section, option, value)with open(cm.ini_file, 'w') as f:self.config.write(f)@propertydef url(self):return self._get(HOST, HOST)ini = ReadConfig()# if __name__ == '__main__':
#     print(ini.url)
#readelement.py
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import os,sys
import yaml
sys.path.append((os.path.abspath(os.path.join(os.path.dirname(__file__), '../'))))
from config.conf import cmclass Element(object):"""获取元素"""def __init__(self, name):self.file_name = '%s.yaml' % nameself.element_path = os.path.join(cm.ELEMENT_PATH, self.file_name)if not os.path.exists(self.element_path):raise FileNotFoundError("%s 文件不存在!" % self.element_path)with open(self.element_path, encoding='utf-8') as f:self.data = yaml.safe_load(f)def __getitem__(self, item):"""获取属性"""data = self.data.get(item)if data:name, value = data.split('==')return name, valueraise ArithmeticError("{}中不存在关键字:{}".format(self.file_name, item))# if __name__ == '__main__':
#     overview = Element('overview')
#     print(overview['首页按钮'])

三、在utils中添加工具类

#logger.py
import ctypes,sysSTD_INPUT_HANDLE = -10
STD_OUTPUT_HANDLE = -11
STD_ERROR_HANDLE = -12FOREGROUND_BLACK = 0x00 # black.
FOREGROUND_DARKBLUE = 0x01 # dark blue.
FOREGROUND_DARKGREEN = 0x02 # dark green.
FOREGROUND_DARKSKYBLUE = 0x03 # dark skyblue.
FOREGROUND_DARKRED = 0x04 # dark red.
FOREGROUND_DARKPINK = 0x05 # dark pink.
FOREGROUND_DARKYELLOW = 0x06 # dark yellow.
FOREGROUND_DARKWHITE = 0x07 # dark white.
FOREGROUND_DARKGRAY = 0x08 # dark gray.
FOREGROUND_BLUE = 0x09 # blue.
FOREGROUND_GREEN = 0x0a # green.
FOREGROUND_SKYBLUE = 0x0b # skyblue.
FOREGROUND_RED = 0x0c # red.
FOREGROUND_PINK = 0x0d # pink.
FOREGROUND_YELLOW = 0x0e # yellow.
FOREGROUND_WHITE = 0x0f # white.BACKGROUND_BLUE = 0x10 # dark blue.
BACKGROUND_GREEN = 0x20 # dark green.
BACKGROUND_DARKSKYBLUE = 0x30 # dark skyblue.
BACKGROUND_DARKRED = 0x40 # dark red.
BACKGROUND_DARKPINK = 0x50 # dark pink.
BACKGROUND_DARKYELLOW = 0x60 # dark yellow.
BACKGROUND_DARKWHITE = 0x70 # dark white.
BACKGROUND_DARKGRAY = 0x80 # dark gray.
BACKGROUND_BLUE = 0x90 # blue.
BACKGROUND_GREEN = 0xa0 # green.
BACKGROUND_SKYBLUE = 0xb0 # skyblue.
BACKGROUND_RED = 0xc0 # red.
BACKGROUND_PINK = 0xd0 # pink.
BACKGROUND_YELLOW = 0xe0 # yellow.
BACKGROUND_WHITE = 0xf0 # white.std_out_handle = ctypes.windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)
def set_cmd_text_color(color, handle=std_out_handle):Bool = ctypes.windll.kernel32.SetConsoleTextAttribute(handle, color)return Booldef reset():set_cmd_text_color(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE)def error(mess, end = '\n', flush = True):set_cmd_text_color(FOREGROUND_RED)print("[ERROR]", mess, end = end, flush = flush)reset()def warn(mess, end = '\n', flush = True):set_cmd_text_color(FOREGROUND_YELLOW)print("[WARN]", mess, end = end, flush = flush)reset()def info(mess, end = '\n', flush = True):set_cmd_text_color(FOREGROUND_GREEN)print("[INFO]", mess, end = end, flush = flush)reset()def write(mess, foregound = FOREGROUND_WHITE, background = FOREGROUND_BLACK, end = '\n', flush = True):set_cmd_text_color(foregound | background)print(mess, end = end, flush = flush)reset()# if __name__=='__main__':
#     info("======")
#times.py
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import time
import datetime
from functools import wrapsdef timestamp():"""时间戳"""return time.time()def dt_strftime(fmt="%Y%m"):"""datetime格式化时间:param fmt "%Y%m%d %H%M%S"""return datetime.datetime.now().strftime(fmt)def sleep(seconds=1.0):"""睡眠时间"""time.sleep(seconds)def running_time(func):"""函数运行时间"""@wraps(func) # @wraps 不改变使用装饰器原有函数的结构def wrapper(*args, **kwargs):start = timestamp()res = func(*args, **kwargs)print("校验元素done!用时%.3f秒!" % (timestamp() - start))return resreturn wrapper# if __name__ == '__main__':
#     print(dt_strftime("%Y-%m-%d %H:%M:%S"))

四、page中创建webpage.py

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
"""
selenium基类
本文件存放了selenium基类的封装方法
"""
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
import sys,os
sys.path.append((os.path.abspath(os.path.join(os.path.dirname(__file__), '../'))))from config.conf import cm
from utils.times import sleep
from utils import loggerclass WebPage(object):"""selenium基类"""def __init__(self, driver):# self.driver = webdriver.Chrome()self.driver = driverself.timeout = 20self.wait = WebDriverWait(self.driver, self.timeout)def get_url(self, url):"""打开网址并验证"""self.driver.set_page_load_timeout(60)try:self.driver.get(url)self.driver.implicitly_wait(10)logger.info("打开网页:%s" % url)except TimeoutException:raise TimeoutException("打开%s超时请检查网络或网址服务器" % url)@staticmethoddef element_locator(func, locator):"""元素定位器"""name, value = locatorreturn func(cm.LOCATE_MODE[name], value)def find_element(self, locator):"""寻找单个元素"""return WebPage.element_locator(lambda *args: self.wait.until(EC.presence_of_element_located(args)), locator)  #presence_of_element_located((By.ID,"acdid")) 显式等待def get_attrib(self, locator,value):"""获取元素属性"""logger.info("获取属性")ele=self.find_element(locator)sleep(0.5)return ele.get_attribute(value)# js='document.querySelector("#质检表_返工单号").value'# self.driver.execute_script(js)def find_elements(self, locator):"""查找多个相同的元素"""return WebPage.element_locator(lambda *args: self.wait.until(EC.presence_of_all_elements_located(args)), locator)def find_element_drag(self,locator):target = self.find_element(locator)self.driver.execute_script("arguments[0].scrollIntoView();", target) #拖动到可见的元素去def elements_num(self, locator):"""获取相同元素的个数"""number = len(self.find_elements(locator))logger.info("相同元素:{}".format((locator, number)))return numberdef input_text(self, locator, txt):"""输入(输入前先清空)"""sleep(0.5)ele = self.find_element(locator)ele.clear()ele.send_keys(txt)sleep(0.5)logger.info("输入文本:{}".format(txt))def input_enter(self, locator):"""回车、tab等键入"""ele = self.find_element(locator)ele.send_keys(Keys.ENTER)def is_click(self, locator):"""点击"""self.find_element(locator).click()sleep()logger.info("点击元素:{}".format(locator))def element_text(self, locator):"""获取当前的text"""_text = self.find_element(locator).textlogger.info("获取文本:{}".format(_text))return _textdef hold_on(self,locator):#定位到要悬停的元素move = self.find_element(locator)#对定位到的元素执行悬停操作ActionChains(self.driver).move_to_element(move).perform()sleep()logger.info("悬停元素:{}".format(locator))def screen_scoll(self):self.driver.execute_script('window.scrollBy(0, 300)')sleep(1)@propertydef get_source(self):"""获取页面源代码"""return self.driver.page_sourcedef refresh(self):"""刷新页面F5"""self.driver.refresh()self.driver.implicitly_wait(30)

五、page_element下创建login.yaml文件,记录元素位置

账号: 'xpath==//*[@id="login_username"]'
密码: 'xpath==//*[@id="login_password"]'
登录: 'xpath==//*[@id="login"]/div[4]/div/div/div/button'

六、在script下创建inspect.py

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import os,sys
import yaml
sys.path.append((os.path.abspath(os.path.join(os.path.dirname(__file__), '../'))))
from config.conf import cm
from utils.times import running_time@running_time
def inspect_element():"""检查所有的元素是否正确只能做一个简单的检查"""for files in os.listdir(cm.ELEMENT_PATH):_path = os.path.join(cm.ELEMENT_PATH, files)with open(_path, encoding='utf-8') as f:data = yaml.safe_load(f)for k in data.values():try:pattern, value = k.split('==')except ValueError:raise Exception("{} : {} 元素表达式中没有`==`".format(_path,k))if pattern not in cm.LOCATE_MODE:raise Exception('%s中元素【%s】没有指定类型' % (_path, k)else:assert value, '%s中元素【%s】类型与值不匹配' % (_path, k)if __name__ == '__main__':inspect_element()

七、在page_object下创建login.py

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import sys,os
sys.path.append((os.path.abspath(os.path.join(os.path.dirname(__file__), '../'))))from page.webpage import WebPage
from common.readelement import Elementlogin = Element('login')  #获取login.yamlclass LoginPage(WebPage):'''登录'''def input_user(self,content):self.input_text(login['账号'],content)def input_pwd(self,content):self.input_text(login['密码'],content)def btn_login(self):self.is_click(login['登录'])

八、在Test_case中创建测试用例test_001_main.py

# -*- coding:utf-8 -*-
import re,os,sys
import allure
sys.path.append((os.path.abspath(os.path.join(os.path.dirname(__file__), '../'))))from utils.times import sleep
import pytest
from pytest import assume
from utils import logger
from utils.times import sleep,dt_strftime
from common.readconfig import ini
from page_object.login import LoginPage@allure.story("测试主流程:顺利通过的全套流程")
class TestOverview:@allure.step("登录")@pytest.fixture(scope="function")def login(self, drivers):"""登录"""login = LoginPage(drivers)login.get_url(ini.url)login.input_user('xxxxx')login.input_pwd('xxxxxx')login.btn_login()@allure.step("登录后的操作")@pytest.mark.usefixtures("login")def test_001(self, drivers):"""登录后操作"""    print("登录后操作")#pytest会自动搜索测试用例,不用在这里调用,这里只是为了单个文件调试的时候使用
# if __name__  == '__main__' :
#     pytest.main(['test_001_main.py','-s'])#'--capture=no'

九、在根目录下添加conftest.py

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import pytest
from py.xml import html
from selenium import webdriverdriver = None@pytest.fixture(scope='session', autouse=True)
def drivers(request):global driverif driver is None:driver = webdriver.Chrome()driver.maximize_window()def fn():driver.quit()request.addfinalizer(fn)return driver@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item):"""当测试失败的时候,自动截图,展示到html报告中:param item:"""pytest_html = item.config.pluginmanager.getplugin('html')outcome = yieldreport = outcome.get_result()report.description = str(item.function.__doc__)extra = getattr(report, 'extra', [])def pytest_html_results_table_header(cells):cells.insert(1, html.th('用例名称'))cells.insert(2, html.th('Test_nodeid'))cells.pop(2)def pytest_html_results_table_html(report, data):if report.passed:del data[:]data.append(html.div('通过的用例未捕获日志输出.', class_='empty log'))def _capture_screenshot():'''截图保存为base64:return:'''return driver.get_screenshot_as_base64()

这里要注意创建的drivers函数,因为添加了@pytest.fixture(scope=‘session’, autouse=True)修饰器,这个函数会在session级别的testcase中生效 ,并返回webdriver。在Testoverview类里,每个testcase,如test_001中都传递了一个“drivers”参数,这个drivers就是调用的conftest中的drivers函数。

十、在根目录下新建pytest.ini文件,对pytest执行过程中的操作做全局控制

[pytest]
addopts = --html=report.html --self-contained-html

十一、执行

在根目录下,在cmd中直接输入pytest,会自动搜索测试用例,执行完成后在根目录下输出html报告。

十二、在utils下创建send_mail.py发送邮件

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import zmail
import sys,os
sys.path.append((os.path.abspath(os.path.join(os.path.dirname(__file__), '../'))))from config.conf import cmdef send_report():"""发送报告"""with open(cm.REPORT_FILE, encoding='utf-8') as f:content_html = f.read()try:mail = {'from': 'xxxxxxxxxx@qq.com','subject': '测试报告','content_html': content_html,'attachments': [cm.REPORT_FILE, ]}server = zmail.server(*cm.EMAIL_INFO.values())server.send_mail(cm.ADDRESSEE, mail)print("测试邮件发送成功!")except Exception as e:print("Error: 无法发送邮件,{}!", format(e))if __name__ == "__main__":'''请先在config/conf.py文件设置QQ邮箱的账号和密码'''send_report()

十二、生成allure报告

需要先安装allure,这里在我的另一个文章python3+unittest+selenium自动化实战
中有详细介绍,但是在那篇文章里,使用了命令行的方式来打开allure server,需要输入多次命令。这里为了简化操作,将所有命令写入一个py文件中,我们只需要运行这个py文件,就可以执行测试用例,并且自动打开生成的allure报告。
因此,在根目录下创建一个run_case.py文件

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import sys
import subprocessWIN = sys.platform.startswith('win')def main():"""主函数"""steps = ["venv\\Script\\activate" if WIN else "source venv/bin/activate","pytest --alluredir allure-results --clean-alluredir","allure generate allure-results -c -o allure-report","allure open allure-report"]for step in steps:subprocess.run("call " + step if WIN else step, shell=True)if __name__ == "__main__":main()

python+pytest+selenium+allure实战相关推荐

  1. Pytest + Selenium + Allure + Jenkins搭建简单自动化框架

    1.环境搭建 1.1 安装python,配置环境变量 下载地址:https://www.python.org/downloads/下载需要的版本(3.7以上) 1.2安装pycharm 下载地址:ht ...

  2. python pytest和allure环境_【转载】Python—Pytest+Allure定制报告

    Allure Test Report 一款测试报告框架,不仅报告美观,而且方便CI集成. 一.环境配置 安装Python依赖库: pip3 install pytest pip3 install py ...

  3. 从零开始搭建一个简单的ui自动化测试框架02(pytest+selenium+allure)

    二.先搭一个架子 在我还是小白连py语法都不太熟悉的时候,经常在网上看关于自学ui自动化测试的博客,最熟悉的套路莫过于先给你介绍一下selenium的各个api,然后写一套代码去登陆微博或者百度什么的 ...

  4. Python之selenium爬虫实战爬取表情包

    文章目录 前言 寻找目标 思路分析 步骤实现 爬取过程 实现效果 以下是全部代码 提示:以下是本篇文章正文内容,下面案例可供参考 前言 大家点进来看selenium操作,那么你应该知道 seleniu ...

  5. Pytest结合allure插件生成alluer测试报告【详细实战篇】

    Pytest结合allure插件生成allure测试报告[详细实战篇] 一.Allure插件介绍 二.Allure环境搭建与安装 2.1 搭建步骤 2.2 生成allure测试报告实例 三.Allur ...

  6. pytest测试实战 电子书_电子书丨Selenium 3+Python 3自动化测试项目实战:从菜鸟到高手...

    ▊<Selenium 3+Python 3自动化测试项目实战:从菜鸟到高手> 田春成 著 电子书售价:39.5元 2019年9月出版 Selenium是目前非常流行的一种自动化测试工具.本 ...

  7. Pytest和Allure测试框架-超详细版+实战

    加我微信:hz223336,领取pytest视频 文章目录 一:pytest的介绍,安装,框架结构,执行方式 1,特点 2,安装 3, pytest的框架结构 4,执行方式 二:Pytest -断言. ...

  8. pytest+allure实战

    pytest+allure实战 pytest+allure实战 基本架构 Login.py test.py run_all_case.py 测试报告 pytest+allure实战 写之前,说一下自己 ...

  9. python接口测试框架选择之pytest+yaml+Allure

    前言 2021年,部门的测试组尝试着用jmeter做简单的接口测试,使用工具就需要遵守工具的很多规则,并且jmeter对需要写辅助测试代码的场景不友好.2022年,改为用python写接口测试.在经过 ...

  10. Python+Requests+Pytest+YAML+Allure实现接口自动化

    作者:wintest 链接:https://www.cnblogs.com/wintest/p/13423231.html 本项目实现接口自动化的技术选型:Python+Requests+Pytest ...

最新文章

  1. python使用退格键时出现^H解决方法
  2. spring单元测试报错:Failed to load ApplicationContext 的解决方法
  3. AMD CPU 看清楚
  4. Scala - 快速学习08 - 函数式编程:高阶函数
  5. [转载]dynamic的小坑--RuntimeBinderException:“object”未包含“xxx”的定义
  6. c语言 交互式电子白板案例,交互式电子白板教学案例
  7. 微型计算机指令系统例题,微机原理复习题(指令系统)
  8. C++ 中的数学计算函数
  9. 开发一款浏览器内核需要学习哪些方面的知识?
  10. 从零实现深度学习框架——实现常见运算的计算图(下)
  11. python聚类分析实例_Biopython - 聚类分析
  12. matlab 偏最小二乘 光谱,【GreenSim原创】偏最小二乘法的Matlab源码
  13. Redis教程--基于docker搭建redis文档服务
  14. 同步助手java_QQ同步助手Java版发布:八大手机平台全覆盖
  15. 可视化布局html5
  16. windows MD5 校验
  17. web前端性能(一)
  18. Visifire图表控件的使用
  19. 关于password导致input弹出浏览器记住密码弹框问题
  20. 量化交易系统-算法交易模块设计-10

热门文章

  1. 误删照片恢复软件推荐,轻松恢复照片好帮手!
  2. C语言研究生科研奖励系统,关于做好2020年研究生科研成果统计、奖励工作的通知...
  3. JVM内存管理与调优排障
  4. 从入门到入土:学习|实例练手|获取裁判决定网|Selenium出击|绕过反爬机制|实现批量下载裁决书|狗头保命|仅用于学习交流|Selenium自动化操作
  5. IDEA多次运行同一个java程序的设置方法
  6. uni-app/vue 文字转语音朗读(附小程序语音识别和朗读)
  7. VMware 导入 CentOS 7
  8. android识别人脸并放贴纸,超简单集成HMS ML Kit 人脸检测实现可爱贴纸
  9. 初识CTF Day3 CTFHUBRCE
  10. 4星|《为何要提升员工的幸福感》:幸福感最高的三大要素是:感恩的习惯、开放的心态,从错误中学习的能力,以及拥有自己的人生意义...