├── .gitignore ├── README.md ├── config.py ├── conftest.py ├── page ├── __init__.py └── baidu_page.py ├── pytest.ini ├── requirements.txt ├── run_tests.py ├── test_dir ├── __init__.py ├── data │ └── data_file.json ├── test_baidu.py └── test_parametrize.py └── test_report └── 2020_07_19_23_01_06 ├── image └── test_bd_search.py_test_search.png ├── junit-xml.xml └── report.html /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.log 3 | __pycache__ 4 | .idea 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pyautoTest Web UI 自动化项目 2 | 3 | ### 特点 4 | 5 | * 全局配置浏览器启动/关闭。 6 | * 测试用例运行失败自动截图。 7 | * 测试用例运行失败可以重跑。 8 | * 测试数据参数化。 9 | 10 | ### 安装 11 | 12 | ```shell 13 | $ pip install -r requirements.txt 14 | ``` 15 | 16 | 注:安装```requirements.txt```指定依赖库的版本,这是经过测试的,有时候新的版本可会有错。 17 | 18 | ### 配置 19 | 20 | 在 `config.py` 文件配置 21 | 22 | ```python 23 | class RunConfig: 24 | """ 25 | 运行测试配置 26 | """ 27 | # 配置浏览器驱动类型。 28 | driver_type = "chrome" 29 | 30 | # 失败重跑次数 31 | rerun = "3" 32 | 33 | # 当达到最大失败数,停止执行 34 | max_fail = "5" 35 | 36 | # 运行测试用例的目录或文件 37 | cases_path = "./test_dir/" 38 | ``` 39 | 40 | ### 运行 41 | 42 | **不支持在编辑器(pycharm/ VS code ...)中运行,请在 cmd(windows)/终端(Linux)下执行。** 43 | 44 | ```shell 45 | $ python run_tests.py (回归模式,生成HTML报告) 46 | $ python run_tests.py -m debug (调试模式) 47 | ``` 48 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | import os 2 | PRO_PATH = os.path.dirname(os.path.abspath(__file__)) 3 | 4 | 5 | class RunConfig: 6 | """ 7 | 运行测试配置 8 | """ 9 | # 运行测试用例的目录或文件 10 | cases_path = os.path.join(PRO_PATH, "test_dir", "test_baidu.py") 11 | 12 | # 配置浏览器驱动类型(chrome/firefox/chrome-headless/firefox-headless)。 13 | driver_type = "chrome" 14 | 15 | # 失败重跑次数 16 | rerun = "1" 17 | 18 | # 当达到最大失败数,停止执行 19 | max_fail = "5" 20 | 21 | # 浏览器驱动(不需要修改) 22 | driver = None 23 | 24 | # 报告路径(不需要修改) 25 | NEW_REPORT = None 26 | -------------------------------------------------------------------------------- /conftest.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pytest 3 | from selenium import webdriver 4 | from selenium.webdriver import Remote 5 | from selenium.webdriver.chrome.options import Options as CH_Options 6 | from selenium.webdriver.firefox.options import Options as FF_Options 7 | from config import RunConfig 8 | 9 | # 项目目录配置 10 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 11 | REPORT_DIR = BASE_DIR + "/test_report/" 12 | 13 | 14 | # 设置用例描述表头 15 | def pytest_html_results_table_header(cells): 16 | cells.insert(2, 'Description') 17 | cells.pop() 18 | 19 | 20 | # 设置用例描述表格 21 | def pytest_html_results_table_row(report, cells): 22 | cells.insert(2, f'{report.description}') 23 | cells.pop() 24 | 25 | 26 | @pytest.hookimpl(hookwrapper=True) 27 | def pytest_runtest_makereport(item): 28 | """ 29 | 用于向测试用例中添加用例的开始时间、内部注释,和失败截图等. 30 | :param item: 31 | """ 32 | pytest_html = item.config.pluginmanager.getplugin('html') 33 | outcome = yield 34 | report = outcome.get_result() 35 | report.description = description_html(item.function.__doc__) 36 | extra = getattr(report, 'extra', []) 37 | if report.when == 'call' or report.when == "setup": 38 | xfail = hasattr(report, 'wasxfail') 39 | if (report.skipped and xfail) or (report.failed and not xfail): 40 | case_path = report.nodeid.replace("::", "_") + ".png" 41 | if "[" in case_path: 42 | case_name = case_path.split("-")[0] + "].png" 43 | else: 44 | case_name = case_path 45 | capture_screenshots(case_name) 46 | img_path = "image/" + case_name.split("/")[-1] 47 | if img_path: 48 | html = '
screenshot
' % img_path 50 | extra.append(pytest_html.extras.html(html)) 51 | report.extra = extra 52 | 53 | 54 | def description_html(desc): 55 | """ 56 | 将用例中的描述转成HTML对象 57 | :param desc: 描述 58 | :return: 59 | """ 60 | if desc is None: 61 | return "No case description" 62 | desc_ = "" 63 | for i in range(len(desc)): 64 | if i == 0: 65 | pass 66 | elif desc[i] == '\n': 67 | desc_ += ";" 68 | else: 69 | desc_ += desc[i] 70 | 71 | desc_lines = desc_.split(";") 72 | 73 | head = f"" 74 | 75 | # 构建 HTML 正文部分 76 | body = "" 77 | for line in desc_lines: 78 | body += f"

{line}

" 79 | body += "" 80 | 81 | # 完整的 HTML 文档 82 | desc_doc = f"{head}{body}" 83 | 84 | return desc_doc 85 | 86 | 87 | def capture_screenshots(case_name): 88 | """ 89 | 配置用例失败截图路径 90 | :param case_name: 用例名 91 | :return: 92 | """ 93 | global driver 94 | file_name = case_name.split("/")[-1] 95 | if RunConfig.NEW_REPORT is None: 96 | raise NameError('没有初始化测试报告目录') 97 | else: 98 | image_dir = os.path.join(RunConfig.NEW_REPORT, "image", file_name) 99 | RunConfig.driver.save_screenshot(image_dir) 100 | 101 | 102 | # 启动浏览器 103 | @pytest.fixture(scope='session', autouse=True) 104 | def browser(): 105 | """ 106 | 全局定义浏览器驱动 107 | :return: 108 | """ 109 | global driver 110 | 111 | if RunConfig.driver_type == "chrome": 112 | # 本地chrome浏览器 113 | driver = webdriver.Chrome() 114 | driver.maximize_window() 115 | 116 | elif RunConfig.driver_type == "firefox": 117 | # 本地firefox浏览器 118 | driver = webdriver.Firefox() 119 | driver.maximize_window() 120 | 121 | elif RunConfig.driver_type == "chrome-headless": 122 | # chrome headless模式 123 | chrome_options = CH_Options() 124 | chrome_options.add_argument("--headless=new") 125 | chrome_options.add_argument('--disable-gpu') 126 | driver = webdriver.Chrome(options=chrome_options) 127 | 128 | elif RunConfig.driver_type == "firefox-headless": 129 | # firefox headless模式 130 | firefox_options = FF_Options() 131 | firefox_options.headless = True 132 | driver = webdriver.Firefox(options=firefox_options) 133 | 134 | elif RunConfig.driver_type == "grid": 135 | # 通过远程节点运行 136 | chrome_options = CH_Options() 137 | driver = Remote(command_executor='http://localhost:4444/wd/hub', options=chrome_options) 138 | 139 | else: 140 | raise NameError("driver驱动类型定义错误!") 141 | 142 | RunConfig.driver = driver 143 | 144 | yield driver 145 | 146 | driver.quit() 147 | 148 | return driver 149 | 150 | 151 | if __name__ == "__main__": 152 | capture_screenshots("test_dir/test_baidu_search.test_search_python.png") 153 | -------------------------------------------------------------------------------- /page/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defnngj/pyautoTest/e4d4a52665d63f42f6941c53a413e8f14ddbf174/page/__init__.py -------------------------------------------------------------------------------- /page/baidu_page.py: -------------------------------------------------------------------------------- 1 | from poium import Page, Element 2 | 3 | 4 | class BaiduPage(Page): 5 | search_input = Element(id_="kw", describe="搜索框") 6 | search_button = Element(id_="su", describe="搜索按钮") 7 | settings = Element(css="#s-usersetting-top", describe="设置") 8 | search_setting = Element(css="#s-user-setting-menu > div > a.setpref", describe="搜索设置") 9 | save_setting = Element(link_text="保存设置", describe="保存设置") 10 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | base_url = https://www.baidu.com -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pytest==8.3.2 2 | pytest-html==4.1.1 3 | pytest-rerunfailures==9.1.1 4 | pytest-base-url==1.4.2 5 | click==7.1.2 6 | poium==1.5.2 -------------------------------------------------------------------------------- /run_tests.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | import os 3 | import time 4 | import logging 5 | import pytest 6 | import click 7 | from conftest import REPORT_DIR 8 | from config import RunConfig 9 | 10 | logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') 11 | logger = logging.getLogger(__name__) 12 | 13 | ''' 14 | 说明: 15 | 1、用例创建原则,测试文件名必须以“test”开头,测试函数必须以“test”开头。 16 | 2、运行方式: 17 | > python run_tests.py (回归模式,生成HTML报告) 18 | > python run_tests.py -m debug (调试模式) 19 | ''' 20 | 21 | 22 | def init_env(new_report): 23 | """ 24 | 初始化测试报告目录 25 | """ 26 | os.mkdir(new_report) 27 | os.mkdir(new_report + "/image") 28 | 29 | 30 | @click.command() 31 | @click.option('-m', default=None, help='输入运行模式:run 或 debug.') 32 | def run(m): 33 | if m is None or m == "run": 34 | logger.info("回归模式,开始执行✈✈!") 35 | now_time = time.strftime("%Y_%m_%d_%H_%M_%S") 36 | RunConfig.NEW_REPORT = os.path.join(REPORT_DIR, now_time) 37 | init_env(RunConfig.NEW_REPORT) 38 | html_report = os.path.join(RunConfig.NEW_REPORT, "report.html") 39 | xml_report = os.path.join(RunConfig.NEW_REPORT, "junit-xml.xml") 40 | pytest.main(["-s", "-v", RunConfig.cases_path, 41 | "--html=" + html_report, 42 | "--junit-xml=" + xml_report, 43 | "--self-contained-html", 44 | "--maxfail", RunConfig.max_fail, 45 | "--reruns", RunConfig.rerun]) 46 | logger.info("运行结束,生成测试报告♥❤!") 47 | elif m == "debug": 48 | print("debug模式,开始执行!") 49 | pytest.main(["-v", "-s", RunConfig.cases_path]) 50 | print("运行结束!!") 51 | 52 | 53 | if __name__ == '__main__': 54 | run() 55 | -------------------------------------------------------------------------------- /test_dir/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defnngj/pyautoTest/e4d4a52665d63f42f6941c53a413e8f14ddbf174/test_dir/__init__.py -------------------------------------------------------------------------------- /test_dir/data/data_file.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 1, 4 | "search_key": "Selenium" 5 | }, 6 | { 7 | "id": 2, 8 | "search_key": "poium" 9 | } 10 | ] -------------------------------------------------------------------------------- /test_dir/test_baidu.py: -------------------------------------------------------------------------------- 1 | """ 2 | @author: 虫师 3 | @data: 2019-10-17 4 | @function python 基本用法 5 | """ 6 | import sys 7 | from time import sleep 8 | import pytest 9 | from os.path import dirname, abspath 10 | 11 | sys.path.insert(0, dirname(dirname(abspath(__file__)))) 12 | from page.baidu_page import BaiduPage 13 | 14 | 15 | class TestSearch: 16 | """百度搜索""" 17 | 18 | def test_baidu_search_case(self, browser, base_url): 19 | """ 20 | 名称:百度搜索"pytest" 21 | 步骤: 22 | 1、打开浏览器 23 | 2、输入"pytest"关键字 24 | 3、点击搜索按钮 25 | 检查点: 26 | * 检查页面标题是否包含关键字。 27 | """ 28 | page = BaiduPage(browser) 29 | page.open(base_url) 30 | page.search_input = "pytest" 31 | page.search_button.click() 32 | sleep(2) 33 | assert browser.title == "pytest_百度搜索" 34 | 35 | 36 | class TestSearchSettings: 37 | """百度搜索设置""" 38 | 39 | def test_baidu_search_setting(self, browser, base_url): 40 | """ 41 | 名称:百度搜索设置 42 | 步骤: 43 | 1、打开百度浏览器 44 | 2、点击设置链接 45 | 3、在下拉框中"选择搜索" 46 | 4、点击"保存设置" 47 | 5、对弹出警告框保存 48 | 检查点: 49 | * 检查是否弹出提示框 50 | """ 51 | page = BaiduPage(browser) 52 | page.open(base_url) 53 | page.settings.click() 54 | page.search_setting.click() 55 | sleep(2) 56 | page.save_setting.click() 57 | alert_text = page.get_alert_text 58 | page.accept_alert() 59 | assert alert_text == "已经记录下您的使用偏好" 60 | 61 | 62 | if __name__ == '__main__': 63 | pytest.main(["-v", "-s", "test_baidu.py"]) 64 | # pytest.main(["-v", "-s", "test_baidu.py::TestSearch::test_baidu_search_case"]) 65 | # pytest.main(["-v", "-s", "test_baidu.py::TestSearch"]) 66 | # pytest.main(["-v", "-s", "test_baidu.py::TestSearchSettings"]) 67 | -------------------------------------------------------------------------------- /test_dir/test_parametrize.py: -------------------------------------------------------------------------------- 1 | """ 2 | @author: 虫师 3 | @data: 2019-10-17 4 | @function pytest 参数使用 5 | """ 6 | import sys 7 | import json 8 | from time import sleep 9 | import pytest 10 | from os.path import dirname, abspath 11 | 12 | base_path = dirname(dirname(abspath(__file__))) 13 | sys.path.insert(0, base_path) 14 | from page.baidu_page import BaiduPage 15 | 16 | 17 | @pytest.mark.parametrize( 18 | "name, search_key", 19 | [("1", "Selenium"), 20 | ("2", "pytest文档"), 21 | ("3", "pytest-html"), 22 | ], 23 | ids=["case1", "case2", "case3"] 24 | ) 25 | def test_baidu_search(name, search_key, browser, base_url): 26 | """百度搜索参数化""" 27 | page = BaiduPage(browser) 28 | page.open(base_url) 29 | page.search_input = search_key 30 | page.search_button.click() 31 | sleep(2) 32 | assert browser.title == search_key + "_百度搜索" 33 | 34 | 35 | def get_data(file_path): 36 | """ 37 | 读取参数化文件 38 | :param file_path: 39 | :return: 40 | """ 41 | data = [] 42 | with(open(file_path, "r")) as f: 43 | dict_data = json.loads(f.read()) 44 | for i in dict_data: 45 | data.append(tuple(i.values())) 46 | return data 47 | 48 | 49 | @pytest.mark.parametrize( 50 | "name, search_key", 51 | get_data(base_path + "/test_dir/data/data_file.json") 52 | ) 53 | def test_baidu_search(name, search_key, browser, base_url): 54 | page = BaiduPage(browser) 55 | page.open(base_url) 56 | page.search_input = search_key 57 | page.search_button.click() 58 | sleep(2) 59 | assert browser.title == search_key + "_百度搜索" 60 | -------------------------------------------------------------------------------- /test_report/2020_07_19_23_01_06/image/test_bd_search.py_test_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defnngj/pyautoTest/e4d4a52665d63f42f6941c53a413e8f14ddbf174/test_report/2020_07_19_23_01_06/image/test_bd_search.py_test_search.png -------------------------------------------------------------------------------- /test_report/2020_07_19_23_01_06/junit-xml.xml: -------------------------------------------------------------------------------- 1 | browser = <selenium.webdriver.chrome.webdriver.WebDriver (session="e13773595aaaca74488d45366ce33b66")> 3 | base_url = 'https://www.baidu.com' 4 | 5 | def test_search(browser, base_url): 6 | """ 7 | 搜索 pytest 关键字 8 | 1. 打开百度首页 9 | 2. 输入关键字“pytest” 10 | 3. 点击搜索按钮 11 | """ 12 | browser.get(base_url) 13 | browser.find_element_by_id("kw").send_keys("pytest") 14 | > browser.find_element_by_id("susss").click() 15 | 16 | test_dir\test_bd_search.py:13: 17 | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 18 | C:\Python37\lib\site-packages\selenium\webdriver\remote\webdriver.py:360: in find_element_by_id 19 | return self.find_element(by=By.ID, value=id_) 20 | C:\Python37\lib\site-packages\selenium\webdriver\remote\webdriver.py:978: in find_element 21 | 'value': value})['value'] 22 | C:\Python37\lib\site-packages\selenium\webdriver\remote\webdriver.py:321: in execute 23 | self.error_handler.check_response(response) 24 | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 25 | 26 | self = <selenium.webdriver.remote.errorhandler.ErrorHandler object at 0x000002749E2209C8> 27 | response = {'status': 404, 'value': '{"value":{"error":"no such element","message":"no such element: Unable to locate element: {\...\n\\tRtlGetAppContainerNamedObjectPath [0x77687C24+228]\\n\\tRtlGetAppContainerNamedObjectPath [0x77687BF4+180]\\n"}}'} 28 | 29 | def check_response(self, response): 30 | """ 31 | Checks that a JSON response from the WebDriver does not have an error. 32 | 33 | :Args: 34 | - response - The JSON response from the WebDriver server as a dictionary 35 | object. 36 | 37 | :Raises: If the response contains an error message. 38 | """ 39 | status = response.get('status', None) 40 | if status is None or status == ErrorCode.SUCCESS: 41 | return 42 | value = None 43 | message = response.get("message", "") 44 | screen = response.get("screen", "") 45 | stacktrace = None 46 | if isinstance(status, int): 47 | value_json = response.get('value', None) 48 | if value_json and isinstance(value_json, basestring): 49 | import json 50 | try: 51 | value = json.loads(value_json) 52 | if len(value.keys()) == 1: 53 | value = value['value'] 54 | status = value.get('error', None) 55 | if status is None: 56 | status = value["status"] 57 | message = value["value"] 58 | if not isinstance(message, basestring): 59 | value = message 60 | message = message.get('message') 61 | else: 62 | message = value.get('message', None) 63 | except ValueError: 64 | pass 65 | 66 | exception_class = ErrorInResponseException 67 | if status in ErrorCode.NO_SUCH_ELEMENT: 68 | exception_class = NoSuchElementException 69 | elif status in ErrorCode.NO_SUCH_FRAME: 70 | exception_class = NoSuchFrameException 71 | elif status in ErrorCode.NO_SUCH_WINDOW: 72 | exception_class = NoSuchWindowException 73 | elif status in ErrorCode.STALE_ELEMENT_REFERENCE: 74 | exception_class = StaleElementReferenceException 75 | elif status in ErrorCode.ELEMENT_NOT_VISIBLE: 76 | exception_class = ElementNotVisibleException 77 | elif status in ErrorCode.INVALID_ELEMENT_STATE: 78 | exception_class = InvalidElementStateException 79 | elif status in ErrorCode.INVALID_SELECTOR \ 80 | or status in ErrorCode.INVALID_XPATH_SELECTOR \ 81 | or status in ErrorCode.INVALID_XPATH_SELECTOR_RETURN_TYPER: 82 | exception_class = InvalidSelectorException 83 | elif status in ErrorCode.ELEMENT_IS_NOT_SELECTABLE: 84 | exception_class = ElementNotSelectableException 85 | elif status in ErrorCode.ELEMENT_NOT_INTERACTABLE: 86 | exception_class = ElementNotInteractableException 87 | elif status in ErrorCode.INVALID_COOKIE_DOMAIN: 88 | exception_class = InvalidCookieDomainException 89 | elif status in ErrorCode.UNABLE_TO_SET_COOKIE: 90 | exception_class = UnableToSetCookieException 91 | elif status in ErrorCode.TIMEOUT: 92 | exception_class = TimeoutException 93 | elif status in ErrorCode.SCRIPT_TIMEOUT: 94 | exception_class = TimeoutException 95 | elif status in ErrorCode.UNKNOWN_ERROR: 96 | exception_class = WebDriverException 97 | elif status in ErrorCode.UNEXPECTED_ALERT_OPEN: 98 | exception_class = UnexpectedAlertPresentException 99 | elif status in ErrorCode.NO_ALERT_OPEN: 100 | exception_class = NoAlertPresentException 101 | elif status in ErrorCode.IME_NOT_AVAILABLE: 102 | exception_class = ImeNotAvailableException 103 | elif status in ErrorCode.IME_ENGINE_ACTIVATION_FAILED: 104 | exception_class = ImeActivationFailedException 105 | elif status in ErrorCode.MOVE_TARGET_OUT_OF_BOUNDS: 106 | exception_class = MoveTargetOutOfBoundsException 107 | elif status in ErrorCode.JAVASCRIPT_ERROR: 108 | exception_class = JavascriptException 109 | elif status in ErrorCode.SESSION_NOT_CREATED: 110 | exception_class = SessionNotCreatedException 111 | elif status in ErrorCode.INVALID_ARGUMENT: 112 | exception_class = InvalidArgumentException 113 | elif status in ErrorCode.NO_SUCH_COOKIE: 114 | exception_class = NoSuchCookieException 115 | elif status in ErrorCode.UNABLE_TO_CAPTURE_SCREEN: 116 | exception_class = ScreenshotException 117 | elif status in ErrorCode.ELEMENT_CLICK_INTERCEPTED: 118 | exception_class = ElementClickInterceptedException 119 | elif status in ErrorCode.INSECURE_CERTIFICATE: 120 | exception_class = InsecureCertificateException 121 | elif status in ErrorCode.INVALID_COORDINATES: 122 | exception_class = InvalidCoordinatesException 123 | elif status in ErrorCode.INVALID_SESSION_ID: 124 | exception_class = InvalidSessionIdException 125 | elif status in ErrorCode.UNKNOWN_METHOD: 126 | exception_class = UnknownMethodException 127 | else: 128 | exception_class = WebDriverException 129 | if value == '' or value is None: 130 | value = response['value'] 131 | if isinstance(value, basestring): 132 | if exception_class == ErrorInResponseException: 133 | raise exception_class(response, value) 134 | raise exception_class(value) 135 | if message == "" and 'message' in value: 136 | message = value['message'] 137 | 138 | screen = None 139 | if 'screen' in value: 140 | screen = value['screen'] 141 | 142 | stacktrace = None 143 | if 'stackTrace' in value and value['stackTrace']: 144 | stacktrace = [] 145 | try: 146 | for frame in value['stackTrace']: 147 | line = self._value_or_default(frame, 'lineNumber', '') 148 | file = self._value_or_default(frame, 'fileName', '<anonymous>') 149 | if line: 150 | file = "%s:%s" % (file, line) 151 | meth = self._value_or_default(frame, 'methodName', '<anonymous>') 152 | if 'className' in frame: 153 | meth = "%s.%s" % (frame['className'], meth) 154 | msg = " at %s (%s)" 155 | msg = msg % (meth, file) 156 | stacktrace.append(msg) 157 | except TypeError: 158 | pass 159 | if exception_class == ErrorInResponseException: 160 | raise exception_class(response, message) 161 | elif exception_class == UnexpectedAlertPresentException: 162 | alert_text = None 163 | if 'data' in value: 164 | alert_text = value['data'].get('text') 165 | elif 'alert' in value: 166 | alert_text = value['alert'].get('text') 167 | raise exception_class(message, screen, stacktrace, alert_text) 168 | > raise exception_class(message, screen, stacktrace) 169 | E selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"css selector","selector":"[id="susss"]"} 170 | E (Session info: chrome=84.0.4147.89) 171 | 172 | C:\Python37\lib\site-packages\selenium\webdriver\remote\errorhandler.py:242: NoSuchElementException -------------------------------------------------------------------------------- /test_report/2020_07_19_23_01_06/report.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Test Report 6 | 184 | 185 | 422 |

report.html

423 |

Report generated on 19-Jul-2020 at 23:01:24 by pytest-html v2.1.1

424 |

Environment

425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 |
JAVA_HOMEC:\Program Files\Java\jdk1.8.0_251
Packages{"pluggy": "0.13.1", "py": "1.9.0", "pytest": "5.4.3"}
PlatformWindows-10-10.0.18362-SP0
Plugins{"allure-pytest": "2.8.16", "html": "2.1.1", "metadata": "1.10.0", "rerunfailures": "9.0"}
Python3.7.7
441 |

Summary

442 |

1 tests ran in 17.83 seconds.

443 | 0 passed, 0 skipped, 1 failed, 0 errors, 0 expected failures, 0 unexpected passes, 1 rerun 444 |

Results

445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 468 | 469 | 470 | 473 | 474 | 475 | 476 | 477 | 487 | 488 | 489 |
ResultTestDescriptionDuration
Failedtest_dir/test_bd_search.py::test_search 459 | 460 | 461 | 462 | 463 |

搜索 pytest 关键字

464 |

1. 打开百度首页

465 |

2. 输入关键字“pytest”

466 |

3. 点击搜索按钮

467 |

0.83
471 |
screenshot
472 |
browser = <selenium.webdriver.chrome.webdriver.WebDriver (session="e13773595aaaca74488d45366ce33b66")>
base_url = 'https://www.baidu.com'

def test_search(browser, base_url):
"""
搜索 pytest 关键字
1. 打开百度首页
2. 输入关键字“pytest”
3. 点击搜索按钮
"""
browser.get(base_url)
browser.find_element_by_id("kw").send_keys("pytest")
> browser.find_element_by_id("susss").click()

test_dir\test_bd_search.py:13:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
C:\Python37\lib\site-packages\selenium\webdriver\remote\webdriver.py:360: in find_element_by_id
return self.find_element(by=By.ID, value=id_)
C:\Python37\lib\site-packages\selenium\webdriver\remote\webdriver.py:978: in find_element
'value': value})['value']
C:\Python37\lib\site-packages\selenium\webdriver\remote\webdriver.py:321: in execute
self.error_handler.check_response(response)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <selenium.webdriver.remote.errorhandler.ErrorHandler object at 0x000002749E2209C8>
response = {'status': 404, 'value': '{"value":{"error":"no such element","message":"no such element: Unable to locate element: {\...\n\\tRtlGetAppContainerNamedObjectPath [0x77687C24+228]\\n\\tRtlGetAppContainerNamedObjectPath [0x77687BF4+180]\\n"}}'}

def check_response(self, response):
"""
Checks that a JSON response from the WebDriver does not have an error.

:Args:
- response - The JSON response from the WebDriver server as a dictionary
object.

:Raises: If the response contains an error message.
"""
status = response.get('status', None)
if status is None or status == ErrorCode.SUCCESS:
return
value = None
message = response.get("message", "")
screen = response.get("screen", "")
stacktrace = None
if isinstance(status, int):
value_json = response.get('value', None)
if value_json and isinstance(value_json, basestring):
import json
try:
value = json.loads(value_json)
if len(value.keys()) == 1:
value = value['value']
status = value.get('error', None)
if status is None:
status = value["status"]
message = value["value"]
if not isinstance(message, basestring):
value = message
message = message.get('message')
else:
message = value.get('message', None)
except ValueError:
pass

exception_class = ErrorInResponseException
if status in ErrorCode.NO_SUCH_ELEMENT:
exception_class = NoSuchElementException
elif status in ErrorCode.NO_SUCH_FRAME:
exception_class = NoSuchFrameException
elif status in ErrorCode.NO_SUCH_WINDOW:
exception_class = NoSuchWindowException
elif status in ErrorCode.STALE_ELEMENT_REFERENCE:
exception_class = StaleElementReferenceException
elif status in ErrorCode.ELEMENT_NOT_VISIBLE:
exception_class = ElementNotVisibleException
elif status in ErrorCode.INVALID_ELEMENT_STATE:
exception_class = InvalidElementStateException
elif status in ErrorCode.INVALID_SELECTOR \
or status in ErrorCode.INVALID_XPATH_SELECTOR \
or status in ErrorCode.INVALID_XPATH_SELECTOR_RETURN_TYPER:
exception_class = InvalidSelectorException
elif status in ErrorCode.ELEMENT_IS_NOT_SELECTABLE:
exception_class = ElementNotSelectableException
elif status in ErrorCode.ELEMENT_NOT_INTERACTABLE:
exception_class = ElementNotInteractableException
elif status in ErrorCode.INVALID_COOKIE_DOMAIN:
exception_class = InvalidCookieDomainException
elif status in ErrorCode.UNABLE_TO_SET_COOKIE:
exception_class = UnableToSetCookieException
elif status in ErrorCode.TIMEOUT:
exception_class = TimeoutException
elif status in ErrorCode.SCRIPT_TIMEOUT:
exception_class = TimeoutException
elif status in ErrorCode.UNKNOWN_ERROR:
exception_class = WebDriverException
elif status in ErrorCode.UNEXPECTED_ALERT_OPEN:
exception_class = UnexpectedAlertPresentException
elif status in ErrorCode.NO_ALERT_OPEN:
exception_class = NoAlertPresentException
elif status in ErrorCode.IME_NOT_AVAILABLE:
exception_class = ImeNotAvailableException
elif status in ErrorCode.IME_ENGINE_ACTIVATION_FAILED:
exception_class = ImeActivationFailedException
elif status in ErrorCode.MOVE_TARGET_OUT_OF_BOUNDS:
exception_class = MoveTargetOutOfBoundsException
elif status in ErrorCode.JAVASCRIPT_ERROR:
exception_class = JavascriptException
elif status in ErrorCode.SESSION_NOT_CREATED:
exception_class = SessionNotCreatedException
elif status in ErrorCode.INVALID_ARGUMENT:
exception_class = InvalidArgumentException
elif status in ErrorCode.NO_SUCH_COOKIE:
exception_class = NoSuchCookieException
elif status in ErrorCode.UNABLE_TO_CAPTURE_SCREEN:
exception_class = ScreenshotException
elif status in ErrorCode.ELEMENT_CLICK_INTERCEPTED:
exception_class = ElementClickInterceptedException
elif status in ErrorCode.INSECURE_CERTIFICATE:
exception_class = InsecureCertificateException
elif status in ErrorCode.INVALID_COORDINATES:
exception_class = InvalidCoordinatesException
elif status in ErrorCode.INVALID_SESSION_ID:
exception_class = InvalidSessionIdException
elif status in ErrorCode.UNKNOWN_METHOD:
exception_class = UnknownMethodException
else:
exception_class = WebDriverException
if value == '' or value is None:
value = response['value']
if isinstance(value, basestring):
if exception_class == ErrorInResponseException:
raise exception_class(response, value)
raise exception_class(value)
if message == "" and 'message' in value:
message = value['message']

screen = None
if 'screen' in value:
screen = value['screen']

stacktrace = None
if 'stackTrace' in value and value['stackTrace']:
stacktrace = []
try:
for frame in value['stackTrace']:
line = self._value_or_default(frame, 'lineNumber', '')
file = self._value_or_default(frame, 'fileName', '<anonymous>')
if line:
file = "%s:%s" % (file, line)
meth = self._value_or_default(frame, 'methodName', '<anonymous>')
if 'className' in frame:
meth = "%s.%s" % (frame['className'], meth)
msg = " at %s (%s)"
msg = msg % (meth, file)
stacktrace.append(msg)
except TypeError:
pass
if exception_class == ErrorInResponseException:
raise exception_class(response, message)
elif exception_class == UnexpectedAlertPresentException:
alert_text = None
if 'data' in value:
alert_text = value['data'].get('text')
elif 'alert' in value:
alert_text = value['alert'].get('text')
raise exception_class(message, screen, stacktrace, alert_text)
> raise exception_class(message, screen, stacktrace)
E selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"css selector","selector":"[id="susss"]"}
E (Session info: chrome=84.0.4147.89)

C:\Python37\lib\site-packages\selenium\webdriver\remote\errorhandler.py:242: NoSuchElementException
Reruntest_dir/test_bd_search.py::test_search 478 | 479 | 480 | 481 | 482 |

搜索 pytest 关键字

483 |

1. 打开百度首页

484 |

2. 输入关键字“pytest”

485 |

3. 点击搜索按钮

486 |

1.10
490 |
screenshot
491 |
browser = <selenium.webdriver.chrome.webdriver.WebDriver (session="8ab137205f23c019f6ffcc6c0ad1dc46")>
base_url = 'https://www.baidu.com'

def test_search(browser, base_url):
"""
搜索 pytest 关键字
1. 打开百度首页
2. 输入关键字“pytest”
3. 点击搜索按钮
"""
browser.get(base_url)
browser.find_element_by_id("kw").send_keys("pytest")
> browser.find_element_by_id("susss").click()

test_dir\test_bd_search.py:13:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
C:\Python37\lib\site-packages\selenium\webdriver\remote\webdriver.py:360: in find_element_by_id
return self.find_element(by=By.ID, value=id_)
C:\Python37\lib\site-packages\selenium\webdriver\remote\webdriver.py:978: in find_element
'value': value})['value']
C:\Python37\lib\site-packages\selenium\webdriver\remote\webdriver.py:321: in execute
self.error_handler.check_response(response)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <selenium.webdriver.remote.errorhandler.ErrorHandler object at 0x000002749E1E7CC8>
response = {'status': 404, 'value': '{"value":{"error":"no such element","message":"no such element: Unable to locate element: {\...\n\\tRtlGetAppContainerNamedObjectPath [0x77687C24+228]\\n\\tRtlGetAppContainerNamedObjectPath [0x77687BF4+180]\\n"}}'}

def check_response(self, response):
"""
Checks that a JSON response from the WebDriver does not have an error.

:Args:
- response - The JSON response from the WebDriver server as a dictionary
object.

:Raises: If the response contains an error message.
"""
status = response.get('status', None)
if status is None or status == ErrorCode.SUCCESS:
return
value = None
message = response.get("message", "")
screen = response.get("screen", "")
stacktrace = None
if isinstance(status, int):
value_json = response.get('value', None)
if value_json and isinstance(value_json, basestring):
import json
try:
value = json.loads(value_json)
if len(value.keys()) == 1:
value = value['value']
status = value.get('error', None)
if status is None:
status = value["status"]
message = value["value"]
if not isinstance(message, basestring):
value = message
message = message.get('message')
else:
message = value.get('message', None)
except ValueError:
pass

exception_class = ErrorInResponseException
if status in ErrorCode.NO_SUCH_ELEMENT:
exception_class = NoSuchElementException
elif status in ErrorCode.NO_SUCH_FRAME:
exception_class = NoSuchFrameException
elif status in ErrorCode.NO_SUCH_WINDOW:
exception_class = NoSuchWindowException
elif status in ErrorCode.STALE_ELEMENT_REFERENCE:
exception_class = StaleElementReferenceException
elif status in ErrorCode.ELEMENT_NOT_VISIBLE:
exception_class = ElementNotVisibleException
elif status in ErrorCode.INVALID_ELEMENT_STATE:
exception_class = InvalidElementStateException
elif status in ErrorCode.INVALID_SELECTOR \
or status in ErrorCode.INVALID_XPATH_SELECTOR \
or status in ErrorCode.INVALID_XPATH_SELECTOR_RETURN_TYPER:
exception_class = InvalidSelectorException
elif status in ErrorCode.ELEMENT_IS_NOT_SELECTABLE:
exception_class = ElementNotSelectableException
elif status in ErrorCode.ELEMENT_NOT_INTERACTABLE:
exception_class = ElementNotInteractableException
elif status in ErrorCode.INVALID_COOKIE_DOMAIN:
exception_class = InvalidCookieDomainException
elif status in ErrorCode.UNABLE_TO_SET_COOKIE:
exception_class = UnableToSetCookieException
elif status in ErrorCode.TIMEOUT:
exception_class = TimeoutException
elif status in ErrorCode.SCRIPT_TIMEOUT:
exception_class = TimeoutException
elif status in ErrorCode.UNKNOWN_ERROR:
exception_class = WebDriverException
elif status in ErrorCode.UNEXPECTED_ALERT_OPEN:
exception_class = UnexpectedAlertPresentException
elif status in ErrorCode.NO_ALERT_OPEN:
exception_class = NoAlertPresentException
elif status in ErrorCode.IME_NOT_AVAILABLE:
exception_class = ImeNotAvailableException
elif status in ErrorCode.IME_ENGINE_ACTIVATION_FAILED:
exception_class = ImeActivationFailedException
elif status in ErrorCode.MOVE_TARGET_OUT_OF_BOUNDS:
exception_class = MoveTargetOutOfBoundsException
elif status in ErrorCode.JAVASCRIPT_ERROR:
exception_class = JavascriptException
elif status in ErrorCode.SESSION_NOT_CREATED:
exception_class = SessionNotCreatedException
elif status in ErrorCode.INVALID_ARGUMENT:
exception_class = InvalidArgumentException
elif status in ErrorCode.NO_SUCH_COOKIE:
exception_class = NoSuchCookieException
elif status in ErrorCode.UNABLE_TO_CAPTURE_SCREEN:
exception_class = ScreenshotException
elif status in ErrorCode.ELEMENT_CLICK_INTERCEPTED:
exception_class = ElementClickInterceptedException
elif status in ErrorCode.INSECURE_CERTIFICATE:
exception_class = InsecureCertificateException
elif status in ErrorCode.INVALID_COORDINATES:
exception_class = InvalidCoordinatesException
elif status in ErrorCode.INVALID_SESSION_ID:
exception_class = InvalidSessionIdException
elif status in ErrorCode.UNKNOWN_METHOD:
exception_class = UnknownMethodException
else:
exception_class = WebDriverException
if value == '' or value is None:
value = response['value']
if isinstance(value, basestring):
if exception_class == ErrorInResponseException:
raise exception_class(response, value)
raise exception_class(value)
if message == "" and 'message' in value:
message = value['message']

screen = None
if 'screen' in value:
screen = value['screen']

stacktrace = None
if 'stackTrace' in value and value['stackTrace']:
stacktrace = []
try:
for frame in value['stackTrace']:
line = self._value_or_default(frame, 'lineNumber', '')
file = self._value_or_default(frame, 'fileName', '<anonymous>')
if line:
file = "%s:%s" % (file, line)
meth = self._value_or_default(frame, 'methodName', '<anonymous>')
if 'className' in frame:
meth = "%s.%s" % (frame['className'], meth)
msg = " at %s (%s)"
msg = msg % (meth, file)
stacktrace.append(msg)
except TypeError:
pass
if exception_class == ErrorInResponseException:
raise exception_class(response, message)
elif exception_class == UnexpectedAlertPresentException:
alert_text = None
if 'data' in value:
alert_text = value['data'].get('text')
elif 'alert' in value:
alert_text = value['alert'].get('text')
raise exception_class(message, screen, stacktrace, alert_text)
> raise exception_class(message, screen, stacktrace)
E selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"css selector","selector":"[id="susss"]"}
E (Session info: chrome=84.0.4147.89)

C:\Python37\lib\site-packages\selenium\webdriver\remote\errorhandler.py:242: NoSuchElementException
--------------------------------------------------------------------------------