create the new project and then I will update forever

This commit is contained in:
ALEX.YI 2023-08-16 01:55:40 +08:00
parent 7463c87d55
commit f51c3052a5
56 changed files with 1857 additions and 2 deletions

View File

@ -1,8 +1,8 @@
# 小北原创UI自动化高阶框架selenium+pytest # 小北原创UI自动化高阶框架selenium+pytest
#### 介绍 #### 介绍
免费开源的高级selenium自动化框架开箱即用复用性高可适用任何web项目中 免费开源的高级selenium自动化框架开箱即用复用性高可适用任何web项目中
原创作者:小北 原创作者:小北
微信xiaobei_upup 微信xiaobei_upup
#### 软件架构 #### 软件架构
@ -37,3 +37,13 @@
4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目
5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) 5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) 6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
# run testcases
pytest
# run allure report
pytest --alluredir ./reports testcases/pytest/test_search_baidu_index.py
# start up serve to browse report
allure serve ./reports
# run pytest to choose which you want to run
pytest -m test

0
common/__init__.py Normal file
View File

158
common/action.py Normal file
View File

@ -0,0 +1,158 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
-------------------------------------------------
File Name
Description :
Author : xiaobei
CreateDate
wechatxiaobei_upup
-------------------------------------------------
"""
import math
import os
import time
def swipe_view_by_percent(driver, x1, y1, x2, y2):
"""
滑动函数百分比类型
Args:
driver: appium驱动webdriver类型
x1: 滑动起点坐标的横坐标占屏幕宽度的百分比0<x1<1
y1: 滑动起点坐标的纵坐标占屏幕高度的百分比0<y1<1
x2: 滑动终点坐标的横坐标占屏幕宽度的百分比0<x2<1
y2: 滑动终点坐标的纵坐标占屏幕高度的百分比0<y2<1
Returns:
"""
width = driver.get_window_size()['width']
height = driver.get_window_size()['height']
detal_x = abs(x2 - x1) * width # x坐标差值
detal_y = abs(y2 - y1) * height # y坐标差值
t = math.sqrt(detal_x ** 2 + detal_y ** 2) * 5
driver.swipe(x1 * width, y1 * height, x2 * width, y2 * height, t)
time.sleep(1)
def swipe_view(driver, start_point, end_point):
is_ele_num = 0
if is_appium_element(start_point):
is_ele_num += 1
if is_appium_element(end_point):
is_ele_num += 1
start_point_coordinate = get_point_coordinate(start_point)
# print(start_point_coordinate)
end_point_coordinate = get_point_coordinate(end_point)
# print(end_point_coordinate)
duration = math.sqrt((end_point_coordinate['x'] - start_point_coordinate['x']) ** 2 + (
end_point_coordinate['y'] - start_point_coordinate['y']) ** 2) * 5
if is_ele_num == 2:
driver.scroll(start_point, end_point, duration)
else:
driver.swipe(start_point_coordinate['x'], start_point_coordinate['y'], end_point_coordinate['x'],
end_point_coordinate['y'], duration)
def swipe_view_time(driver, start_point, end_point, duration):
is_ele_num = 0
if is_appium_element(start_point):
is_ele_num += 1
if is_appium_element(end_point):
is_ele_num += 1
start_point_coordinate = get_point_coordinate(start_point)
# print(start_point_coordinate)
end_point_coordinate = get_point_coordinate(end_point)
# print(end_point_coordinate)
# duration = math.sqrt((end_point_coordinate['x'] - start_point_coordinate['x']) ** 2 + (
# end_point_coordinate['y'] - start_point_coordinate['y']) ** 2) * 5
if is_ele_num == 2:
driver.scroll(start_point, end_point, duration)
else:
driver.swipe(start_point_coordinate['x'], start_point_coordinate['y'], end_point_coordinate['x'],
end_point_coordinate['y'], duration)
def is_appium_element(element):
import appium
if isinstance(element, appium.webdriver.webelement.WebElement):
return True
else:
return False
def get_point_coordinate(ele):
import re
coordinate = {}
if is_appium_element(ele):
temp = ele.get_attribute('bounds')
print(temp)
searchObj = re.search(r'\[(\d+),(\d+)\]\[(\d+),(\d+)\]', temp)
if searchObj:
x_1 = int(searchObj.group(1))
y_1 = int(searchObj.group(2))
x_2 = int(searchObj.group(3))
y_2 = int(searchObj.group(4))
coordinate['x'] = (x_2 - x_1) / 2 + x_1
coordinate['y'] = (y_2 - y_1) / 2 + y_1
else:
# TODO 抛出异常
pass
elif isinstance(ele, tuple):
coordinate['x'] = ele[0]
coordinate['y'] = ele[1]
else:
# TODO 抛出异常
pass
return coordinate
def get_zhibiao_button(ele):
import re
coordinate = {}
temp = ele.get_attribute('bounds')
print(temp)
searchObj = re.search(r'\[(\d+),(\d+)\]\[(\d+),(\d+)\]', temp)
if searchObj:
x_2 = int(searchObj.group(3))
y_2 = int(searchObj.group(4))
coordinate['x'] = x_2 / 2
coordinate['y'] = y_2 - 100
else:
# TODO 抛出异常
pass
print([coordinate['x'], coordinate['y']])
return [(coordinate['x'], coordinate['y'])]
def close_open_app(driver, bundld_id, use_key_element=False, key_element=None):
"""
app关闭重启函数
Args:
driver: appium驱动
bundld_id: 要操作的app包名
use_key_element: 是否校验关键元素
key_element: 关键元素
Returns:
"""
time.sleep(4)
if driver.query_app_state(bundld_id) == 4:
if use_key_element:
if key_element is not None:
return True
try:
staus = driver.terminate_app(bundld_id, timeout=5000)
except:
driver.close_app() # 该函数仅能关闭初始化指定的app
driver.activate_app(bundld_id)
if driver.query_app_state(bundld_id) == 4:
return True
else:
return False

51
common/async.py Normal file
View File

@ -0,0 +1,51 @@
"""
-------------------------------------------------
File Name
Description :异步下载操作
Author : xiaobei
CreateDate
wechatxiaobei_upup
-------------------------------------------------
"""
import asyncio
import aiohttp
from multiprocessing import Pool
async def download_link(link):
async with aiohttp.ClientSession() as session:
async with session.get(link) as resp:
if resp.status == 200:
content = await resp.read()
# 保存下载内容到本地
...
async def download_links(links):
tasks = []
for link in links:
tasks.append(asyncio.ensure_future(download_link(link)))
await asyncio.gather(*tasks)
def download_in_pool(links):
loop = asyncio.get_event_loop()
loop.run_until_complete(download_links(links))
url = 'https://example.com/example.m3u8'
r = requests.get(url)
if r.status_code == 200:
# 解析m3u8文件获取下载链接列表
m3u8_content = r.content.decode()
download_links = [link.strip() for link in m3u8_content.split('\n') if link.endswith('.ts')]
# 将下载链接列表按照线程数均分为16份
links_list = [download_links[i:i + 16] for i in range(0, len(download_links), 16)]
# 使用进程池来管理多个进程
pool = Pool(processes=16)
# 在进程池中提交异步下载任务
for links in links_list:
pool.apply_async(download_in_pool, (links,))
# 等待所有任务完成
pool.close()
pool.join()

31
common/file_upload.py Normal file
View File

@ -0,0 +1,31 @@
"""
-------------------------------------------------
File Name
Description :
Author : xiaobei
CreateDate
wechatxiaobei_upup
-------------------------------------------------
"""
import os
import time
from pywinauto import Desktop
def upload_files(file_path, *args):
"""
:param file_path: files path which geometry files in directory
:param args: file name about geometry files
"""
app = Desktop()
# select the explorer file popover
shon = app["Select Geometry Files"]
# accept the one or more files to write into input box
for i in args:
send_keys('"{}"'.format(i))
url_tab = shon["Toolbar3"]
url_tab.click()
# input url of the geometry files
send_keys(file_path)
send_keys("{VK_RETURN}")
time.sleep(1)
shon["打开(O)"].click_input()

278
common/imageColor.py Normal file
View File

@ -0,0 +1,278 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
-------------------------------------------------
File Name image
Description : 颜色识别的工具类
Author : xiaobei
CreateDate 2023/3/12 10:17
-------------------------------------------------
"""
import cv2
import numpy as np
import collections
import tempfile
import os, random
import time
from PIL import Image
#pillow
def getColorList():
"""
颜色范围初始化
Returns:
"""
dict = collections.defaultdict(list)
# 黑色
lower_black = np.array([0, 0, 0])
upper_black = np.array([180, 255, 55])
color_list = []
color_list.append(lower_black)
color_list.append(upper_black)
dict['black'] = color_list
# 灰色
lower_gray = np.array([0, 0, 55])
upper_gray = np.array([180, 43, 220])
color_list = []
color_list.append(lower_gray)
color_list.append(upper_gray)
dict['gray'] = color_list
# 白色
lower_white = np.array([0, 0, 221])
upper_white = np.array([180, 30, 255])
color_list = []
color_list.append(lower_white)
color_list.append(upper_white)
dict['white'] = color_list
# 红色
lower_red = np.array([156, 43, 46])
upper_red = np.array([180, 255, 255])
color_list = []
color_list.append(lower_red)
color_list.append(upper_red)
dict['red'] = color_list
# 红色2
lower_red = np.array([0, 43, 46])
upper_red = np.array([10, 255, 255])
color_list = []
color_list.append(lower_red)
color_list.append(upper_red)
dict['red2'] = color_list
# 橙色
lower_orange = np.array([11, 43, 46])
upper_orange = np.array([25, 255, 255])
color_list = []
color_list.append(lower_orange)
color_list.append(upper_orange)
dict['orange'] = color_list
# 黄色
lower_yellow = np.array([26, 43, 46])
upper_yellow = np.array([34, 255, 255])
color_list = []
color_list.append(lower_yellow)
color_list.append(upper_yellow)
dict['yellow'] = color_list
# 绿色
lower_green = np.array([35, 43, 46])
upper_green = np.array([77, 255, 255])
color_list = []
color_list.append(lower_green)
color_list.append(upper_green)
dict['green'] = color_list
# 青色
lower_cyan = np.array([78, 43, 46])
upper_cyan = np.array([99, 255, 255])
color_list = []
color_list.append(lower_cyan)
color_list.append(upper_cyan)
dict['cyan'] = color_list
# 蓝色
lower_blue = np.array([100, 43, 46])
upper_blue = np.array([124, 255, 255])
color_list = []
color_list.append(lower_blue)
color_list.append(upper_blue)
dict['blue'] = color_list
# 紫色
lower_purple = np.array([125, 43, 46])
upper_purple = np.array([155, 255, 255])
color_list = []
color_list.append(lower_purple)
color_list.append(upper_purple)
dict['purple'] = color_list
return dict
def get_color(image_path, shield_list=None):
"""
获取指定图片的主体颜色有且只会返回一种颜色
Args:
image_path:待处理的图片路径
shield_list: 需要屏蔽掉的颜色列表
可选参数有'black'(黑色),'gray'(灰色),'white'(白色),'red'(红色),'red2'(红色2),'orange'(橙色),'yellow'(黄色),
'green'(绿色),'cyan'(青色),'blue'(蓝色),'purple'(紫色)
Returns: 返回指定图片的主体颜色的字符串例如'black'如果是纯色图片且该颜色被屏蔽则返回None
"""
if shield_list is None:
shield_list = []
image_frame = cv2.imread(image_path)
hsv = cv2.cvtColor(image_frame, cv2.COLOR_BGR2HSV)
maxsum = -100
color = None
color_dict = getColorList()
for black_color in shield_list:
if black_color in color_dict:
color_dict.pop(black_color)
tmp_num = random.randint(0, 99)
color_dict_len = len(color_dict)
same_pic = 0
for d in color_dict:
mask = cv2.inRange(hsv, color_dict[d][0], color_dict[d][1])
# cv2.imwrite(os.path.join(tempfile.gettempdir(), f"{d}_{tmp_num}.jpg"), mask)
binary = cv2.threshold(mask, 127, 255, cv2.THRESH_BINARY)[1]
binary = cv2.dilate(binary, None, iterations=0)
cnts, hiera = cv2.findContours(binary.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
sum = 0
for c in cnts:
sum += cv2.contourArea(c)
if sum > maxsum:
maxsum = sum
color = d
same_pic = 1
elif sum == maxsum:
same_pic += 1
if same_pic == color_dict_len:
color = None
return color
def get_screenshot_by_element(driver, element):
"""
获取指定元素的截图(android)
Args:
driver: appium驱动
element: 要获取截图的元素
Returns:
"""
# 先截取整个屏幕,存储至系统临时目录下
TEMP_FILE = os.path.join(tempfile.gettempdir(), f"temp_screen_{int(time.time()*1000)}.png")
driver.get_screenshot_as_file(TEMP_FILE)
# 获取元素四角坐标
location = element.location
size = element.size
# 截取图片
TEMP_FILE_2 = os.path.join(tempfile.gettempdir(), f"temp_screen_{int(time.time()*1000)}.png")
cut_image(TEMP_FILE, location["x"], location["y"], location["x"] + size["width"], location["y"] + size["height"],
TEMP_FILE_2)
return TEMP_FILE_2
def assert_color(driver, element):
image_path = get_screenshot_by_element(driver, element)
res = get_color(cv2.imread(image_path))
if res == 'red' or res == 'red2':
return 'red'
elif res == 'green':
return 'green'
def get_screenshot_by_percent(driver, out_image_path=None, x1=0, y1=0, x2=1, y2=1):
"""
根据百分比截取屏幕截图
Args:
driver: appium驱动
out_image_path: 截图结果输出路径
x1: 所要截取区域左上角坐标的横坐标占屏幕宽度的百分比0<=x1<=1
y1: 所要截取区域左上角坐标的纵坐标占屏幕宽度的百分比0<=y1<=1
x2: 所要截取区域右上角坐标的横坐标占屏幕宽度的百分比0<=x2<=1
y2: 所要截取区域右下角坐标的纵坐标占屏幕宽度的百分比0<=y2<=1
Returns:截图结果存放路径
"""
# 先截取整个屏幕,存储至系统临时目录下
TEMP_FILE = os.path.join(tempfile.gettempdir(), f"temp_screen_{int(time.time()*1000)}.png")
driver.get_screenshot_as_file(TEMP_FILE)
# 截取图片
out_image_path = cut_image(TEMP_FILE, x1, y1, x2, y2, out_image_path, use_percent=True)
return out_image_path
def cut_image(input_image_path, x1, y1, x2, y2, out_image_path, use_percent=False):
"""
图片截取函数
Args:
input_image_path: 要处理的图片原始路径
x1:要截取范围左上角的横坐标或百分比
y1:要截取范围左上角的纵坐标或百分比
x2:要截取范围右下角的横坐标或百分比
y2:要截取范围右下角的纵坐标或百分比
out_image_path: 结果输出的图片路径
use_percent: 是否启用百分比模式默认为不启用
"""
if not out_image_path:
out_image_path = os.path.join(tempfile.gettempdir(), f"temp_screen_{int(time.time()*1000)}.png")
image = Image.open(input_image_path) # 读取原始图片
if use_percent:
size = image.size # 获取图片的尺寸
x1 = size[0] * x1
y1 = size[1] * y1
x2 = size[0] * x2
y2 = size[1] * y2
box = (x1, y1, x2, y2) # 设定截取区域
newImage = image.crop(box) # 进行截取操作
newImage.save(out_image_path) # 保存截取结果
return out_image_path
def get_color_by_element(driver, element, shield_list=None):
"""
获取指定元素的主体颜色
Args:
driver: appium驱动
element: 要获取颜色的元素
shield_list: 要屏蔽的颜色列表
Returns:
"""
return get_color(get_screenshot_by_element(driver, element), shield_list)
if __name__ == '__main__':
# filename = 'ios_h.png'
# filename = 'C:\\Users\\viruser.v-desktop\\Desktop\\tmp\\white.png'
filename = 'D:\\img\\323232.png'
# filename = 'ios_l.png'
# filename = 'and_h.png'
# filename = 'and_l.png'
print(get_color(filename, shield_list=['white']))
# file_list = ['ios_h.png', 'ios_l.png', 'and_h.png', 'and_l.png']
# for i in file_list:
# frame = cv2.imread(i)
# print(get_color(frame))

43
common/image_identify.py Normal file
View File

@ -0,0 +1,43 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
# ==================================================
# @Time : 2023-06-23 13:55:01
# @Author : 小北
# @微信xiaobei_upup
# @Email : 2211484376@qq.com
# @QQ群585971269
# @微信群:可加我微信拉你
# @Site : 苏州
# @Desc : 免费验证码识别
# @跳槽辅导:初中高级测试跳槽涨薪就业辅导,详情咨询微信
# @求职辅导:初中高级测试求职就业速成辅导,详情咨询微信
# @特色: 小北独创VIP面试速成课程拿下心仪的offer
# @如何付款:小程序付款 or 支付宝 or 微信
# ==================================================
"""
from PIL import Image
import ddddocr
def image_identify(driver, ele, whole_name, crop_name):
"""
:param driver: 浏览器驱动
:param ele: 验证码的元素
:param whole_name: 整个页面的截图名字
:param crop_name: 页面中对于验证码的抠图名字
:return: 返回验证码识别出来的字符串
"""
driver.save_screenshot(whole_name)
left = ele.location['x'] + 220
top = ele.location['y'] + 90
right = ele.size['width'] + left + 85
height = ele.size['height'] + top + 15
img = Image.open(whole_name).crop((left, top, right, height))
img.save(crop_name)
ocr = ddddocr.DdddOcr()
with open(crop_name, 'rb') as f:
image = f.read()
res = ocr.classification(image)
print(res)
return res

52
common/logger.py Normal file
View File

@ -0,0 +1,52 @@
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import logging
from config.conf import cm
"""
-------------------------------------------------
File Name
Description :
Author : xiaobei
CreateDate
wechatxiaobei_upup
-------------------------------------------------
"""
"""
日志大家应该都很熟悉这个名词就是记录代码中的动作
这个文件就是我们用来在自动化测试过程中记录一些操作步骤的
"""
class Log:
def __init__(self):
self.logger = logging.getLogger()
if not self.logger.handlers:
self.logger.setLevel(logging.DEBUG)
# 创建一个handle写入文件
fh = logging.FileHandler(cm.log_file, encoding='utf-8')
fh.setLevel(logging.INFO)
# 创建一个handle输出到控制台
ch = logging.StreamHandler()
ch.setLevel(logging.INFO)
# 定义输出的格式
formatter = logging.Formatter(self.fmt)
fh.setFormatter(formatter)
ch.setFormatter(formatter)
# 添加到handle
self.logger.addHandler(fh)
self.logger.addHandler(ch)
@property
def fmt(self):
return '%(levelname)s\t%(asctime)s\t[%(filename)s:%(lineno)d]\t%(message)s'
log = Log().logger
if __name__ == '__main__':
log.info('hello world')

25
common/login.py Normal file
View File

@ -0,0 +1,25 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
# ==================================================
# @Time : 2023-08-12 19:20:47
# @Author : 小北
# @微信xiaobei_upup
# @Email : 2211484376@qq.com
# @QQ群585971269
# @微信群:可加我微信拉你
# @Site : 苏州
# @Desc :
# @跳槽辅导:初中高级测试跳槽涨薪面试辅导,详情咨询微信
# @求职辅导:初中高级测试求职面试辅导,详情咨询微信
# @特色: 小北独创VIP面试速成课程拿下心仪的offer
# @如何付款先拿offer再付款只需交定金相互信任无套路
# ==================================================
"""
from page.wps_index.login_page import wps_login
def login(driver, username, password):
login1 = wps_login(driver)
login1

10
common/logout.py Normal file
View File

@ -0,0 +1,10 @@
"""
-------------------------------------------------
File Name
Description :
Author : xiaobei
CreateDate
wechatxiaobei_upup
-------------------------------------------------
"""

49
common/readconfigini.py Normal file
View File

@ -0,0 +1,49 @@
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import configparser
from config.conf import cm
"""
-------------------------------------------------
File Name
Description :
Author : xiaobei
CreateDate
wechatxiaobei_upup
-------------------------------------------------
"""
"""
读取这个conf.ini配置文件以使用里面的信息
###
可以看到我们用python内置的configparser模块对config.ini文件进行了读取
对于url值的提取我使用了高阶语法@property属性值写法更简单
"""
HOST = 'HOST'
class ReadConfig(object):
"""配置文件"""
def __init__(self):
self.config = configparser.RawConfigParser() # 当有%的符号时请使用Raw读取
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)
@property
def url(self):
return self._get(HOST, HOST)
ini = ReadConfig()
if __name__ == '__main__':
print(ini.url)

45
common/screenshots.py Normal file
View File

@ -0,0 +1,45 @@
"""
-------------------------------------------------
File Name
Description :
Author : xiaobei
CreateDate
wechatxiaobei_upup
-------------------------------------------------
"""
import os
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import pytest
class ScreenshotUtil:
def __init__(self):
self.driver = None
def create_driver(self):
options = Options()
options.add_argument('--headless')
options.add_argument('--disable-gpu')
self.driver = webdriver.Chrome(chrome_options=options)
def take_screenshot(self, name):
if not self.driver:
self.create_driver()
self.driver.get_screenshot_as_file(os.path.join(os.getcwd(), f"{name}.png"))
def close_driver(self):
if self.driver:
self.driver.quit()
@pytest.fixture()
def screenshot():
screenshot_util = ScreenshotUtil()
yield screenshot_util
screenshot_util.close_driver()
def test_take_screenshot(screenshot):
screenshot.take_screenshot("test")
assert os.path.isfile(os.path.join(os.getcwd(), "test.png"))

36
common/send_mail.py Normal file
View File

@ -0,0 +1,36 @@
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
"""
-------------------------------------------------
File Name
Description :
Author : xiaobei
CreateDate
wechatxiaobei_upup
-------------------------------------------------
"""
import zmail
from config.conf import cm
def send_report():
"""发送报告"""
with open(cm.REPORT_FILE, encoding='utf-8') as f:
content_html = f.read()
try:
mail = {
'from': '2211484376@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()

53
common/stock_refresh.py Normal file
View File

@ -0,0 +1,53 @@
"""
-------------------------------------------------
File Name
Description :
Author : xiaobei
CreateDate
wechatxiaobei_upup
-------------------------------------------------
"""
import pytest
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
"""
它使用了WebDriverWait类可以检测股票数据是否更新并判断股票是否涨停或跌停
启动Chrome驱动程序并在你的浏览器中加载URL然后它将等待10秒钟来检查股票价格是否发生变化
如果股票价格发生变化则它将打印出涨停或跌停的消息如果股票价格未发生变化则测试失败
"""
class RefreshChecker:
def __init__(self, url):
self.url = url
self.driver = webdriver.Chrome()
def __enter__(self):
self.driver.get(self.url)
return self
def __exit__(self, exc_type, exc_value, traceback):
self.driver.quit()
def get_price(self):
# get current price
a = self.driver.find_element_by_id('price').text
return a
def check_refresh(self, previous_price, timeout=10):
WebDriverWait(self.driver, timeout).until(EC.text_to_be_present_in_element((By.ID, 'price'), lambda price: price != previous_price))
current_price = self.get_price()
price_change = float(current_price) - float(previous_price)
if price_change >= 10:
print("涨停!")
elif price_change <= -10:
print("跌停!")
# 测似
def test_refresh_checker():
with RefreshChecker("http://www.baidu.com/stock") as checker:
previous_price = checker.get_price()
checker.check_refresh(previous_price)

56
common/times.py Normal file
View File

@ -0,0 +1,56 @@
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import time
import datetime
from functools import wraps
"""
-------------------------------------------------
File Name
Description :
Author : xiaobei
CreateDate
wechatxiaobei_upup
-------------------------------------------------
"""
def 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)
def wrapper(*args, **kwargs):
start = timestamp()
res = func(*args, **kwargs)
print("校验元素done用时%.3f秒!" % (timestamp() - start))
return res
return wrapper
if __name__ == '__main__':
@running_time
def aaaaa():
"""这里是aaaaa的docstring"""
print(dt_strftime("%Y%m%d%H%M%S"))
print(aaaaa.__doc__,aaaaa.__name__)
aaaaa()

20
common/xb_assert.py Normal file
View File

@ -0,0 +1,20 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
# ==================================================
# @Time : 2023-06-26 14:27:52
# @Author : 小北
# @微信xiaobei_upup
# @Email : 2211484376@qq.com
# @QQ群585971269
# @微信群:可加我微信拉你
# @Site : 苏州
# @Desc :
# @跳槽辅导:初中高级测试跳槽涨薪面试辅导,详情咨询微信
# @求职辅导:初中高级测试求职面试辅导,详情咨询微信
# @特色: 小北独创VIP面试速成课程拿下心仪的offer
# @如何付款先拿offer再付款只需交定金相互信任无套路
# ==================================================
"""
def xb_assert(actual_value, expected_value ):
assert actual_value == expected_value, "实际值为: {}, 预期值为: {}".format(actual_value, expected_value)

0
config/__init__.py Normal file
View File

74
config/conf.py Normal file
View File

@ -0,0 +1,74 @@
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import os
from selenium.webdriver.common.by import By
from common.times import dt_strftime
"""
配置文件总是项目中必不可少的部分
将固定不变的信息集中在固定的文件中
这个conf文件我模仿了Django的settings.py文件的设置风格但是又有些许差异
author: B站小北 time2023-04-18
"""
class 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': '2211484376@qq.com', # 切换成你自己的地址
'password': 'QQ邮箱授权码',
'smtp_host': 'smtp.qq.com',
'smtp_port': 465
}
# 收件人
ADDRESSEE = [
'2211484376@qq.com',
]
# 测试网址
BAIDU_URL = "https://www.baidu.com"
ZHUIFENG = "https://exam.wzzz.fun"
WPS_LOGIN = "https://account.wps.cn/"
FILE_UPLOAD = "https://letcode.in/file"
@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()))
@property
def 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_file
cm = ConfigManager()
if __name__ == '__main__':
print(cm.BASE_DIR)

135
conftest.py Normal file
View File

@ -0,0 +1,135 @@
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import pytest
from selenium import webdriver
"""
-------------------------------------------------
File Name
Description : 产生driver
Author : xiaobei
CreateDate
wechatxiaobei_upup
-------------------------------------------------
conftest.py测试框架pytest的胶水文件里面用到了fixture的方法封装并传递出了driver
test_pytest.fixture 这个实现了和unittest的setupteardown一样的前置启动后置清理的装饰器
管理数据库连接全局管理我们的driver
"""
@pytest.fixture(scope='session', autouse=True)
def drivers():
driver = webdriver.Chrome()
driver.maximize_window()
yield driver
driver.quit()
# @test_pytest.fixture(scope='session', autouse=True)
# def drivers(request):
# global driver
# if driver is None:
# driver = webdriver.Chrome()
# driver.maximize_window()
#
# def fn():
# driver.quit()
#
# request.addfinalizer(fn)
# return driver
#
# @test_pytest.fixture(scope='session', autouse=True)
# def drivers():
# global driver
# option = Options()
# # 无头模式
# option.add_argument('--headless')
# # 沙盒模式运行
# option.add_argument('--no-sandbox')
# # 大量渲染时候写入/tmp而非/dev/shm
# option.add_argument('disable-dev-shm-usage')
# # 指定驱动路径
# driver = webdriver.Chrome(chrome_options=option)
#
# yield driver
# driver.quit()
# @test_pytest.hookimpl(hookwrapper=True)
# def pytest_runtest_makereport(item):
# """
# 当测试失败的时候自动截图展示到html报告中
# :param item:
# """
# pytest_html = item.config.pluginmanager.getplugin('html')
# outcome = yield
# report = outcome.get_result()
# report.description = str(item.function.__doc__)
# extra = getattr(report, 'extra', [])
#
# if report.when == 'call' or report.when == "setup":
# xfail = hasattr(report, 'wasxfail')
# if (report.skipped and xfail) or (report.failed and not xfail):
# file_name = report.nodeid.replace("::", "_") + ".png"
# screen_img = _capture_screenshot()
# if file_name:
# html = '<div><img src="data:image/png;base64,%s" alt="screenshot" style="width:1024px;height:768px;" ' \
# 'onclick="window.open(this.src)" align="right"/></div>' % screen_img
# extra.append(pytest_html.extras.html(html))
# report.extra = 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_row(report, cells):
# cells.insert(1, html.td(report.description))
# cells.insert(2, html.td(report.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()

17
data/data.csv Normal file
View File

@ -0,0 +1,17 @@
username,password
user1434sfdsvfdsfffffffffffg,pass1sdgggggggggggggggggggggggg
user2sgdddddddddddddddddddddd,pass2sdggggggggggggggggggggggf
wzzr43ferde4rf4erft4erft4ertf,1234543tr4ertfergvf34terferfgt34t
user143t34trfrdfvdfvgefrferf,pass1ertg34tgervgfrdgvergergfgrg
wzzr43ferde4rf4erft4erft4ertf,1234543tr4ertfergvf34terferfgt34t
user143t34trfrdfvdfvgefrferf,pass1ertg34tgervgfrdgvergergfgrg
wzzr43ferde4rf4erft4erft4ertf,1234543tr4ertfergvf34terferfgt34t
user143t34trfrdfvdfvgefrferf,pass1ertg34tgervgfrdgvergergfgrg
wzzr43ferde4rf4erft4erft4ertf,1234543tr4ertfergvf34terferfgt34t
user143t34trfrdfvdfvgefrferf,pass1ertg34tgervgfrdgvergergfgrg
wzzr43ferde4rf4erft4erft4ertf,1234543tr4ertfergvf34terferfgt34t
user143t34trfrdfvdfvgefrferf,pass1ertg34tgervgfrdgvergergfgrg
wzzr43ferde4rf4erft4erft4ertf,1234543tr4ertfergvf34terferfgt34t
user143t34trfrdfvdfvgefrferf,pass1ertg34tgervgfrdgvergergfgrg
wzzr43ferde4rf4erft4erft4ertf,1234543tr4ertfergvf34terferfgt34t
user143t34trfrdfvdfvgefrferf,pass1ertg34tgervgfrdgvergergfgrg
1 username password
2 user1434sfdsvfdsfffffffffffg pass1sdgggggggggggggggggggggggg
3 user2sgdddddddddddddddddddddd pass2sdggggggggggggggggggggggf
4 wzzr43ferde4rf4erft4erft4ertf 1234543tr4ertfergvf34terferfgt34t
5 user143t34trfrdfvdfvgefrferf pass1ertg34tgervgfrdgvergergfgrg
6 wzzr43ferde4rf4erft4erft4ertf 1234543tr4ertfergvf34terferfgt34t
7 user143t34trfrdfvdfvgefrferf pass1ertg34tgervgfrdgvergergfgrg
8 wzzr43ferde4rf4erft4erft4ertf 1234543tr4ertfergvf34terferfgt34t
9 user143t34trfrdfvdfvgefrferf pass1ertg34tgervgfrdgvergergfgrg
10 wzzr43ferde4rf4erft4erft4ertf 1234543tr4ertfergvf34terferfgt34t
11 user143t34trfrdfvdfvgefrferf pass1ertg34tgervgfrdgvergergfgrg
12 wzzr43ferde4rf4erft4erft4ertf 1234543tr4ertfergvf34terferfgt34t
13 user143t34trfrdfvdfvgefrferf pass1ertg34tgervgfrdgvergergfgrg
14 wzzr43ferde4rf4erft4erft4ertf 1234543tr4ertfergvf34terferfgt34t
15 user143t34trfrdfvdfvgefrferf pass1ertg34tgervgfrdgvergergfgrg
16 wzzr43ferde4rf4erft4erft4ertf 1234543tr4ertfergvf34terferfgt34t
17 user143t34trfrdfvdfvgefrferf pass1ertg34tgervgfrdgvergergfgrg

BIN
data/pytesseract.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

0
page/__init__.py Normal file
View File

88
page/basePage.py Normal file
View File

@ -0,0 +1,88 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
-------------------------------------------------
File Name basePage
Description : Python descriptor
Author : beige
CreateDate 2023/03/19 21:36
-------------------------------------------------
功能1可以轻松实现元素定位
2还可以实现在某个元素输入字符串
"""
from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException, TimeoutException
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
# Map PageElement constructor arguments to webdriver locator enums
_LOCATOR_MAP = {
'xpath': By.XPATH,
'id': By.ID,
'tag_name': By.TAG_NAME,
'name': By.NAME,
'css': By.CSS_SELECTOR,
'class1':By.CLASS_NAME
}
class PageObject(object):
"""
接收driver为了让driver后续完全脱手再也不接触driver而写让测试者能够拜托driver的繁琐操作
Page Object pattern.
:param webdriver: `selenium.webdriver.WebDriver`
Selenium webdriver instance
:param root_uri: `str`
Root URI to base any calls to the ``PageObject.get`` method. If not defined
in the constructor it will try and look it from the webdriver object.
"""
def __init__(self, webdriver: object):
"""接收driver为了让driver后续完全脱手再也不接触driver而写让测试者能够拜托driver的繁琐操作。"""
self.w = webdriver
class PageElement(object):
"""Page Element descriptor.
:param xpath: `str`
Use this xpath locator
elem1 = PageElement(css='div.myclass')
elem2 = PageElement(id='foo')
Page Elements act as property descriptors for their Page Object, you can get
and set them as normal attributes.
"""
def __init__(self, context=False, **kwargs):
"""处理传进来的元素定位键值对,让(id='kw')变成By.id, 'kw'"""
if not kwargs:
raise ValueError("Please specify a locator")
if len(kwargs) > 1:
raise ValueError("Please specify only one locator")
k, v = next(iter(kwargs.items())) # 使用了迭代器,生成器来让(id='kw')变成By.id, 'kw'两个单独的参数
self.locator = (_LOCATOR_MAP[k], v)
self.has_context = bool(context)
def __get__(self, instance, owner, context=None):
"""实现元素定位find_element()"""
if not instance:
return None
if not context and self.has_context:
return lambda ctx: self.__get__(instance, owner, context=ctx)
if not context:
context = instance.w
return WebDriverWait(context, 5, 1).until(lambda x: x.find_element(*self.locator))
def __set__(self, instance, value):
"""实现往元素中写入东西send_keys()"""
if self.has_context:
raise ValueError("Sorry, the set descriptor doesn't support elements with context.")
elem = self.__get__(instance, instance.__class__)
if not elem:
raise ValueError("Can't set value, element not found")
elem.send_keys(value)
# Backwards compatibility with previous versions that used factory methods
page_element = PageElement

View File

View File

@ -0,0 +1,29 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
# ==================================================
# @Time : 2023-08-15 18:42:37
# @Author : 小北
# @微信xiaobei_upup
# @Email : 2211484376@qq.com
# @QQ群585971269
# @微信群:可加我微信拉你
# @Site : 苏州
# @Desc :
# @跳槽辅导:初中高级测试跳槽涨薪就业辅导,详情咨询微信
# @求职辅导:初中高级测试求职就业辅导,详情咨询微信
# @特色: 小北独创VIP就业速成课程拿下心仪的offer
# @如何付款:官方小程序付款 or 微信 or 支付宝
# ==================================================
"""
from page.basePage import *
class FileUpload(PageObject):
file_choose = PageElement(css='.file-label')
@property
def click_file_choose(self):
"""点击搜索"""
return self.file_choose.click()

View File

@ -0,0 +1,33 @@
from page.basePage import PageObject, PageElement
from page.basePage import *
"""
-------------------------------------------------
File Name
Description :
Author : xiaobei
CreateDate
wechatxiaobei_upup
-------------------------------------------------
"""
"""
在平时中我们应该养成写注释的习惯因为过一段时间后没有注释代码读起来很费劲
"""
class zhuifeng_index_page(PageObject):
input_account = PageElement(xpath='/html/body/section/main/div/div[2]/div/form/div[1]/div/div/input')
input_password = PageElement(xpath='/html/body/section/main/div/div[2]/div/form/div[2]/div/div/input')
auto_code = PageElement(xpath='/html/body/section/main/div/div[2]/div/form/div[3]/div/div/input')
log_in_button = PageElement(xpath='/html/body/section/main/div/div[2]/div/form/div[4]/div/button[1]')
image = PageElement(id='code')
image_code = PageElement(xpath='/html/body/section/main/div/div[2]/div/form/div[3]/div/div/input')
@property
def click_log_in_button(self):
"""点击搜索"""
return self.log_in_button.click()

34
pytest.ini Normal file
View File

@ -0,0 +1,34 @@
[pytest]
;addopts配置命令行参数用空格进行分隔
;可执行标记为mark的对应用例用or表示标记为demo或者smoke的用例都会执行
addopts =
-vs
--alluredir=./reports/
--clean-alluredir
-m "smoke"
-n 0
--reruns=0
;注册 mark 标记
markers =
smoke: marks tests as smoke
test: marks tests as test
testNow: marks tests as testNow
;执行的时候使用 pytest -m smoke
minversion = 5.0
;测试用例的路径,可自己配置,
;../pytestproject为上一层的pytestproject文件夹
;./testcase为pytest.ini当前目录下的同级文件夹
;改变用例的查找路径规则当前目录的testcase文件夹下的所有
testpaths =./testcases/
;模块名的规则,配置测试搜索的模块文件名称
python_files = test*.py
;类名的规则,配置测试搜索的测试类名
python_classes = Test*
;方法名的规则,配置测试搜索的测试函数名
python_functions = test

View File

@ -0,0 +1 @@
{"uuid": "66ae6d33-e955-4397-927c-44e95e90a7e3", "children": ["7f19c6f7-a80f-4b70-985a-7cfcd5e63804", "ee15855b-c6d9-4be8-b5ff-f67ef1bdad75", "4e339847-f374-4691-9f96-6b87e2d51832"], "befores": [{"name": "drivers", "status": "passed", "start": 1691998099297, "stop": 1691998101160}], "afters": [{"name": "drivers::0", "status": "passed", "start": 1691998116026, "stop": 1691998118116}], "start": 1691998099297, "stop": 1691998118116}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"uuid": "5fc06f2f-c6ae-4e11-ae8f-f25706efe8d2", "befores": [{"name": "username", "status": "passed", "start": 1691998105893, "stop": 1691998105893}], "start": 1691998105893, "stop": 1691998115417}

View File

@ -0,0 +1 @@
{"uuid": "fcf82d15-9117-41ea-9be3-09f41dbd4ca7", "children": ["7f19c6f7-a80f-4b70-985a-7cfcd5e63804"], "befores": [{"name": "open_baidu", "status": "passed", "start": 1691998101161, "stop": 1691998105892}], "afters": [{"name": "open_baidu::0", "status": "passed", "start": 1691998115418, "stop": 1691998115418}], "start": 1691998101160, "stop": 1691998115418}

View File

@ -0,0 +1 @@
{"uuid": "506fc813-3192-40a1-88e1-52788dbbc862", "befores": [{"name": "username", "status": "passed", "start": 1691998115751, "stop": 1691998115752}], "start": 1691998115751, "stop": 1691998115941}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"name": "test_001[user1-pass1]", "status": "passed", "parameters": [{"name": "username", "value": "'user1'"}, {"name": "password", "value": "'pass1'"}], "start": 1691998105893, "stop": 1691998115416, "uuid": "7f19c6f7-a80f-4b70-985a-7cfcd5e63804", "historyId": "7447b017eda5d8972c74406bfcfd1dba", "testCaseId": "f4ef65b043d05db61048415b9c5a1413", "fullName": "testcases.zhuifeng_tc.test_ddt_zhuifeng.TestSearch2#test_001", "labels": [{"name": "tag", "value": "smoke"}, {"name": "parentSuite", "value": "testcases.zhuifeng_tc"}, {"name": "suite", "value": "test_ddt_zhuifeng"}, {"name": "subSuite", "value": "TestSearch2"}, {"name": "host", "value": "LAPTOP-2RGLO47J"}, {"name": "thread", "value": "68708-MainThread"}, {"name": "framework", "value": "pytest"}, {"name": "language", "value": "cpython3"}, {"name": "package", "value": "testcases.zhuifeng_tc.test_ddt_zhuifeng"}]}

View File

@ -0,0 +1 @@
{"uuid": "aa385fe0-fcef-4dfd-95d5-deadc8129ca0", "children": ["4e339847-f374-4691-9f96-6b87e2d51832"], "befores": [{"name": "open_baidu", "status": "broken", "statusDetails": {"message": "selenium.common.exceptions.NoSuchWindowException: Message: no such window: target window already closed\nfrom unknown error: web view not found\n (Session info: chrome=114.0.5735.199)\nStacktrace:\nBacktrace:\n\tGetHandleVerifier [0x0038A813+48355]\n\t(No symbol) [0x0031C4B1]\n\t(No symbol) [0x00225358]\n\t(No symbol) [0x0020D293]\n\t(No symbol) [0x0026E37B]\n\t(No symbol) [0x0027C473]\n\t(No symbol) [0x0026A536]\n\t(No symbol) [0x002482DC]\n\t(No symbol) [0x002493DD]\n\tGetHandleVerifier [0x005EAABD+2539405]\n\tGetHandleVerifier [0x0062A78F+2800735]\n\tGetHandleVerifier [0x0062456C+2775612]\n\tGetHandleVerifier [0x004151E0+616112]\n\t(No symbol) [0x00325F8C]\n\t(No symbol) [0x00322328]\n\t(No symbol) [0x0032240B]\n\t(No symbol) [0x00314FF7]\n\tBaseThreadInitThunk [0x764B00C9+25]\n\tRtlGetAppContainerNamedObjectPath [0x77507B1E+286]\n\tRtlGetAppContainerNamedObjectPath [0x77507AEE+238]\n\n", "trace": " File \"H:\\Develop\\env_dependence\\Python\\Lib\\site-packages\\pluggy\\_callers.py\", line 39, in _multicall\n res = hook_impl.function(*args)\n ^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"H:\\Develop\\env_dependence\\Python\\Lib\\site-packages\\_pytest\\fixtures.py\", line 1130, in pytest_fixture_setup\n result = call_fixture_func(fixturefunc, request, kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"H:\\Develop\\env_dependence\\Python\\Lib\\site-packages\\_pytest\\fixtures.py\", line 902, in call_fixture_func\n fixture_result = next(generator)\n ^^^^^^^^^^^^^^^\n File \"H:\\Develop\\Python\\Selenium_UI_Project_2023_08_14\\testcases\\zhuifeng_tc\\test_ddt_zhuifeng.py\", line 33, in open_baidu\n drivers.get(ConfigManager.ZHUIFENG)\n File \"H:\\Develop\\env_dependence\\Python\\Lib\\site-packages\\selenium\\webdriver\\remote\\webdriver.py\", line 449, in get\n self.execute(Command.GET, {\"url\": url})\n File \"H:\\Develop\\env_dependence\\Python\\Lib\\site-packages\\selenium\\webdriver\\remote\\webdriver.py\", line 440, in execute\n self.error_handler.check_response(response)\n File \"H:\\Develop\\env_dependence\\Python\\Lib\\site-packages\\selenium\\webdriver\\remote\\errorhandler.py\", line 245, in check_response\n raise exception_class(message, screen, stacktrace)\n"}, "start": 1691998115957, "stop": 1691998115958}], "start": 1691998115957, "stop": 1691998116024}

View File

@ -0,0 +1 @@
{"uuid": "7597ef3d-c261-44de-85ab-4859306a7342", "befores": [{"name": "password", "status": "passed", "start": 1691998105893, "stop": 1691998105893}], "start": 1691998105893, "stop": 1691998115416}

View File

@ -0,0 +1 @@
{"uuid": "0d046b0d-e722-45bd-96de-41477187d81d", "children": ["ee15855b-c6d9-4be8-b5ff-f67ef1bdad75"], "befores": [{"name": "open_baidu", "status": "passed", "start": 1691998115423, "stop": 1691998115750}], "afters": [{"name": "open_baidu::0", "status": "passed", "start": 1691998115942, "stop": 1691998115942}], "start": 1691998115423, "stop": 1691998115942}

View File

@ -0,0 +1 @@
{"uuid": "f32cd291-f80c-4b2e-a1d4-3fa05d9d7fea", "befores": [{"name": "password", "status": "passed", "start": 1691998115752, "stop": 1691998115752}], "start": 1691998115752, "stop": 1691998115939}

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 KiB

0
testcases/__init__.py Normal file
View File

View File

@ -0,0 +1,20 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
# ==================================================
# @Time : 2023-06-23 13:54:50
# @Author : 小北
# @微信xiaobei_upup
# @Email : 2211484376@qq.com
# @QQ群585971269
# @微信群:可加我微信拉你
# @Site : 苏州
# @Desc :
# @跳槽辅导:初中高级测试跳槽涨薪面试辅导,详情咨询微信
# @求职辅导:初中高级测试求职面试辅导,详情咨询微信
# @特色: 小北独创VIP面试速成课程拿下心仪的offer
# @如何付款先拿offer再付款只需交定金相互信任无套路
# ==================================================
"""
if __name__ == '__main__':
pass

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -0,0 +1 @@
{"uuid": "ce0eeefd-2a2a-4018-a3b9-718cd422d8c6", "children": ["68fc0bc4-6ad8-49dc-924c-d3a871a19290"], "befores": [{"name": "drivers", "status": "passed", "start": 1692107307725, "stop": 1692107319283}], "afters": [{"name": "drivers::0", "status": "passed", "start": 1692107424127, "stop": 1692107428670}], "start": 1692107307725, "stop": 1692107428670}

View File

@ -0,0 +1 @@
{"uuid": "7c866e15-5cf4-401e-a2c7-0444227fcd47", "children": ["68fc0bc4-6ad8-49dc-924c-d3a871a19290"], "befores": [{"name": "open_baidu", "status": "passed", "start": 1692107319284, "stop": 1692107324782}], "afters": [{"name": "open_baidu::0", "status": "passed", "start": 1692107424126, "stop": 1692107424126}], "start": 1692107319284, "stop": 1692107424126}

View File

@ -0,0 +1 @@
{"name": "test_001[wzz-12345]", "status": "passed", "parameters": [{"name": "username", "value": "'wzz'"}, {"name": "password", "value": "'12345'"}], "start": 1692107324912, "stop": 1692107424124, "uuid": "68fc0bc4-6ad8-49dc-924c-d3a871a19290", "historyId": "d590c7043674851a28364ae332272855", "testCaseId": "75ce6b87312d044f17edfe42bfb0fde7", "fullName": "testcases.zhuifeng_tc.test_ddt_zhuifeng.TestSearch#test_001", "labels": [{"name": "tag", "value": "smoke"}, {"name": "parentSuite", "value": "testcases.zhuifeng_tc"}, {"name": "suite", "value": "test_ddt_zhuifeng"}, {"name": "subSuite", "value": "TestSearch"}, {"name": "host", "value": "LAPTOP-2RGLO47J"}, {"name": "thread", "value": "14440-MainThread"}, {"name": "framework", "value": "pytest"}, {"name": "language", "value": "cpython3"}, {"name": "package", "value": "testcases.zhuifeng_tc.test_ddt_zhuifeng"}]}

View File

@ -0,0 +1 @@
{"uuid": "7f2b1f3e-3d70-4378-8776-469aef9249b0", "befores": [{"name": "password", "status": "passed", "start": 1692107324783, "stop": 1692107324783}], "start": 1692107324783, "stop": 1692107424124}

View File

@ -0,0 +1 @@
{"uuid": "1bcd466c-4cff-414e-b378-8b136d4b2a10", "befores": [{"name": "username", "status": "passed", "start": 1692107324783, "stop": 1692107324783}], "start": 1692107324783, "stop": 1692107424126}

View File

@ -0,0 +1,54 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
# ==================================================
# @Time : 2023-06-23 13:55:01
# @Author : 小北
# @微信xiaobei_upup
# @Email : 2211484376@qq.com
# @QQ群585971269
# @微信群:可加我微信拉你
# @Site : 苏州
# @Desc :
# @跳槽辅导:初中高级测试跳槽涨薪面试辅导,详情咨询微信
# @求职辅导:初中高级测试求职面试辅导,详情咨询微信
# @特色: 小北独创VIP面试速成课程拿下心仪的offer
# @如何付款先拿offer再付款只需交定金相互信任无套路
# ==================================================
"""
import pytest
from page.zhuifeng_pages.zhuifeng_index import zhuifeng_index_page
from config.conf import ConfigManager
import time
from common.image_identify import image_identify
"""
test_pytest.fixture + 生成器yield 一起实现了和unittest的setupteardown一样的前置启动后置清理的装饰器
"""
class TestSearch:
@pytest.fixture(scope='function', autouse=True)
def open_baidu(self, drivers):
"""打开百度"""
drivers.get(ConfigManager.ZHUIFENG)
yield
print("后置")
@pytest.mark.smoke
@pytest.mark.parametrize('username, password', [
('wzz', '12345')
])
def test_001(self, drivers, username, password):
zhufeng = zhuifeng_index_page(drivers)
zhufeng.input_account = username
zhufeng.input_password = password
# zhufeng.log_in_button.click()
zhufeng.image_code = image_identify(drivers, zhufeng.image, 'whole_pic.png', 'crop_pic.png')
zhufeng.click_log_in_button
time.sleep(3)
if __name__ == '__main__':
pytest.main(['vs', 'testcases/test_pytest/test_search_baidu_index.py'])

View File

@ -0,0 +1,58 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
# ==================================================
# @Time : 2023-06-23 14:04:29
# @Author : 小北
# @微信xiaobei_upup
# @Email : 2211484376@qq.com
# @QQ群585971269
# @微信群:可加我微信拉你
# @Site : 苏州
# @Desc :
# @跳槽辅导:初中高级测试跳槽涨薪面试辅导,详情咨询微信
# @求职辅导:初中高级测试求职面试辅导,详情咨询微信
# @特色: 小北独创VIP面试速成课程拿下心仪的offer
# @如何付款先拿offer再付款只需交定金相互信任无套路
# ==================================================
"""
import pytest
from page.zhuifeng_pages.zhuifeng_index import zhuifeng_index_page
from config.conf import ConfigManager
import time
import csv
import threading
"""
test_pytest.fixture + 生成器yield 一起实现了和unittest的setupteardown一样的前置启动后置清理的装饰器
"""
@pytest.fixture(scope='function', autouse=True)
def open_baidu(drivers):
"""打开百度"""
drivers.get(ConfigManager.ZHUIFENG)
yield
print("后置")
def read_csv_file(file_path):
"""生成器方式去读取csv里面的数据来做数据驱动测试yield关键字来控制一行一行的读取字典里面的内容字典里面的数据是隐形的还未产生就和奶糖盒子一样的道理"""
with open(file_path, 'r', newline='') as file:
reader = csv.DictReader(file) # 这是一个迭代器对象把每次读取出来的数据都放到字典里面存起来下面用一个for循环一次一次的去读取字典里面的数据确保不会一次性将所有的数据读取到内存中。
for row in reader: # 如过下面没有生成器那么这里直接全部数据都遍历一遍如果有生成器就会卡住一个一个来接收到next方法才会读取下一行。
yield row['username'], row['password']
@pytest.mark.smoke
@pytest.mark.parametrize('username, password', read_csv_file(r'H:\Develop\Python\Selenium_UI_Project\data\data.csv'))
def test_001(drivers, username, password):
zhufeng = zhuifeng_index_page(drivers)
zhufeng.input_account = username
zhufeng.input_password = password
# zhufeng.log_in_button.click()
zhufeng.click_log_in_button
# assert drivers.current_url == 'https://exam.wzzz.fun'
# if __name__ == '__main__':
# pytest.main(['--alluredir', './reports', 'vs', 'testcases/zhuifeng_tc/test_ddt_zhuifeng_csv_file.py'])

View File

@ -0,0 +1,54 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
# ==================================================
# @Time : 2023-06-23 14:04:29
# @Author : 小北
# @微信xiaobei_upup
# @Email : 2211484376@qq.com
# @QQ群585971269
# @微信群:可加我微信拉你
# @Site : 苏州
# @Desc :
# @跳槽辅导:初中高级测试跳槽涨薪面试辅导,详情咨询微信
# @求职辅导:初中高级测试求职面试辅导,详情咨询微信
# @特色: 小北独创VIP面试速成课程拿下心仪的offer
# @如何付款先拿offer再付款只需交定金相互信任无套路
# ==================================================
"""
import pytest
from page.zhuifeng_pages.zhuifeng_index import zhuifeng_index_page
from config.conf import ConfigManager
import time
import csv
import threading
"""
test_pytest.fixture + 生成器yield 一起实现了和unittest的setupteardown一样的前置启动后置清理的装饰器
"""
@pytest.fixture(scope='function', autouse=True)
def open_baidu(drivers):
"""打开百度"""
drivers.get(ConfigManager.ZHUIFENG)
yield
print("后置")
def read_csv_file(file_path):
"""生成器方式去读取csv里面的数据来做数据驱动测试yield关键字来控制一行一行的读取字典里面的内容字典里面的数据是隐形的还未产生就和奶糖盒子一样的道理"""
with open(file_path, 'r', newline='') as file:
reader = csv.DictReader(file) # 这是一个迭代器对象把每次读取出来的数据都放到字典里面存起来下面用一个for循环一次一次的去读取字典里面的数据确保不会一次性将所有的数据读取到内存中。
for row in reader: # 如过下面没有生成器那么这里直接全部数据都遍历一遍如果有生成器就会卡住一个一个来接收到next方法才会读取下一行。
yield row['username'], row['password']
@pytest.mark.parametrize('username, password', read_csv_file(r'H:\Develop\Python\Selenium_UI_Project\data\data.csv'))
def test_001(drivers, username, password):
zhufeng = zhuifeng_index_page(drivers)
zhufeng.input_account = username
zhufeng.input_password = password
# zhufeng.log_in_button.click()
zhufeng.click_log_in_button
# assert drivers.current_url == 'https://exam.wzzz.fun'

View File

@ -0,0 +1,73 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
# ==================================================
# @Time : 2023-06-23 14:04:29
# @Author : 小北
# @微信xiaobei_upup
# @Email : 2211484376@qq.com
# @QQ群585971269
# @微信群:可加我微信拉你
# @Site : 苏州
# @Desc :
# @跳槽辅导:初中高级测试跳槽涨薪面试辅导,详情咨询微信
# @求职辅导:初中高级测试求职面试辅导,详情咨询微信
# @特色: 小北独创VIP面试速成课程拿下心仪的offer
# @如何付款先拿offer再付款只需交定金相互信任无套路
# ==================================================
"""
import pytest
from page.zhuifeng_pages.zhuifeng_index import zhuifeng_index_page
from config.conf import ConfigManager
import time
import csv
import threading
"""
test_pytest.fixture + 生成器yield 一起实现了和unittest的setupteardown一样的前置启动后置清理的装饰器
"""
@pytest.fixture(scope='function', autouse=True)
def open_baidu(drivers):
"""打开百度"""
drivers.get(ConfigManager.ZHUIFENG)
yield
print("后置")
def read_csv_file(file_path):
"""生成器方式去读取csv里面的数据来做数据驱动测试yield关键字来控制一行一行的读取字典里面的内容字典里面的数据是隐形的还未产生就和奶糖盒子一样的道理"""
with open(file_path, 'r', newline='') as file:
reader = csv.DictReader(file) # 这是一个迭代器对象把每次读取出来的数据都放到字典里面存起来下面用一个for循环一次一次的去读取字典里面的数据确保不会一次性将所有的数据读取到内存中。
for row in reader: # 如过下面没有生成器那么这里直接全部数据都遍历一遍如果有生成器就会卡住一个一个来接收到next方法才会读取下一行。
yield row['username'], row['password']
@pytest.mark.parametrize('username, password', read_csv_file(r'H:\Develop\Python\Selenium_UI_Project\data\data.csv'))
def test_001(drivers, username, password):
zhufeng = zhuifeng_index_page(drivers)
zhufeng.input_account = username
zhufeng.input_password = password
# zhufeng.log_in_button.click()
zhufeng.click_log_in_button
# assert drivers.current_url == 'https://exam.wzzz.fun'
if __name__ == '__main__':
pytest.main(['--alluredir', './reports', 'vs', 'testcases/zhuifeng_tc/test_ddt_zhuifeng_csv_file3.py'])
# t1 = threading.Thread(target=test_pytest.main,
# args=(["-v", "-s", "testcases/zhuifeng_tc/test_ddt_zhuifeng_csv_file.py"],))
# t2 = threading.Thread(target=test_pytest.main,
# args=(["-v", "-s", "testcases/zhuifeng_tc/test_ddt_zhuifeng.py"],))
# t3 = threading.Thread(target=test_pytest.main,
# args=(["-v", "-s", "testcases/zhuifeng_tc/test_zhuifeng_system.py"],))
#
# t1.start()
# t2.start()
# t3.start()
#
#
# t1.join()
# t2.join()
# t3.join()

View File

@ -0,0 +1,73 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
# ==================================================
# @Time : 2023-06-23 14:04:29
# @Author : 小北
# @微信xiaobei_upup
# @Email : 2211484376@qq.com
# @QQ群585971269
# @微信群:可加我微信拉你
# @Site : 苏州
# @Desc :
# @跳槽辅导:初中高级测试跳槽涨薪面试辅导,详情咨询微信
# @求职辅导:初中高级测试求职面试辅导,详情咨询微信
# @特色: 小北独创VIP面试速成课程拿下心仪的offer
# @如何付款先拿offer再付款只需交定金相互信任无套路
# ==================================================
"""
import pytest
from page.zhuifeng_pages.zhuifeng_index import zhuifeng_index_page
from config.conf import ConfigManager
import time
import csv
import threading
"""
test_pytest.fixture + 生成器yield 一起实现了和unittest的setupteardown一样的前置启动后置清理的装饰器
"""
@pytest.fixture(scope='function', autouse=True)
def open_baidu(drivers):
"""打开百度"""
drivers.get(ConfigManager.ZHUIFENG)
yield
print("后置")
def read_csv_file(file_path):
"""生成器方式去读取csv里面的数据来做数据驱动测试yield关键字来控制一行一行的读取字典里面的内容字典里面的数据是隐形的还未产生就和奶糖盒子一样的道理"""
with open(file_path, 'r', newline='') as file:
reader = csv.DictReader(file) # 这是一个迭代器对象把每次读取出来的数据都放到字典里面存起来下面用一个for循环一次一次的去读取字典里面的数据确保不会一次性将所有的数据读取到内存中。
for row in reader: # 如过下面没有生成器那么这里直接全部数据都遍历一遍如果有生成器就会卡住一个一个来接收到next方法才会读取下一行。
yield row['username'], row['password']
@pytest.mark.parametrize('username, password', read_csv_file(r'H:\Develop\Python\Selenium_UI_Project\data\data.csv'))
def test_001(drivers, username, password):
zhufeng = zhuifeng_index_page(drivers)
zhufeng.input_account = username
zhufeng.input_password = password
# zhufeng.log_in_button.click()
zhufeng.click_log_in_button
# assert drivers.current_url == 'https://exam.wzzz.fun'
if __name__ == '__main__':
pytest.main(['--alluredir', './reports', 'vs', 'testcases/zhuifeng_tc/test_ddt_zhuifeng_csv_file4.py'])
# t1 = threading.Thread(target=test_pytest.main,
# args=(["-v", "-s", "testcases/zhuifeng_tc/test_ddt_zhuifeng_csv_file.py"],))
# t2 = threading.Thread(target=test_pytest.main,
# args=(["-v", "-s", "testcases/zhuifeng_tc/test_ddt_zhuifeng.py"],))
# t3 = threading.Thread(target=test_pytest.main,
# args=(["-v", "-s", "testcases/zhuifeng_tc/test_zhuifeng_system.py"],))
#
# t1.start()
# t2.start()
# t3.start()
#
#
# t1.join()
# t2.join()
# t3.join()

View File

@ -0,0 +1,73 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
# ==================================================
# @Time : 2023-06-23 14:04:29
# @Author : 小北
# @微信xiaobei_upup
# @Email : 2211484376@qq.com
# @QQ群585971269
# @微信群:可加我微信拉你
# @Site : 苏州
# @Desc :
# @跳槽辅导:初中高级测试跳槽涨薪面试辅导,详情咨询微信
# @求职辅导:初中高级测试求职面试辅导,详情咨询微信
# @特色: 小北独创VIP面试速成课程拿下心仪的offer
# @如何付款先拿offer再付款只需交定金相互信任无套路
# ==================================================
"""
import pytest
from page.zhuifeng_pages.zhuifeng_index import zhuifeng_index_page
from config.conf import ConfigManager
import time
import csv
import threading
"""
test_pytest.fixture + 生成器yield 一起实现了和unittest的setupteardown一样的前置启动后置清理的装饰器
"""
@pytest.fixture(scope='function', autouse=True)
def open_baidu(drivers):
"""打开百度"""
drivers.get(ConfigManager.ZHUIFENG)
yield
print("后置")
def read_csv_file(file_path):
"""生成器方式去读取csv里面的数据来做数据驱动测试yield关键字来控制一行一行的读取字典里面的内容字典里面的数据是隐形的还未产生就和奶糖盒子一样的道理"""
with open(file_path, 'r', newline='') as file:
reader = csv.DictReader(file) # 这是一个迭代器对象把每次读取出来的数据都放到字典里面存起来下面用一个for循环一次一次的去读取字典里面的数据确保不会一次性将所有的数据读取到内存中。
for row in reader: # 如过下面没有生成器那么这里直接全部数据都遍历一遍如果有生成器就会卡住一个一个来接收到next方法才会读取下一行。
yield row['username'], row['password']
@pytest.mark.parametrize('username, password', read_csv_file(r'H:\Develop\Python\Selenium_UI_Project_2023_08_14\data\data.csv'))
def test_001(drivers, username, password):
zhufeng = zhuifeng_index_page(drivers)
zhufeng.input_account = username
zhufeng.input_password = password
# zhufeng.log_in_button.click()
zhufeng.click_log_in_button
# assert drivers.current_url == 'https://exam.wzzz.fun'
if __name__ == '__main__':
pytest.main(['--alluredir', './reports', 'vs', 'testcases/zhuifeng_tc/test_ddt_zhuifeng_csv_file5.py'])
# t1 = threading.Thread(target=test_pytest.main,
# args=(["-v", "-s", "testcases/zhuifeng_tc/test_ddt_zhuifeng_csv_file.py"],))
# t2 = threading.Thread(target=test_pytest.main,
# args=(["-v", "-s", "testcases/zhuifeng_tc/test_ddt_zhuifeng.py"],))
# t3 = threading.Thread(target=test_pytest.main,
# args=(["-v", "-s", "testcases/zhuifeng_tc/test_zhuifeng_system.py"],))
#
# t1.start()
# t2.start()
# t3.start()
#
#
# t1.join()
# t2.join()
# t3.join()

View File

@ -0,0 +1,53 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
# ==================================================
# @Time : 2023-06-23 13:55:01
# @Author : 小北
# @微信xiaobei_upup
# @Email : 2211484376@qq.com
# @QQ群585971269
# @微信群:可加我微信拉你
# @Site : 苏州
# @Desc :
# @跳槽辅导:初中高级测试跳槽涨薪面试辅导,详情咨询微信
# @求职辅导:初中高级测试求职面试辅导,详情咨询微信
# @特色: 小北独创VIP面试速成课程拿下心仪的offer
# @如何付款先拿offer再付款只需交定金相互信任无套路
# ==================================================
"""
import pytest
from page.zhuifeng_pages.file_upload_pages import FileUpload
from config.conf import ConfigManager
import time
from pywinauto import Desktop
from common.image_identify import image_identify
"""
test_pytest.fixture + 生成器yield 一起实现了和unittest的setupteardown一样的前置启动后置清理的装饰器
"""
class TestFileUpload:
@pytest.fixture(scope='function', autouse=True)
def open_file_upload(self, drivers):
"""打开文件上传网址"""
drivers.get(ConfigManager.FILE_UPLOAD)
yield
print("后置")
@pytest.mark.smoke
def test_001(self, drivers):
file = FileUpload(drivers)
file.click_file_choose
time.sleep(3)
app = Desktop()
dialog = app['打开']
# 根据名字找到弹出窗口
dialog["Edit"].type_keys('1.jpg')
# 在弹出的框中输入相关的值。
dialog["Button"].click()
if __name__ == '__main__':
pytest.main(['vs', 'testcases/test_pytest/test_file_upload.py'])

View File

@ -0,0 +1,54 @@
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
"""
-------------------------------------------------
File Name
Description :
Author : xiaobei
CreateDate
wechatxiaobei_upup
-------------------------------------------------
"""
import pytest
from page.zhuifeng_pages.zhuifeng_index import zhuifeng_index_page
from config.conf import ConfigManager
import time
"""
test_pytest.fixture + 生成器yield 一起实现了和unittest的setupteardown一样的前置启动后置清理的装饰器
"""
class TestSearch2:
@pytest.fixture(scope='function', autouse=True)
def open_baidu(self, drivers):
"""打开"""
drivers.get(ConfigManager.ZHUIFENG)
yield
print("后置")
@pytest.mark.parametrize('username, password', [
('user1', 'pass1'),
('user2', 'pass2'),
('user3', 'pass3')
])
def test_001(self, drivers,username, password):
zhufeng = zhuifeng_index_page(drivers)
zhufeng.input_account = username
zhufeng.input_password = password
time.sleep(3)
# @allure.step("操作步骤2")
# def test_002(self, drivers):
# baidu = baidu_index_page(drivers)
# baidu.search_frame.send_keys('阿里巴巴')
#
# baidu.click_search
# webdriver = WebPage(drivers)
# webdriver.refresh()
# # print(webdriver.get_source)
# assert baidu.search_button
# # log.info()

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 KiB