当前位置: 首页 > news >正文

wordpress 大网站中国企业诚信网

wordpress 大网站,中国企业诚信网,书画艺术网站建设概况,水利网站建设情况汇报测试框架的设计有两种思路#xff0c;一种是自底向上#xff0c;从脚本逐步演变完善成框架#xff0c;这种适合新手了解框架的演变过程。另一种则是自顶向下#xff0c;直接设计框架结构和选取各种问题的解决方案#xff0c;这种适合有较多框架事件经验的人。本章和下一张…测试框架的设计有两种思路一种是自底向上从脚本逐步演变完善成框架这种适合新手了解框架的演变过程。另一种则是自顶向下直接设计框架结构和选取各种问题的解决方案这种适合有较多框架事件经验的人。本章和下一张分别从两种设计思路来介绍框架的搭建过程。从脚本到用例相比于一堆测试脚本使用规范化的测试用例格式会方便我们灵活的执行和管理用例。一个完整的自动化测试用例应包含测试准备setup测试准备步骤、用例辅助方法或工具可以共用测试步骤test steps核心测试步骤断言assertions期望结果于实际结果的比对用例可以报告不止一个断言测试清理teardown对执行测试造成的影响进行清理和还原以免影响后续执行可以共用。编写测试函数将测试脚本转化为Pytest测试用例的方法非常简单只要将测试过程编写为test开头的测试函数即可。有时候我们为了快速实现一个功能会直接把代码按步骤写到模块里如下例代码test_baidu_search_v0.9.py内容Copyfrom selenium import webdriver from time import sleepdriver webdriver.Chrome() driver.get(https://www.baidu.com) driver.find_element_by_id(kw).send_keys(博客园 韩志超) driver.find_element_by_id(su).click() sleep(1) if韩志超in driver.title:print(通过) else:print(失败) driver.quit()然后我们开启第一步优化首先可以把步骤写到一个函数里这样方便在脚步中写多个用例另外我们可以按照Pytest测试框架用例到写法写成标准的用例。期望结果的判断我们使用标准的assert断言语句修改后如下代码test_baidu_search_v1.0.py内容Copyfrom selenium import webdriver from time import sleepdeftest_baidu_search_01():driver webdriver.Chrome()driver.get(https://www.baidu.com)driver.find_element_by_id(kw).send_keys(博客园 韩志超)driver.find_element_by_id(su).click()sleep(1)assert韩志超in driver.title, 标题不包含韩志超# 自定义失败消息driver.quit()不同于v0.9版Python脚本的运行方法命令行使用python 脚步路径Pytest用例脚本使用pytest 脚本路径或python -m pytest 脚本路径来执行。我们也可以在Pytest用例脚本下面加上以下语句Copyif __name__ __main__:pytest.main([__file__])这样便可以像Python脚本一样直接运行。其中__file__指当前脚本也可以添加其他运行参数如-qs等。使用断言测试用例中必须包含期望结果来验证执行的通过与否。不同于“调试”需要有人值守来人工判断没个执行过程是否通过自动化“测试”往往需要批量运行并自动判断用例是否通过。断言即是执行过程中的实际结果与期望结果的自动对比。Pytest中使用标准的assert语句来进行断言。assert断言语句在用例执行失败时和期望结果不一致会抛出AssertionError异常测试框架会自动捕获该异常并将用例标记为执行失败状态并且不会因为异常导致执行中断而影响其他用例的执行。注在用例中也可以使用if判断配合pytest.fail()或者手动抛出AsserionError异常来将用例设置为失败状态示例如下Copyif韩志超notin driver.title:# rasie AssersionError(标题不包含韩志超)pytest.fail(标题不包含韩志超)Web UI自动化测试过程中常用的断言策略有以下几种流程成功执行视为通过按确定的元素操作步骤可以正常完成整个流程视为通过通过标题断言通过当前网页标题driver.title来判断处于某一页面上通过URL断言通过当前URLdriver.current_url来判断处于某一页面上通过页面源码断言通过网页源代码driver.page_source中包含特定信息来判断处于某一页面上通过存在特定元素断言通过存在某个特定元素来判断处于某一页面上。通过元素判断是否在某一页面上的示例如下Copyfrom selenium import webdriver from selenium.common.exceptions import NoSuchElementExceptiondeftest_open_baidu():driver webdriver.Chrome()driver.get(https://www.baidu.com)try:driver.find_element_by_id(kw) # 尝试定位搜索框except NoSuchElementException:pytest.fail(不存在搜索框)在框架中可以将常用的断言方法进行封装以方便使用。分离测试准备及清理方法在测试用例中我们要尽可能的分离核心测试步骤将可以共用的测试准备及测试清理步骤单独提取出来以方便复用。在上例中我们核心的测试步骤是从打开百度网站到断言网页标题而启动浏览器和关闭浏览器可以视为测试准备和测试清理方法。测试准备和测试清理方法我们可以使用Pytest中的setup_function()及teardown_function()方法也可以使用自定义的Fixture方法来吧两个方法集中的一个函数中如下例代码test_baidu_search_v3.py内容Copyfrom time import sleepfrom selenium import webdriver import pytestdefsetup_function():global driverdriver webdriver.Chrome()defteardown_function():driver.quit()deftest_baidu_search_01(driver):driver.get(https://www.baidu.com)driver.find_element_by_id(kw).send_keys(博客园 韩志超)driver.find_element_by_id(su).click()sleep(1)assert韩志超in driver.titleif __name__ __main__:pytest.main([__file__])使用自定义Fixture方法代码test_baidu_search_v4.py内容Copyfrom time import sleepfrom selenium import webdriver import pytestpytest.fixturedefdriver():dr webdriver.Chrome()yield drdr.quit()deftest_baidu_search_01(driver):driver.get(https://www.baidu.com)driver.find_element_by_id(kw).send_keys(博客园 韩志超)driver.find_element_by_id(su).click()sleep(1)assert韩志超in driver.titleif __name__ __main__:# --html需要pip install pytest-htmlpytest.main([__file__, --htmlreport.html,--self-contained-html])上例中我们自定义了一个名为driver的Fixture方法。yield上面对的所有语句属于测试准这里创建了一个浏览器驱动对象dr。yield语句将dr对象交给用例执行并等待用例执行完毕再执行下面的测试清理语句退出浏览器。用例中使用Fixture函数名driver作为参数来注入测试准备和测试清理方法用例中使用的driver即Fixture函数yield返回的dr浏览器驱动对象。使用Pytest-selenium插件Pytest框架的优点之一是拥有很多功能丰富的插件。使用这些插件可以省略我们自己编写Fixture方法的过程直接安装使用。上例中我们自己编写了一个名为driver的fixture方法我们也可以直接使用Pytest-Selenium插件该插件提供了一个全局的driver或seleniumFixture方法可以直接使用并且支持切换使用的浏览器。安装Pytest-Selenium插件并修改代码如下代码test_baidu_search_v5.py内容Copyfrom time import sleepfrom selenium import webdriver import pytestdeftest_baidu_search_01(driver):driver.get(https://www.baidu.com)driver.find_element_by_id(kw).send_keys(博客园 韩志超)driver.find_element_by_id(su).click()sleep(1)assert韩志超in driver.titleif __name__ __main__:# --html需要pip install pytest-html# --driver 需要pip install pytest-seleniumpytest.main([__file__, --driverchrome, --htmlreport.html,--self-contained-html])pytest-selenium还支持配置浏览器选项及配合pytest-html失败自动截图等功能详细可以参考其官方使用文档https://pytest-selenium.readthedocs.io/en/latest/。注pytest-selenium默认会拦截所有接口请求可以在pytest.ini中配置sensitive_url 来设置无敏感url。生成测试报告使用Pytest框架生成测试报告最常用的插件有pytest-html和allure-pytest两种前者简单可以生成单文件测试报告。后者华丽功能强大使用较为复杂。本章我们使用pytest-html来生成报告allure-pytest的具体使用下章讲解。pytest-html的使用方式非常简单安装pytest-html并使用--html来生成报告即可if name main:# --html需要pip install pytest-htmlpytest.main([file, --htmlreport.html,--self-contained-html])注如果想自己生成HTML测试报告可以在conftest.py文件中通过pytest_terminal_summary钩子方法terminalreporter参数对象的stats属性结合三方库Jinjia2来自定义生成报告。增加易维护性众所周知UI的变动导致Web自动化用例的维护成本非常高当一个元素变动时如登录按钮所有使用到这个元素的用例都将因此而失败逐个修改每一条用例的成本是非常高的。最好的做法就是使用模块封装的方式来隔离变动隔离变动旨在隔离易变的和稳定的常用的策略为代码隔离易变如元素定位和稳定的页面操作可以使用模块封装的方式对易变的操作进行封装数据变动较频繁建议与代码隔离以降低代码的修改配置配置也是数据的一种主要用于增加框架使用的灵活性配置变动也较频繁讲义与代码隔离。另外使用数据驱动、添加日志和失败自动截图也是快速定位问题、降低维护成本的有效方法。元素失败自动截图我们可以封装通用的定位元素方法来代替driver.find_element(),在其中捕获异常并截图。并且为了方便区分元素定位元素时为元素添加了一个高亮黄色的边框。实现方式如下Copyimport time import os from selenium import webdriver from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.common.exceptions import TimeoutException, NoSuchElementExceptionSNAPSHOTS_DIR snapshotsdeffind_element(driver: webdriver.Chrome, by, value, timeout5):style background: green; border: 2px solid red;js arguments[0].setAttribute(style, arguments[1]);try:WebDriverWait(driver, timeout).until(EC.presence_of_element_located((by,value)))except TimeoutException:snapshot_file snapshot_%s.png % int(time.time())driver.save_screenshot(os.path.join(SNAPSHOTS_DIR, snapshot_file))raise NoSuchElementException(%s 秒内未找到元素 %s%s % (timeout, by, value))else:element driver.find_element(by, value)driver.execute_script(js, element, style) # 添加高亮样式return element分层-封装测试步骤我们可以使用分层的方式将每个测试步骤如打开百度、输入关键词、点击搜索按钮等封装成函数以供用例调用。我们可以每个元素操作封装一个函数也可以封装一个包含这3步操作等搜索函数来完成所有步骤。前一种方法虽然麻烦但可以保证步骤操作的灵活性并自由组合如打开百度其他用例也可使用如输入关键词后不点击搜索按钮等示例代码如下代码test_baidu_search_v6.py内容Copyfrom time import sleepfrom selenium import webdriver import pytestdeffind_element(driver, by, value, timeout5):...defopen_baidu(driver):print(打开百度)driver.get(https://www.baidu.com)definput_keyword(driver, keyword):print(f输入关键字 {keyword})find_element(driver, id, kw).send_keys(keyword)defclick_search_btn(driver):print(点击百度一下按钮)find_element(driver, id, su).click()deftest_baidu_search_01(driver):open_baidu(driver) # Step 01input_keyword(driver, 博客园 韩志超) # Step 02click_search_btn(driver) # Step 03sleep(1)assert韩志超in driver.title # 断言if __name__ __main__:# --html需要pip install pytest-htmlpytest.main([__file__, --htmlreport.html,--self-contained-html])当我们将元素的操作进行封装以实现只在一个地方定位和操作易变的元素。所有使用到该元素的该操作时如输入关键词都应该调用封装的函数而不是直接定位函数完成操作。这样当元素变动是只需要修复所封装的元素操作方法即可用例不用修改。这大大降低了维护成本。分离测试数据相对于代码来说测试数据是易变的同时不同悲催环境使用的测试数据集也应该不一样。在数据量较少的情况下我们可以用一个JSON或YAML文件来存储所需的测试数据。文件data.json内容Copy{keywords:[博客园 韩志超临渊,简书 韩志超]}文件data.yaml内容Copykeywords:-博客园韩志超-临渊-简书韩志超代码test_baidu_search_v7.py内容Copyimport jsonimport yaml # 需要pip install pyyaml安装 import pytestdef load_json(file_path):print(f加载JSON文件{ file_path })with open(data.json) as f:return json.load(f)def load_yaml(file_path):print(f加载YAML文件{ file_path })with open(data.json) as f:return yaml.safe_load(f)pytest.fixture def case_data():# return load_json(demo.json)return load_yaml(demo.yaml)pytest.fixture def driver():dr webdriver.Chrome()yield drdr.quit()def test_baidu_search_01(driver, case_data):keyword case_data[keywords][0] # 从用例数据中选取指定数据driver.get(https://www.baidu.com)driver.find_element_by_id(kw).send_keys(keyword)driver.find_element_by_id(su).click()sleep(1)assert 韩志超 in driver.title注Fixtrue函数不建议用使用test_开头如test_data定义fixture以免识别为测试用例。使用数据驱动示例代码如下Copyimport pytestKEYWORD_LIST load_yaml(demo.yaml)[keywords]pytest.mark.paramitrize(keyword, KEYWORD_LIST)deftest_baidu_search_01(driver, keyword): # keyword对应每一个要搜索的关键词driver.get(https://www.baidu.com)driver.find_element_by_id(kw).send_keys(keyword)driver.find_element_by_id(su).click()sleep(1)assert keyword in driver.title # 有可能失败使用日志在项目中必要的输出信息可以帮助我们显示测试步骤的一些中间结果和快速的定位问题虽然Pytest框架可以自动捕获print信息并输出屏幕或报告中当时更规范的应使用logging的记录和输出日志。 相比print, logging模块可以分等级记录信息。日志等级实用方法层、页面对象层、Fixture业务层、用例层都可以直接使用logging来输出日志, 使用方法。Copy# test_logging.pyimport loggingdeftest_logging():logging.debug(调试信息)logging.info(步骤信息)logging.warning(警告信息一般可以继续进行)logging.error(出错信息)try:assert0except Exception as ex:logging.exception(ex) # 多行异常追溯信息Error级别logging.critical(严重出错信息)使用pytest运行不会有任何的log信息因为Pytest默认只在出错的信息中显示WARNING以上等级的日志。 要开启屏幕实时日志并修改log显示等级。Log等级: NOTSET DEBUG INFO WARNING(WARN) ERROR CRITICALCopy# pytest.ini[pytest]log_cliTruelog_cli_levelINFO运行pytest test_logging.py查看结果Copy----------------------------- live log call ------------------------------- INFO root:test_logging.py:5 步骤信息 WARNING root:test_logging.py:6 警告信息一般可以继续进行 ERROR root:test_logging.py:7 出错信息 ERROR root:test_logging.py:11 assert 0 Traceback (most recent call last):File /Users/apple/Desktop/demo/test_logging.py, line 9, in test_loggingassert 0 AssertionError: assert 0 CRITICAL root:test_logging.py:12 严重出错信息由于日志等级设置的为INFO级别因此debug的日志不会输出。对于不同层日志级别的使用规范可以在实用方法层输出debug级别的日志如组装的文件路径文件读取的数据执行的sqlsql查询结果等等。 在PageObject层输出info级别的日志如执行某个页面的某项操作等。 Fixtures层和用例层可以根据需要输出一些必要的infowarning或error级别的信息。日志格式默认的日志格式没有显示执行时间我们也可以自定义日志输出格式。Copy# pytest.ini ... log_cli_format%(asctime)s %(levelname)s %(message)s log_cli_date_format%Y-%m-%d %H:%M:%S %(asctime)s表示时间默认为Sat Jan 13 21:56:34 2018这种格式我们可以使用log_cli_date_format来指定时间格式。 %(levelname)s代表本条日志的级别 %(message)s为具体的输出信息再次运行pytest test_logging.py显示为以下格式Copy-------------------------------- live log call ------------------------------- 2019-11-06 21:44:50 INFO 步骤信息 2019-11-06 21:44:50 WARNING 警告信息一般可以继续进行 2019-11-06 21:44:50 ERROR 出错信息 2019-11-06 21:44:50 ERROR assert 0 Traceback (most recent call last):File /Users/apple/Desktop/demo/test_logging.py, line 9, in test_loggingassert 0 AssertionError: assert 0 2019-11-06 21:44:50 CRITICAL 严重出错信息更多日志显示选项%(levelno)s: 打印日志级别的数值%(pathname)s: 打印当前执行程序的路径其实就是sys.argv[0]%(filename)s: 打印当前执行程序名%(funcName)s: 打印日志的当前函数%(lineno)d: 打印日志的当前行号%(thread)d: 打印线程ID%(threadName)s: 打印线程名称%(process)d: 打印进程ID输出日志到文件在pytest.ini中添加以下配置Copy... log_file logs/pytest.log log_file_level debug log_file_format %(asctime)s %(levelname)s %(message)s log_file_date_format %Y-%m-%d %H:%M:%Slog_file是输出的文件路径输入到文件的日志等级、格式、日期格式要单独设置。 遗憾的是输出到文件的日志每次运行覆盖一次不支持追加模式。用例依赖处理一般来说不建议用例之间存在顺序依赖。用例应该不依赖其他任何用例能够独立运行。加入确实存在步骤的先后顺序如Copydeftest_add_customer():passdeftest_query_customer():passdeftest_delete_customer():pass假设测试查询客户及测试删除用户需要先添加用户常用的处理方法如下使用步骤封装代替用例顺序依赖将业务步骤单独封装并在用例中进行调用如Copydefadd_customer():passdefquery_customer():passdefdelete_customer():passdeftest_add_customer():add_customer()deftest_query_customer():add_customer()query_customer()deftest_delete_customer():add_customer()delete_customer()虽然add_customer()方法会执行多次但是每条用例都可以单独执行比较推荐这种方式。使用例按顺序执行如果想要强制用例有序可以使用插件pytest-ordering使用pip安装后使用方式如下Copypytest.mark.run(order1)deftest_add_customer():passpytest.mark.run(order2)deftest_query_customer():passpytest.mark.run(order3)deftest_delete_customer():pass增加灵活性实现多环境切换对于自动化测试框架来说希望能一套用例来可以跑多套环境。不同环境的执行流程基本是一样的不一样的是服务器地址base_url和所使用的数据。我们可以使用pytest-base-url插件配合pytest-variables插件来实现服务器地址和测试数据的切换。示例如下Copyfrom time import sleepfrom selenium import webdriver import pytestdeftest_baidu_search_01(driver, base_url, variables):url base_url /keyword variables[keywords][0]driver.get(url)driver.find_element_by_id(kw).send_keys(keyword)driver.find_element_by_id(su).click()sleep(1)assert韩志超in driver.titleif __name__ __main__:# --html需要pip install pytest-html# --driver 需要pip install pytest-selenium# --base-url 需要pip install pytest-base-url# --variables 需要安装 pip install pytest-variablespytest.main([__file__, --driverchrome, --htmlreport.html,--self-contained-html, --base-urlhttps://www.baidu.com, --variablestest.json])测试环境数据test.json内容如下Copy{keywords:[博客园 韩志超临渊,简书 韩志超]}由于pytest-selenium默认把所有url当作敏感url我们需要在pytest.ini中通过配置进行关闭即设置无敏感url。具体设置方法如下Copy[pytest]sensitive_url None用例标记除了使用目录来按模块来整理用例外我们也可以通过规范用例命令规则及自定义标签来组织用例。除pytest.mark.skip、pytest.mark.skipIf、pytest.mark.xfail、pytest.mark.paramitrize等系统标记外我们可以自定义任何标记来使用如使用smoke标记冒烟用例使用destructive标记破坏性用例有修改操作未还原的使用abnormal标记异常用例使用flaky标记不稳定用例使用h5标记H5相关用例。在严格模式下可用标签需要在pytest.ini例出来以防止随意使用任意标签导致的标签混乱问题。在pytest.ini文件注册标签如下Copy[pytest]markers smoke: smoke test casedestructive: destructive test caseabnormal: abnormal test caseflaky: flaky test caseh5: h5 test casehzc: testcase by hzc 用例标记方式如下 python pytest.mark.smoke def test_baidu_search_01(driver, base_url, variables):...用例可以添加多个标记运行时可以使用pytest -m 命令挑选标签执行如Copypytest -m smoke and h5即运行带例smoke和h5两个标签的用例另外也支持ornot等多个标签的逻辑判断。在规划标记是也可以按维护人添加标记以方便运行某人负责的所有用例。用例等级除了自定义用例标记外我们可以对用例重要性进行评级来快速回归不同优先级的用例。对用例进行标记等级我们可以使用三方插件pytest-level。安装方式Copypip install pytest-level标记用例Copypytest.mark.smokepytest.mark.level(1)deftest_baidu_search_01(driver, base_url, variables):...运行方式Copypytest --level1用例顺序在某些情况下我们如何希望用例有序可以使用pytest-ordering插件实现。安装方法Copypip install pytest-ordering标记用例Copyimport pytestpytest.mark.run(order1)deftest_login():passpytest.mark.run(order2)deftest_add_goods():passpytest.mark.run(order3)deftest_query_goods():passpytest.mark.run(order4)deftest_del_goods():pass运行时用例便可按数字从小到大的顺序运行。一般情况下不建议用例之间有顺序依赖。每条用例应该可以独立执行的有依赖的测试用例建议作为测试步骤放到一个大的场景用例中去这样可以确保执行的有序如Copydeflogin(username, password):passdefadd_goods(goods_name, *args):passdefquery_goods(goods_name):passdefdel_goods(goods_name):passdeftest_login():login(user, pwd)# ... 断言结果判断deftest_add_goods():login(user, pwd)add_goods(...)# ... 断言结果判断deftest_query_goods():login(user, pwd)add_goods(...)query_goods(...)# ... 断言结果判断deftest_del_goods():login(user, pwd)add_goods(...)query_goods(...)del_goods(...)# ... 断言结果判断也可以写一个大的场景用例包含4个测试点点验证这看起来有很多冗余并且在一个用例中如test_del_goods中登录、添加商品、查询商品应该被视为是测试准备setup只保留核心的del_goods(...)作为测试步骤。Copydeftest_login_add_query_del_goods():login(user, pwd)# ... 断言结果判断add_goods(...)# ... 断言结果判断query_goods(...)# ... 断言结果判断del_goods(...)# ... 断言结果判断这样步骤永远是有序的一个步骤失败后续步骤将中断不再执行。另外针对上面每个验证点分开的用例形式我们可以使用Fixture模块化的特性采用步骤渐进的方式来编写每一个带依赖的步骤示例如下Copyimport pytestpytest.fixturedeflogin():# fixture一般不使用普通参数默认用户名密码需要确定并写在函数中useranme, password user, pwd# ... 业务逻辑pytest.fixturedefadd_goods(login): # 依赖login步骤passpytest.fixturedefquery_goods(add_goods): # 依赖add_goods步骤passdeftest_login(): # 作为参数引用login步骤login(user, pwd)# ... 断言结果判断deftest_add_goods(login):add_goods(...)# ... 断言结果判断deftest_query_goods(add_goods):query_goods(...)# ... 断言结果判断deftest_del_goods(query_goods):del_goods(...)# ... 断言结果判断这样做的好处是任何一个用例都可以单独执行。缺点是一起执行时登录、添加商品等会执行不止一遍。不稳定用例处理不稳定用例flaky tests是UI自动化测试过程中一个典型的问题。主要的策略有暂时跳过用例等环境或用例稳定后再运行为用例设置超时时间防止卡死用例失败后自动重试以下为3种策略的具体实现方式。标记跳过用例对于不稳定的用例暂时跳过用例是最常用的方法之一。在用例上使用pytest.mark.skip()、pytest.mark.skipIf()或在Fixture函数、测试用例中使用pytest.skip()方法即可跳过该用例。使用超时时间未避免用例卡死长时间未结束我们可以使用pytest-timeout为用例统一或分别添加超时时间。安装方法Copypip install pytest-timeout使用方法如下全局使用Copypytest --timeout300配置方法Copy[pytest]timeout 300单独使用Copypytest.mark.timeout(60)deftest_foo():pass用例失败重跑对于不稳定用例失败后立即重试可以应对一些环境或UI不稳定导致的一些用例失败的问题。我们可以很方便的借助pytest-rerunfailures这个插件来实现这个功能。安装方法Copypip install pytest-rerunfailures使用方法Copypytest -rerun 3 rerun-delay 1即每次失败后延迟1秒进行重试最多重试3次有一次成功则视为成功。3次都失败则视为失败。从面向过程到面向对象按页面归类元素操作Page Object模式即Page Object Module页面对象模型模式是一种基于模块的框架结构。以页面为对象来统一管理页面上元素的定位及操作修改后示例如下代码test_baidu_search_v7内容Copyfrom time import sleepfrom selenium import webdriver import pytestclassBaiduHomePage:url https://www.baidu.comsearch_ipt_loc (id, kw) # 百度搜索框search_btn_loc (id, su) # 百度一下按钮def__init__(self, driver): # 初始化传入driverself.driver driver # 绑定页面对象defopen(self):print(打开百度页面)self.driver.get(self.url)definput_search_keyword(self, keyword):print(f输入搜索关键词 {keyword})self.driver.find_element(*self.search_ipt_loc).send_keys(keyword)defclick_search_button(self):print(点击百度一下按钮)self.driver.find_element(*self.search_btn_loc).click()defsearch(self, keyword): # 页面常用组合操作print(f搜索关键字 {keyword})self.open()self.input_search_keyword(keyword)self.click_search_button()sleep(0.5)pytest.fixturedefbaidu_home(driver): # 自定义一个fixture方法方便多个用例共享page_obj BaiduHomePage(driver)return page_objdeftest_baidu_search_01(driver, baidu_home):baidu_home.seach(博客园 韩志超)assert韩志超in driver.titleif __name__ __main__:# --html需要pip install pytest-html# --driver 需要pip install pytest-seleniumpytest.main([__file__, --driverchrome, --htmlreport.html,--self-contained-html])上例BaiduHomePage中除了封装了每个元素的单独操作外还封装了组合的search操作这样既可以灵活使用如只输入搜索词不点击搜索按钮也方便用例中快速使用组合操作。上例中没有把页面对象baidu_home的实例化放到用例中而是单独封装了一个Fixture方法这样的好处是所有需要用到此页面对象的用例都可以直接使用。框架封装的一个设计方向就是让用户的使用尽可能简单。用例的编写便是用户的一个高频使用场景我们通过设计要使的用例的编写尽可能简单。封装常用方法除了用例的编写外页面模型也是需要用户进行编辑和新增的如何使页面模型的编写更简单呢比如每个页面模型都要编写初始化方法传入driver比如常用的通过节点文本定位、鼠标悬浮、强制等待、主动等待、偶现元素处理等。我们可以编写一个页面基础类作为所有所有页面对象的父类在页面基础类中实现这些操作。如下例代码test_baidu_search_v8.py内容Copyfrom time import sleepfrom selenium import webdriver from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.common.exceptions import NoSuchElementException, NoAlertPresentException import pytestclassBasePage:url Nonedef__init__(self, driver): # 初始化传入driverself.driver driver # 绑定页面对象 propertydeftitle(self):return self.driver.titlepropertydefpage_source(self):return self.driver.page_sourcedefopen(self, urlNone):url url or self.url # 如果没有指定url则打开页面urlprint(f打开 {url})if url:self.driver.get(self.url)return self # 返回self以支持链式操作如page.open().click_element()defwait(self, secs1):print(f等待 {secs}s)sleep(secs)return selfdeffind_element(self, by, value, timeoutNone, ignore_errorFalse):元素定位方法增加显式等待和忽略异常选项处理偶现元素try:if timeout isNone:return self.driver.find_element(by, value)else:return WebDriverWait(self.driver, timeout).until(EC.presence_of_element_located((by, value)))except NoSuchElementException:if ignore_error isFalse: # 不忽略错误则抛出异常raisedefclick_element(self, by, value, timeoutNone, ignore_errorFalse):print(f点击元素 {by}{value} 超时 {timeout} 忽略异常 {ignore_error})self.find_element(by, value, timeout, ignore_error).click()return selfdefinput_text(self, by, value, text, timeoutNone):print(f向元素 {by}{value} 输入文本 {text} 超时 {timeout})elm self.find_element(by, value, timeout)elm.clear()elm.send_keys(text)return selfdefmove_to_element(self, by, value):print(移动到元素 {by}{value})elm self.find_element(by, value)ActionChains(self.driver).move_to_element(elm).perform()return selfdefswitch_to_frame(self, *frames):print(f切换到框架 { .join(frames)})for frame in frames:self.driver.switch_to.frame(frame)defswitch_to_window(self, index):print(f切换到第{index1}个窗口)window_list self.driver.window_handlesself.driver.switch_to.window(window_list[index])return selfdefdismiss_alert(self, ignore_errorFalse):print(关闭警告弹框)try:self.driver.switch_to.alert().dissmiss()except NoAlertPresentException:if ignore_error isFalse:raisereturn selfdefremove_attr(self, by, value, attr):print(f移除元素 {by}{value}{attr}属性)elm self.find_element(by, value)js_script farguments[0].removeAttribute({attr});self.driver.execute_script(js_script, elm)return self我们在BasePage页面基础类里我们将页面标题driver.title页面源码driver.page_source绑定给页面对象以方便获取。我们定义了一个open方法拥有打开指定url或者子类配置的url。重新封装了find_element并扩展了显式等待和对偶现元素的支持偶现元素定位不到视作未出现不报错。除了find_element方法返回元素对象外其他操作方法都返回self对象本身这样可以使页面对象支持链式操作如Copy... page BasePage(driver) page.open(https://www.baidu.com/).click_element(id,su).input_text(简书韩志超).wait()此外我们还封装了点击元素、输入文本、鼠标悬浮、切换窗口、框架、关闭警告框、移除元素属性等常用操作。我们在每种基本操作中增加了print信息使得执行过程更透明易懂。其他常用的操作读者可以根据需求自行补充其他封装方法或者直接使用对象.driver来调用driver的原生方法如定位一组元素Copy... page BasePage(driver) elm_list page.driver.find_elements_by_xpath(//li)有了页面基础类每个页面对象类写起来遍稍微简略点BaiduHomePage类修改后代码如下CopyclassBaiduHomePage(BasePage):url https://www.baidu.comsearch_ipt_loc (id, kw) # 百度搜索框search_btn_loc (id, su) # 百度一下按钮definput_search_keyword(self, keyword):self.input_text(*self.search_ipt_loc, keyword)defclick_search_button(self):self.click_element(*self.search_btn_loc)defsearch(self, keyword): # 页面常用组合操作print(f搜索关键字 {keyword})self.open().input_search_keyword(keyword).click_search_button().wait(0.5)首先集成BasePage类并无须再写__init__初始化方法直接配置url和页面元素对象即可。这里对单个元素操作不再添加额外打印信息使用基础方法click_element、input_text自带的打印信息。在组合操作search方法中我们使用了链式操作写起来更简洁。注在页面对象类的元素操作中也可以每个操作都返回self以使得上层测试用例再使用时支持链式操作。提高运行效率提高运行效率通常以下两种方式优化用例执行速度如使用Headless无界面模式、使用Cookie绕过登录、使用页面URL直达内部页面而不是通过页面一步步操作及使用接口、数据库而不是页面操作进行测准备等。并行执行并行是开多个浏览器同时执行多条用例这就要求我们的用例之间没有运行顺序的依赖用例可以单独运行。使用Headless模式Headless即无界面模式可以在一定程度上提高用例的执行速度。pytest-selenium插件提供了chrome_options的Fixture函数可以添加Chrome浏览器参数。我们只需要自定义一个--headless命令后选项重写chrome_options参数通过request这个内置的Fixture方法拿到配置对象config判断命令行选项是否包含--headless来添加对应的浏览器参数即可。实现方式如下文件conftest.py部分内容Copyimport pytestdefpytest_addoption(parser):parser.addoption(--headless, actionstore_true, helprun chrome headless)pytest.fixturedefchrome_options(request, chrome_options):if request.config.getoption(--headless):chrome_options.add_argument(--headless)return chrome_options 使用--headless运行测试用例 ... if __name__ __main__:# --html需要pip install pytest-html# --driver 需要pip install pytest-selenium# --base-url 需要pip install pytest-base-url# --variables 需要安装 pip install pytest-variablespytest.main([__file__, --driverchrome, --headless,--htmlreport.html,--self-contained-html,--base-urlhttps://www.baidu.com,--variablestest.json])多进程并行测试使用pytest-xdist可以启动多个进程来平均分发多个用例安装方法如下Copypip install pytest-xdist使用方法非常简单命令行中添加参数-n进程数即可。Copypytest -n3即启动3个进程来执行所有用例。发送邮件添加自定义选项和配置假设我们要实现一个运行完发送Email的功能。 我们自定义一个命令行参数项--send-email不需要参数值。当用户带上该参数运行时我们就发送报告不带则不发运行格式如下Copypytest test_cases/ --htmlreport.html --send-email这里一般应配合--html先生成报告。 由于Pytest本身并没有--send-email这个参数我们需要通过Hooks方法进行添加。文件conftest.py部分内容Copy def pytest_addoption(parser):Pytest初始化时添加选项的方法parser.addoption(--send-email, actionstore_true, helpsend email with test report)另外发送邮件我们还需要邮件主题、正文、收件人等配置信息。我们可以把这些信息配置到pytest.ini中如文件pytest.ini部分内容Copy... email_subject Test Report email_receivers superhin126.com email_body Hi,all\n, Please check the attachment for the Test Report.这里需要注意自定义的配置选项需要先注册才能使用注册方法如下**文件conftest.py部分内容Copydefpytest_addoption(parser):...parser.addini(email_subject, helptest report email subject)parser.addini(email_receivers, helptest report email receivers)parser.addini(email_body, helptest report email body)实现发送Email功能前面我们只是添加了运行参数和Email配置我们在某个生成报告时的Hook方法中根据参数添加发送Email功能示例如下**文件conftest.py部分内容Copyfrom utils.notify import Emaildefpytest_terminal_summary(config):Pytest生成报告时的命令行报告运行总结方法send_email config.getoption(--send-email)email_receivers config.getini(email_receivers).split(,)if send_email isTrueand email_receivers:report_path config.getoption(htmlpath)email_subject config.getini(email_subject) orTestReportemail_body config.getini(email_body) orHiif email_receivers:Email().send(email_subject, email_receivers, email_body, report_path)框架整理分类整理一个好的框架需要清晰的结构我们使用目录或包将不同的脚本进行归类例如testcases存放测试用例可以按模块建立子目录存放fixtures方法集中放在conftest.py中pages存放页面对象模型可以按模块建立子目录存放utils存放常用的工具方法的封装如发邮件功能的封装另外我们对输入的测试数据资源输出的测试报告、日志文件等也需要建立指定的目录存放如data/存放测试数据或资源reports/存放测试报告运行日志等再加上Pytest运行配置pytest.ini和一些说明文件如pytest.iniPytest配置文件requirements.txt运行依赖的三方包README.md框架说明文件。整个框架结构如下CopyWebAuto/| -- data/| -- test.json| -- prod.json| -- pages/| -- baidu_page.py| -- base_page.py| -- reports/| -- testcases/| -- test_baidu_search.py| -- utils/| -- send_email.pyconftest.pypytest.inirequirements.txtREADME.md敏感数据处理在测试环境中经常会用到一些身份认证信息如用户名、密码等这些属于敏感数据直接写在代码中有可能会造成敏感信息泄露。最简单的做法是将这些敏感信息配置到所运行机器如本机的环境变量中。如我们在自己电脑上的环境变量中添加两个变量CopyWEBAUTO_DEFAULT_USERadmin WEBAUTO_DEFAULT_PWD123456然后我们可以在代码中通过os.getenv()来获取指定的环境变量Copyimport osusername os.getenv(WEBAUTO_DEFAULT_USER) password os.getenv(WEBAUTO_DEFAULT_PWD)声明依赖文件一般来说框架不只是给自己一个人使用的多数情况下需要大家协作完成用例的补充。这时候我们一般要在项目中新建一个requirements.txt的来列出所有需要安装的三方包例如Copyselenium pytest pytest-selenium pytest-html pytest-variables pytest-timeout pytest-level pytest-base-url pytest-ordering pytest-rerunfailures pytest-xdist编写使用说明一个框架最好能有一个使用说明一样的文件简单阐述下框架的结构、有哪些特性、如何编写维护用例、如何运行等等。一般推荐使用Markdown语法编写。Markdown是一种标记语言可以通过不同的标记写出层次分明的文档示例如下Copy# WebAuto **项目Web自动化测试框架 使用Pytest Selenium基于POM模式搭建。## 特性 ## 安装方法 ## 使用方法实战案例光学理论是没用的要学会跟着一起敲要动手实操才能将自己的所学运用到实际当中去这时候可以搞点实战案例来学习。电商项目实战web测试项目webApph5小程序 测试项目接口自动化测试实战项目Linux实战项目面试资料我们进阶学习自动化测试必然是为了找到高薪的工作下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料并且有字节大佬给出了权威的解答刷完这一套面试资料相信大家都能找到满意的工作。以上资料对于想要测试进阶的朋友们来说应该会很有帮助需要的小伙伴可以后台私信找我免费领取。总结我见过很多leader在面试的时候遇到处于迷茫期的大龄程序员比面试官年龄都大。这些人有一些共同特征可能工作了好几年更夸张的是7、8年工作内容的重复性比较高没有什么技术含量的工作。凡事要趁早特别是技术行业一定要提升技术功底丰富自动化项目实战经验这对于你未来几年职业规划以及测试技术掌握的深度非常有帮助。如果对你有帮助的话点个赞收个藏给作者一个鼓励。也方便你下次能够快速查找。如有不懂还要咨询下方小卡片博主也希望和志同道合的测试人员一起学习进步在适当的年龄选择适当的岗位尽量去发挥好自己的优势。我的自动化测试开发之路一路走来都离不每个阶段的计划因为自己喜欢规划和总结测试开发视频教程、学习笔记领取传送门
http://www.w-s-a.com/news/387219/

相关文章:

  • 天猫网站建设的目标是什么做网站常见问题模板
  • 做php网站需要什么软件天津建设网官方网站
  • 南漳网站开发上海网站推广方法
  • 深圳seo网站大连旅顺房价
  • dede网站 地图什么做有没有做黑市网站
  • 做网站参考文献域名如何做网站
  • 怎么选择网站开发英文网站建设用途
  • 怎样做电子商务网站织梦生成手机网站
  • 公司网站建设选什么服务器网站里怎样添加关键词
  • 深圳建设局网站深业中城绿化项目营销型网站开发流程包括
  • 找销售的网站九江市建设项目服务中心
  • 东原ARC网站建设公司合肥seo网站推广外包
  • 那个网站是做房产中介的网站制作软件小学
  • 做网页怎么建站点视频解析网站
  • 做网站的系统设计网站设计论文前言
  • 做外贸网站多久更新汕头市建设局网站首页
  • 如何建设专业化的网站手机管理网站模板
  • 花生壳做网站如何用腾讯云做网站
  • 搭建集团网站开发app需要哪些软件
  • 网站建设 中企动力福州阀门wordpress 多说评论
  • php网站集成支付宝接口下载免费网络软件
  • 卡盟网站是怎么建设的用花生壳做网站速度可以吗
  • 杭州物联网前十名公司优秀seo平台
  • 网新中英企业网站管理系统wordpress 登录 缓存
  • wordpress模板建站教程wordpress添加广告位手机自适应
  • h5游戏平台入口优化是什么梗
  • 建设银行对公网站打不开网络推广活动方案主题和思路
  • 茶叶网站开发目的和意义网页设计需要考什么证
  • 高端企业网站建设公司怎么做实用性建设网站都需要哪些
  • 网站备案必须要幕布吗易企秀网站怎么做轮播图