300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > 使用selenium + pytest + allure做WBE UI自动化

使用selenium + pytest + allure做WBE UI自动化

时间:2021-05-14 15:22:45

相关推荐

使用selenium + pytest + allure做WBE UI自动化

目录结构

依然使用了常见的po 模式

目录结构 config config.ini 修改目标路由与相关配置 elements 抽象页面空间元素 method 抽象页面方法 TestCase 测试用例 utils Assert 自定义断言chromedriver 浏览器驱动 需匹配版本 非linux 环境无视Driver 初始化drivergraphical 图片相对定位log 日志seleniumBase api封装seleniumOpt opt定义

初始化Driver

根据判断运行环境 选择不同的chrome driver 或者使用ChromeDriverManager自动下载

class Driver:@classmethoddef driver(cls):try:# select local or docker# if platform == "Darwin":# url = Config().get_conf("dockerHub", "url")# driver = webdriver.Remote(url, desired_capabilities=capabilities(),# options=options())# # else:if platform.system() == "Linux":executable_path = os.path.join(os.path.dirname(__file__), "chromedriver")driver = webdriver.Chrome(executable_path=executable_path, options=options())else:executable_path = ChromeDriverManager().install()driver = webdriver.Chrome(executable_path=executable_path, options=options())driver.implicitly_wait(20)log.info(f"driver:{driver.name}")return driverexcept BaseException as e:log.error(e)raise e

自定义driveroptions配置

def options():"""浏览器设置:return:"""_options = ChromeOptions()# # 设置chrome为手机界面# mobileEmulation = Config().get_conf("seleniumOpt", "mobileEmulation")# if mobileEmulation == 'true':#mobile_emulation = {"deviceName": "Galaxy S5"}#_options.add_experimental_option("mobileEmulation", mobile_emulation)if platform.system() == 'Darwin':passelse:_options.add_argument('--headless')_options.add_argument('--no-sandbox')_options.add_argument('--disable-gpu')_options.add_argument('--disable-dev-shm-usage')return _options

Base类

封装常用API 把driver实例化传入

查找元素尽量使用显示等待expected_conditions

使用了Faker可随意生成测试数据

查找元素失败 使用allure上传截图

class SeleniumBaseConfig:faker = Faker(locale="zh_CN")Assert = Assertlog = get_log()def __init__(self, driver: webdriver):""":param driver: 驱动"""self.driver = driverdef find_element(self, locator: tuple, timeout=5):"""find_element:param locator: (xpath,xxx)(id,xxx):param timeout: timeout:return:"""try:element = WebDriverWait(self.driver, timeout).until(EC.presence_of_element_located(locator))self.log.info(f"[{self.find_element.__name__}] locator: {locator} element: {str(element)}")return elementexcept WebDriverException as e:self.log.error(e)allure.attach(self.get_png(), "异常截图", allure.attachment_type.PNG)allure.attach("未找到: {}, 详细信息如下: {}".format(locator, e))raise e

自定Assert

很常规 没什么可说的

class Assert:@staticmethoddef assert_unequal(exp, res):try:log.info("ASSERT UNEQUAL: EXPECT IS [{}] RESULT IS [{}]".format(exp, res))assert exp != resreturn Trueexcept AssertionError as e:log.error("ASSERT UNEQUAL ERROR: EXPECT IS [{}] RESULT IS [{}]".format(exp, res))raise e

关于页面元素

根据op 依旧对页面抽象成类 并继承SeleniumBaseConfig

元素类1一对1配套

class RegisterMethod(SeleniumBaseConfig):def __init__(self, driver):super().__init__(driver)def register(self, mobile: str):"""注册:param mobile:return:"""self.send_keys(RegisterLogin.INPUT_NUMBER, mobile)self.click(RegisterLogin.NEXT_STEP_BUTTON)time.sleep(1)

对应元素类

class Register:# ================== 公共 ================# 下一步NEXT_STEP_BUTTON = ("xpath", "//button")# 返回RETURN = ('xpath', '//p[@class="return-prev-step"]')# input 错误信息INPUT_ERROR_INFO = ("xpath", '//p[@class="default-input-error"]')# ================== home界面 ================CREATE_BUTTON = ("xpath", "//div[text()='创建']") # 创建buttonJOIN_BUTTON = ("xpath", "//div[text()='加入']") # 创建buttonINPUT_TEAM_NAME = ("xpath", '//input[@placeholder="请输入团队名称"]') # 输入团队名称INPUT_YOUR_NAME = ("xpath", '//input[@placeholder="请输入你的姓名"]') # 输入个人名称

测试用例

@pytest.mark.P3@allure.title("创建新团队:姓名输入汉字、英文、数字字符")@pytest.mark.flaky(reruns=1, reruns_delay=2)def test_create_team_username(self):"""姓名输入汉字、英文、数字字符1.register2.create team3.create_team_username"""pics = []self.driver.register(self.mobile)exp = "true"res = self.driver.check_button_disabled()self.driver.allure_report(self.test_create_team_username, pics, exp, res)self.driver.Assert.assert_equal(exp, res)

Config 配置

一般配置host等数据 使用ConfigParser读取写入

config.ini

[loginParam]username = usernamepassword = 123456[domain]cm = xx

class Config:def get_conf(self, title, value):"""read .ini:param title::param value::return:"""log.info("{} : {}:{}".format(self.get_conf.__name__, title, value))return self.config.get(title, value)def set_conf(self, title, value, text):"""change .ini:param title::param value::param text::return:"""log.info("{} : {}:{}:{}".format(self.set_conf.__name__, title, value, text))self.config.set(title, value, text)with open(self.config_path, 'w+') as f:return self.config.write(f)

main 启动

使用sys.argv获取命令行自定义参数

if len(sys.argv) > 3:runCase = sys.argv[1] # P1 P2 P3 ALL 指定运行的用例等级if runCase != "ALL":case_path = f"-m={runCase}"xml = sys.argv[2] # 报告路径if xml:xml_report_path = xmlBUILD_NUMBER = sys.argv[3]args = ["-s", "-v", "-n=auto", case_path, '--alluredir', xml_report_path, '--clean-alluredir']pytest.main(args=args)

图片相对定位 获得坐标

场景

canvas 页面 无元素可定位

使用

先截取想要点击的位置图片 保存到本地

find_me传入driver对象

通过center_xcenter_y属性方法 获得相对坐标

class GraphicalLocator():def __init__(self, img_path):self.locator = img_path# x, y position in pixels counting from left, top cornerself.x = Noneself.y = Noneself.img = cv2.imread(img_path)self.height = self.img.shape[0]self.width = self.img.shape[1]self.threshold = None@propertydef center_x(self):return self.x + int(self.width / 2) if self.x and self.width else None@propertydef center_y(self): return self.y + int(self.height / 2) if self.y and self.height else Nonedef find_me(self, drv): # Clear last found coordinatesself.x = self.y = None# Get current screenshot of a web pagescr = drv.get_screenshot_as_png()# Convert img to BytesIOscr = Image.open(BytesIO(scr))# Convert to format accepted by OpenCVscr = numpy.asarray(scr, dtype=numpy.float32).astype(numpy.uint8)# Convert image from BGR to RGB formatscr = cv2.cvtColor(scr, cv2.COLOR_BGR2RGB)# Image matching works only on gray images# (color conversion from RGB/BGR to GRAY scale)img_match = cv2.minMaxLoc(cv2.matchTemplate(cv2.cvtColor(scr, cv2.COLOR_RGB2GRAY),cv2.cvtColor(self.img, cv2.COLOR_BGR2GRAY),cv2.TM_CCOEFF_NORMED))# Calculate position of found elementself.x = img_match[3][0]self.y = img_match[3][1]# From full screenshot crop part that matches template imagescr_crop = scr[self.y:(self.y + self.height),self.x:(self.x + self.width)]# Calculate colors histogram of both template# and matching images and compare themscr_hist = cv2.calcHist([scr_crop], [0, 1, 2], None,[8, 8, 8], [0, 256, 0, 256, 0, 256])img_hist = cv2.calcHist([self.img], [0, 1, 2], None,[8, 8, 8], [0, 256, 0, 256, 0, 256])comp_hist = pareHist(img_hist, scr_hist,cv2.HISTCMP_CORREL)# Save treshold matches of: graphical image and image histogramself.threshold = {'shape': round(img_match[1], 2), 'histogram': round(comp_hist, 2)}# Return image with blue rectangle around matchreturn cv2.rectangle(scr, (self.x, self.y),(self.x + self.width, self.y + self.height),(0, 0, 255), 2)

pytest

pytest 有很多好用的功能、比如失败重跑、多进程并行用例、fixture测试

用例参数化等等

详见 gitHUB-SeleniumUIAuto 中的 readme

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。