├── util ├── __pycache__ │ ├── common.cpython-37.pyc │ ├── logger.cpython-37.pyc │ ├── __init__.cpython-37.pyc │ ├── HTMLTestRunner_cn.cpython-37.pyc │ └── overwrite_TestCase.cpython-37.pyc ├── __init__.py ├── overwrite_TestCase.py ├── common.py ├── logger.py └── HTMLTestRunner_cn.py ├── business ├── __pycache__ │ ├── login.cpython-37.pyc │ └── __init__.cpython-37.pyc ├── __init__.py └── login.py ├── pages ├── __pycache__ │ ├── __init__.cpython-37.pyc │ ├── basepage.cpython-37.pyc │ └── login_page.cpython-37.pyc ├── __init__.py ├── login_page.py └── basepage.py ├── locator ├── __pycache__ │ ├── __init__.cpython-37.pyc │ └── login_locator.cpython-37.pyc ├── __init__.py └── login_locator.py ├── testcases ├── __pycache__ │ ├── __init__.cpython-37.pyc │ ├── test_login.cpython-37.pyc │ ├── base_Testcase.cpython-37.pyc │ ├── test_login.cpython-37-PYTEST.pyc │ └── test_login.cpython-37-pytest-5.3.1.pyc ├── __init__.py ├── base_Testcase.py └── test_login.py ├── webDriver ├── __pycache__ │ ├── __init__.cpython-37.pyc │ └── driver_factory.cpython-37.pyc ├── __init__.py └── driver_factory.py ├── run_with_pytest.py ├── run.py └── log └── log.txt /util/__pycache__/common.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangkaiwoniu/python_ui_autoFrame/HEAD/util/__pycache__/common.cpython-37.pyc -------------------------------------------------------------------------------- /util/__pycache__/logger.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangkaiwoniu/python_ui_autoFrame/HEAD/util/__pycache__/logger.cpython-37.pyc -------------------------------------------------------------------------------- /business/__pycache__/login.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangkaiwoniu/python_ui_autoFrame/HEAD/business/__pycache__/login.cpython-37.pyc -------------------------------------------------------------------------------- /pages/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangkaiwoniu/python_ui_autoFrame/HEAD/pages/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /pages/__pycache__/basepage.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangkaiwoniu/python_ui_autoFrame/HEAD/pages/__pycache__/basepage.cpython-37.pyc -------------------------------------------------------------------------------- /util/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangkaiwoniu/python_ui_autoFrame/HEAD/util/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /business/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangkaiwoniu/python_ui_autoFrame/HEAD/business/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /locator/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangkaiwoniu/python_ui_autoFrame/HEAD/locator/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /pages/__pycache__/login_page.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangkaiwoniu/python_ui_autoFrame/HEAD/pages/__pycache__/login_page.cpython-37.pyc -------------------------------------------------------------------------------- /testcases/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangkaiwoniu/python_ui_autoFrame/HEAD/testcases/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /testcases/__pycache__/test_login.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangkaiwoniu/python_ui_autoFrame/HEAD/testcases/__pycache__/test_login.cpython-37.pyc -------------------------------------------------------------------------------- /webDriver/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangkaiwoniu/python_ui_autoFrame/HEAD/webDriver/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /locator/__pycache__/login_locator.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangkaiwoniu/python_ui_autoFrame/HEAD/locator/__pycache__/login_locator.cpython-37.pyc -------------------------------------------------------------------------------- /util/__pycache__/HTMLTestRunner_cn.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangkaiwoniu/python_ui_autoFrame/HEAD/util/__pycache__/HTMLTestRunner_cn.cpython-37.pyc -------------------------------------------------------------------------------- /testcases/__pycache__/base_Testcase.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangkaiwoniu/python_ui_autoFrame/HEAD/testcases/__pycache__/base_Testcase.cpython-37.pyc -------------------------------------------------------------------------------- /util/__pycache__/overwrite_TestCase.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangkaiwoniu/python_ui_autoFrame/HEAD/util/__pycache__/overwrite_TestCase.cpython-37.pyc -------------------------------------------------------------------------------- /webDriver/__pycache__/driver_factory.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangkaiwoniu/python_ui_autoFrame/HEAD/webDriver/__pycache__/driver_factory.cpython-37.pyc -------------------------------------------------------------------------------- /testcases/__pycache__/test_login.cpython-37-PYTEST.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangkaiwoniu/python_ui_autoFrame/HEAD/testcases/__pycache__/test_login.cpython-37-PYTEST.pyc -------------------------------------------------------------------------------- /util/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | #====#====#====#==== 4 | #Author: 5 | #CreatDate: 6 | #Version: 7 | #====#====#====#==== 8 | 9 | 10 | -------------------------------------------------------------------------------- /business/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | #====#====#====#==== 4 | #Author: 5 | #CreatDate: 6 | #Version: 7 | #====#====#====#==== 8 | 9 | 10 | -------------------------------------------------------------------------------- /locator/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | #====#====#====#==== 4 | #Author: 5 | #CreatDate: 6 | #Version: 7 | #====#====#====#==== 8 | 9 | 10 | -------------------------------------------------------------------------------- /pages/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | #====#====#====#==== 4 | #Author: 5 | #CreatDate: 6 | #Version: 7 | #====#====#====#==== 8 | 9 | 10 | -------------------------------------------------------------------------------- /testcases/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | #====#====#====#==== 4 | #Author: 5 | #CreatDate: 6 | #Version: 7 | #====#====#====#==== 8 | 9 | 10 | -------------------------------------------------------------------------------- /testcases/__pycache__/test_login.cpython-37-pytest-5.3.1.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangkaiwoniu/python_ui_autoFrame/HEAD/testcases/__pycache__/test_login.cpython-37-pytest-5.3.1.pyc -------------------------------------------------------------------------------- /webDriver/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | #====#====#====#==== 4 | #Author: 5 | #CreatDate: 6 | #Version: 7 | #====#====#====#==== 8 | 9 | 10 | -------------------------------------------------------------------------------- /locator/login_locator.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | #====#====#====#==== 4 | #Author: 5 | #CreatDate: 6 | #Version: 7 | #====#====#====#==== 8 | from selenium.webdriver.common.by import By 9 | 10 | 11 | login_locator = { 12 | "username" : (By.CSS_SELECTOR,'input[name="username"] '), 13 | "password" : (By.CSS_SELECTOR,'.pword'), 14 | "login_button" : (By.ID,"btnSubmit") 15 | } 16 | 17 | 18 | -------------------------------------------------------------------------------- /run_with_pytest.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | #====#====#====#==== 4 | #Author: 5 | #CreatDate: 6 | #Version: 7 | #====#====#====#==== 8 | import pytest 9 | import allure 10 | from util.common import * 11 | import os 12 | 13 | if __name__ == '__main__': 14 | allure_report = get_allure_dir() 15 | pytest.main(['-s','-v',"--reruns","1",'testcases','--alluredir',"./report/xml"]) 16 | os.system("allure generate report/xml -o %s"%(allure_report)) 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /business/login.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | #====#====#====#==== 4 | #Author: 5 | #CreatDate: 6 | #Version: 7 | #====#====#====#==== 8 | from pages.login_page import LoginPage 9 | 10 | 11 | class Login: 12 | 13 | def __init__(self,driver): 14 | self.driver = driver 15 | self.loginpage = LoginPage(self.driver) 16 | 17 | def login(self,uname,pword): 18 | self.loginpage.send_uname(uname) 19 | self.loginpage.send_pword(pword) 20 | self.loginpage.click_button() 21 | -------------------------------------------------------------------------------- /pages/login_page.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | #====#====#====#==== 4 | #Author: 5 | #CreatDate: 6 | #Version: 7 | #====#====#====#==== 8 | from pages.basepage import BasePage 9 | from locator.login_locator import login_locator 10 | 11 | 12 | class LoginPage(BasePage): 13 | 14 | def send_uname(self,uname): 15 | self.send_text(login_locator.get("username"), uname) 16 | 17 | def send_pword(self,pword): 18 | self.send_text(login_locator.get("password"),pword) 19 | 20 | def click_button(self): 21 | self.click_element(login_locator.get("login_button")) 22 | -------------------------------------------------------------------------------- /run.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | #====#====#====#==== 4 | #Author: 5 | #CreatDate: 6 | #Version: 7 | #====#====#====#==== 8 | import unittest,os 9 | from util.common import get_report_path 10 | from util.HTMLTestRunner_cn import HTMLTestRunner 11 | 12 | def run(): 13 | test_path = os.path.dirname(__file__) + os.sep + "testcases" 14 | report_name = get_report_path() 15 | suite = unittest.defaultTestLoader.discover(test_path,"test_*",top_level_dir=None) 16 | with open(report_name,"wb") as f: 17 | runner =HTMLTestRunner( 18 | stream= f, 19 | title= "UI report", 20 | description= "自动化测试报告", 21 | retry= 1, 22 | save_last_try= True 23 | ) 24 | runner.run(suite) 25 | 26 | if __name__ == '__main__': 27 | run() 28 | -------------------------------------------------------------------------------- /testcases/base_Testcase.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | #====#====#====#==== 4 | #Author: 5 | #CreatDate: 6 | #Version: 7 | #====#====#====#==== 8 | from util.overwrite_TestCase import NewTestcase 9 | from webDriver.driver_factory import DriverFactory 10 | from util.logger import Logger 11 | from pages.basepage import BasePage 12 | 13 | class BaseTestcase(NewTestcase): 14 | 15 | driver = None 16 | logger = Logger() 17 | 18 | @classmethod 19 | def setUpClass(cls) -> None: 20 | cls.logger.info("-----开始执行测试-----") 21 | cls.driver = DriverFactory.get_driver() 22 | cls.driver.maximize_window() 23 | cls.driver.implicitly_wait(10) 24 | cls.basePage = BasePage(cls.driver) 25 | 26 | 27 | @classmethod 28 | def tearDownClass(cls) -> None: 29 | cls.driver.quit() 30 | cls.logger.info("-----测试结束-----") 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /testcases/test_login.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | #====#====#====#==== 4 | #Author: 5 | #CreatDate: 6 | #Version: 7 | #====#====#====#==== 8 | from testcases.base_Testcase import BaseTestcase 9 | from business.login import Login 10 | from parameterized import parameterized 11 | import allure 12 | import time 13 | 14 | class TestLogin(BaseTestcase): 15 | 16 | def setUp(self) -> None: 17 | self.basePage.open_home("http://129.211.69.116:4070/login") 18 | 19 | def tearDown(self) -> None: 20 | self.basePage.delete_cookies() 21 | 22 | @parameterized.expand([ 23 | ("001","admin","admin123") 24 | ]) 25 | def test_login_correct(self,num,uname,pwrod): 26 | Login(self.driver).login(uname,pwrod) 27 | time.sleep(2) 28 | title = self.driver.title 29 | self.assertEqual_new(title,"管理系统首页","正常登录异常",self.driver) 30 | 31 | @parameterized.expand([ 32 | ("001", "administrator", "administrator123") 33 | ]) 34 | def test_login_wrong(self, num, uname, pwrod): 35 | Login(self.driver).login(uname, pwrod) 36 | title = self.driver.title 37 | self.assertEqual_new(title, "管理系统", "错误登录异常", self.driver) 38 | 39 | -------------------------------------------------------------------------------- /util/overwrite_TestCase.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | #====#====#====#==== 4 | #Author: 5 | #CreatDate: 6 | #Version: 7 | #====#====#====#==== 8 | import unittest 9 | from util.logger import Logger 10 | from util.common import * 11 | 12 | 13 | class NewTestcase(unittest.TestCase): 14 | 15 | logger = Logger() 16 | 17 | def assertEqual_new(self, first, second, msg,driver): 18 | try: 19 | self.assertEqual(first,second,msg) 20 | except Exception as e: 21 | self.input_log_and_save_screenshot(str(e),driver) 22 | raise 23 | 24 | def asserIn_new(self, first, second, msg, driver): 25 | try: 26 | self.assertIn(first,second,msg) 27 | except Exception as e: 28 | self.input_log_and_save_screenshot(str(e), driver) 29 | raise 30 | 31 | def assertIs_new(self, first, second, msg, driver): 32 | try: 33 | self.assertIs(first,second,msg) 34 | except Exception as e: 35 | self.input_log_and_save_screenshot(str(e), driver) 36 | raise 37 | 38 | def assertisNone_new(self,exp,msg,driver): 39 | try: 40 | self.assertIsNone(exp,msg) 41 | except Exception as e: 42 | self.input_log_and_save_screenshot(str(e), driver) 43 | raise 44 | 45 | def input_log_and_save_screenshot(self,exc,driver): 46 | self.logger.erro(f"断言失败" + exc) 47 | save_screen_shot(driver) 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /util/common.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | #====#====#====#==== 4 | #Author: 5 | #CreatDate: 6 | #Version: 7 | #====#====#====#==== 8 | import os 9 | import time 10 | from datetime import date 11 | 12 | def get_date_by_type(type): 13 | return time.strftime(type,time.localtime(time.time())) 14 | 15 | def get_time(): 16 | return time.strftime("%y-%m-%d--%H-%M-%S",time.localtime(time.time())) 17 | 18 | def get_date(): 19 | return str(date.today()) 20 | 21 | def save_screen_shot(driver): 22 | """ 23 | 浏览器截图方法 24 | :return: 25 | """ 26 | sub_dir_path = create_dir_path("screenShot") 27 | screnn_name = os.path.join(sub_dir_path, (get_time() + "_screenShot.png")) 28 | driver.get_screenshot_as_file(screnn_name) 29 | 30 | def get_allure_dir(): 31 | dir_report_path = create_dir_path("report") 32 | allure_dir = os.path.join(dir_report_path,(get_time() + "_allure_report")) 33 | # create_dir(allure_dir) 34 | return allure_dir 35 | 36 | def get_report_path(): 37 | """ 38 | 返回report文件夹 39 | :return: 40 | """ 41 | dir_report_path = create_dir_path("report") 42 | report_name = os.path.join(dir_report_path, (get_time() + "_report.html")) 43 | return report_name 44 | 45 | def create_dir(path): 46 | if not os.path.exists(path): 47 | os.makedirs(path) 48 | 49 | def create_dir_path(dir): 50 | root_dir_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), dir) 51 | sub_dir_path = os.path.join(root_dir_path, get_date()) 52 | create_dir(sub_dir_path) 53 | return sub_dir_path 54 | 55 | 56 | if __name__ == '__main__': 57 | # print(os.path.join(os.path.dirname(os.path.dirname(__file__)),"screenShot")) 58 | # print(get_date(),type(str(get_date())),sep="----") 59 | print(get_allure_dir()) 60 | print(get_time()) 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /webDriver/driver_factory.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | #====#====#====#==== 4 | #Author: 5 | #CreatDate: 6 | #Version: 7 | #====#====#====#==== 8 | import os 9 | from selenium import webdriver 10 | from util.logger import Logger 11 | 12 | class DriverFactory: 13 | 14 | """ 15 | # singleton 16 | 单例模式,保证全局只有有一个driver 17 | """ 18 | _instance = None 19 | _dirver = None 20 | Chrome_driver_path = os.path.join(os.path.dirname(__file__),"drivers" + os.sep + "chromedriver.exe") 21 | logger = Logger() 22 | 23 | def __new__(cls, *args, **kwargs): 24 | if not cls._instance: 25 | cls._instance = super().__new__(cls) 26 | return cls._instance 27 | 28 | @classmethod 29 | def get_driver(cls,browers="chrome"): 30 | if cls._dirver is None: 31 | cls._dirver = cls.create_driver(browers) 32 | cls.logger.info("创建" + browers + "浏览器") 33 | return cls._dirver 34 | 35 | @classmethod 36 | def create_driver(cls,browers): 37 | driver = None 38 | if browers.lower() == "chrome": 39 | driver = webdriver.Chrome(executable_path=cls.Chrome_driver_path) 40 | elif browers.lower() == "firfox": 41 | driver = webdriver.Chrome() 42 | elif driver.lower() == "ie": 43 | driver = webdriver.Ie() 44 | elif driver.lower() == "Safari": 45 | driver = webdriver.Safari() 46 | else: 47 | #后期此处改为logger 48 | cls.logger.warn("您传入的参数browers异常") 49 | # print("您传入的参数browers异常") 50 | return driver 51 | 52 | if __name__ == '__main__': 53 | print(DriverFactory.Chrome_driver_path) 54 | driver = DriverFactory.get_driver() 55 | driver.get("http://www.baidu.com") 56 | from time import sleep 57 | sleep(2) 58 | driver.quit() 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /util/logger.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | #====#====#====#==== 4 | #Author: 5 | #CreatDate: 6 | #Version: 7 | #====#====#====#==== 8 | import logging 9 | from logging import handlers 10 | import os 11 | 12 | #项目路径 13 | pro_path = os.path.dirname(os.path.dirname(__file__)) 14 | 15 | class Logger: 16 | 17 | _instance = None 18 | logger = None 19 | log_path = os.path.join(pro_path,("log" + os.sep + "log.txt")) 20 | 21 | def __new__(cls, *args, **kwargs): 22 | if not cls._instance: 23 | cls._instance = super().__new__(cls,*args,**kwargs) 24 | return cls._instance 25 | 26 | def __init__(self): 27 | if self.logger is None: 28 | self.logger = self.create_logger() 29 | 30 | def create_logger(self): 31 | logger = None 32 | formater = logging.Formatter( 33 | '[%(asctime)s] [%(levelname)s] [%(pathname)s : %(funcName)s:%(lineno)d , %(message)s' 34 | ) 35 | logger = logging.getLogger("logger") 36 | logger.setLevel(logging.DEBUG) 37 | fileHandle = handlers.TimedRotatingFileHandler( 38 | self.log_path,when="h",interval=24,backupCount=1,encoding="utf8") 39 | consoleHandle = logging.StreamHandler() 40 | fileHandle.setLevel(logging.INFO) 41 | consoleHandle.setLevel(logging.DEBUG) 42 | fileHandle.setFormatter(formater) 43 | consoleHandle.setFormatter(formater) 44 | logger.addHandler(fileHandle) 45 | logger.addHandler(consoleHandle) 46 | return logger 47 | 48 | def info(self,msg): 49 | self.logger.info(msg) 50 | 51 | def debug(self,msg): 52 | self.logger.debug(msg) 53 | 54 | def warn(self,msg): 55 | self.logger.warn(msg) 56 | 57 | def erro(self,msg): 58 | self.logger.error(msg) 59 | 60 | if __name__ == '__main___': 61 | logger = Logger() 62 | logger.info("test logger") 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /pages/basepage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | #====#====#====#==== 4 | #Author: 5 | #CreatDate: 6 | #Version: 7 | #====#====#====#==== 8 | from selenium import webdriver 9 | from selenium.webdriver.support.wait import WebDriverWait 10 | from selenium.common.exceptions import TimeoutException,NoSuchElementException 11 | from selenium.webdriver.support import expected_conditions as ec 12 | from util.common import * 13 | from util.logger import Logger 14 | import os 15 | 16 | 17 | class BasePage: 18 | 19 | """ 20 | page基类,编写常用action, 21 | """ 22 | logger = Logger() 23 | 24 | def __init__(self,driver): 25 | self.driver = driver 26 | 27 | def find_element_and_wait(self,locator): 28 | """ 29 | :param locator: 元组形式(By.ID,"id") 30 | :return: 返回element 31 | """ 32 | element = None 33 | try: 34 | # element = WebDriverWait(self.driver,30).until(ec.presence_of_element_located(*locator)) 35 | element = WebDriverWait(self.driver,30).until(lambda driver:self.driver.find_element(*locator)) 36 | except TimeoutException as e: 37 | #后期改为logger 38 | self.logger.erro(f"寻找{locator}元素时间超时") 39 | self.get_screenShot() 40 | # print("寻找元素时间超时") 41 | return element 42 | 43 | def find_elements_and_wait(self,locator): 44 | """ 45 | :param locator: 元组形式(By.ID,"id") 46 | :return: 返回element 47 | """ 48 | elements = None 49 | try: 50 | # element = WebDriverWait(self.driver,30).until(ec.presence_of_element_located(*locator)) 51 | elements = WebDriverWait(self.driver,30).until(lambda driver:self.driver.find_elements(*locator)) 52 | except TimeoutException as e: 53 | #后期改为logger 54 | self.logger.erro(f"寻找{locator}元素时间超时") 55 | self.get_screenShot() 56 | # print("寻找元素时间超时") 57 | return elements 58 | 59 | def open_home(self,url): 60 | self.logger.info(f"打开{url}") 61 | self.driver.get(url) 62 | 63 | def click_element(self,locator): 64 | self.logger.info(f"点击{locator}元素") 65 | self.find_element_and_wait(locator).click() 66 | 67 | def send_text(self,locator,text): 68 | self.logger.info(f"在{locator}上输入{text}") 69 | element = self.find_element_and_wait(locator) 70 | element.clear() 71 | element.send_keys(text) 72 | 73 | def execute_js_click(self,locator): 74 | self.logger.info(f"使用js点击{locator}") 75 | element = self.find_element_and_wait(locator) 76 | self.driver.execute_script("arguments[0].click();",element) 77 | 78 | def is_exist(self,locator): 79 | flag = True 80 | try: 81 | self.driver.find_element(*locator) 82 | except NoSuchElementException as e: 83 | flag = False 84 | return flag 85 | 86 | def delete_cookies(self): 87 | self.logger.info("清除cookies") 88 | self.driver.delete_all_cookies() 89 | 90 | def get_screenShot(self): 91 | self.logger.info("截图") 92 | save_screen_shot(self.driver) 93 | 94 | def switch_frame(self,locator): 95 | iframe = self.find_element_and_wait(locator) 96 | self.logger.info(f"进入{locator}iframe") 97 | self.driver.switch_to.frame(iframe) 98 | 99 | def switch_to_default(self): 100 | self.logger.info("退出iframe") 101 | self.driver.switch_to.default_content() 102 | 103 | def quit(self): 104 | self.logger.info("关闭浏览器") 105 | self.driver.quit() 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /log/log.txt: -------------------------------------------------------------------------------- 1 | [2019-11-29 19:48:26,268] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 创建chrome浏览器 2 | [2019-11-29 20:09:45,933] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 创建chrome浏览器 3 | [2019-11-29 20:10:01,398] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', 'input[name="username"] ')上输入administrator 4 | [2019-11-29 20:10:02,783] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', '.pword')上输入administrator123 5 | [2019-11-29 20:10:03,159] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 点击('id', 'btnSubmit')元素 6 | [2019-11-29 20:10:05,100] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', 'input[name="username"] ')上输入administrator 7 | [2019-11-29 20:10:05,461] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', '.pword')上输入administrator123 8 | [2019-11-29 20:10:05,874] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 点击('id', 'btnSubmit')元素 9 | [2019-11-29 20:17:54,280] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 创建chrome浏览器 10 | [2019-11-29 20:18:10,201] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', 'input[name="username"] ')上输入administrator 11 | [2019-11-29 20:18:10,724] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', '.pword')上输入administrator123 12 | [2019-11-29 20:18:11,146] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 点击('id', 'btnSubmit')元素 13 | [2019-11-29 20:18:11,294] [ERROR] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : erro:58 , 断言失败'管理系统' != : 管理系统首页 14 | [2019-11-29 20:18:12,435] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', 'input[name="username"] ')上输入administrator 15 | [2019-11-29 20:18:12,770] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', '.pword')上输入administrator123 16 | [2019-11-29 20:18:13,111] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 点击('id', 'btnSubmit')元素 17 | [2019-11-29 20:18:13,252] [ERROR] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : erro:58 , 断言失败'管理系统' != : 管理系统 18 | [2019-11-29 20:22:44,870] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 创建chrome浏览器 19 | [2019-11-29 20:23:00,423] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', 'input[name="username"] ')上输入administrator 20 | [2019-11-29 20:23:02,260] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', '.pword')上输入administrator123 21 | [2019-11-29 20:23:02,758] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 点击('id', 'btnSubmit')元素 22 | [2019-11-29 20:23:03,567] [ERROR] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : erro:58 , 断言失败'管理系统' != '管理系统首页' 23 | - 管理系统 24 | + 管理系统首页 25 | ? ++ 26 | : 正常登录异常 27 | [2019-11-29 20:23:04,742] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', 'input[name="username"] ')上输入administrator 28 | [2019-11-29 20:23:05,065] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', '.pword')上输入administrator123 29 | [2019-11-29 20:23:05,505] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 点击('id', 'btnSubmit')元素 30 | [2019-11-30 08:56:54,439] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , -----开始执行测试----- 31 | [2019-11-30 08:57:20,590] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 创建chrome浏览器 32 | [2019-11-30 08:57:24,854] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 打开http://129.211.69.116:4070/login 33 | [2019-11-30 08:57:38,376] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', 'input[name="username"] ')上输入administrator 34 | [2019-11-30 08:57:44,212] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', '.pword')上输入administrator123 35 | [2019-11-30 08:57:44,676] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 点击('id', 'btnSubmit')元素 36 | [2019-11-30 08:57:44,934] [ERROR] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : erro:58 , 断言失败'管理系统' != '管理系统首页' 37 | - 管理系统 38 | + 管理系统首页 39 | ? ++ 40 | : 正常登录异常 41 | [2019-11-30 08:57:46,499] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 清除cookies 42 | [2019-11-30 08:57:47,131] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 打开http://129.211.69.116:4070/login 43 | [2019-11-30 08:57:47,295] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', 'input[name="username"] ')上输入administrator 44 | [2019-11-30 08:57:47,635] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', '.pword')上输入administrator123 45 | [2019-11-30 08:57:48,016] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 点击('id', 'btnSubmit')元素 46 | [2019-11-30 08:57:48,113] [ERROR] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : erro:58 , 断言失败'管理系统' != '管理系统首页' 47 | - 管理系统 48 | + 管理系统首页 49 | ? ++ 50 | : 正常登录异常 51 | [2019-11-30 08:57:48,668] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 清除cookies 52 | [2019-11-30 08:57:49,317] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 打开http://129.211.69.116:4070/login 53 | [2019-11-30 08:57:49,485] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', 'input[name="username"] ')上输入administrator 54 | [2019-11-30 08:57:49,859] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', '.pword')上输入administrator123 55 | [2019-11-30 08:57:50,250] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 点击('id', 'btnSubmit')元素 56 | [2019-11-30 08:57:50,342] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 清除cookies 57 | [2019-11-30 08:57:54,279] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , -----测试结束----- 58 | [2019-11-30 09:17:07,380] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , -----开始执行测试----- 59 | [2019-11-30 09:17:23,108] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 创建chrome浏览器 60 | [2019-11-30 09:17:27,287] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 打开http://129.211.69.116:4070/login 61 | [2019-11-30 09:17:38,866] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', 'input[name="username"] ')上输入administrator 62 | [2019-11-30 09:17:42,966] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', '.pword')上输入administrator123 63 | [2019-11-30 09:17:43,432] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 点击('id', 'btnSubmit')元素 64 | [2019-11-30 09:17:43,799] [ERROR] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : erro:58 , 断言失败'管理系统' != '管理系统首页' 65 | - 管理系统 66 | + 管理系统首页 67 | ? ++ 68 | : 正常登录异常 69 | [2019-11-30 09:17:45,164] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 清除cookies 70 | [2019-11-30 09:17:45,482] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 打开http://129.211.69.116:4070/login 71 | [2019-11-30 09:17:45,751] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', 'input[name="username"] ')上输入administrator 72 | [2019-11-30 09:17:46,193] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', '.pword')上输入administrator123 73 | [2019-11-30 09:17:46,591] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 点击('id', 'btnSubmit')元素 74 | [2019-11-30 09:17:46,697] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 清除cookies 75 | [2019-11-30 09:17:50,409] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , -----测试结束----- 76 | [2019-11-30 14:24:48,661] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , -----开始执行测试----- 77 | [2019-11-30 14:25:05,133] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 创建chrome浏览器 78 | [2019-11-30 14:25:09,421] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 打开http://129.211.69.116:4070/login 79 | [2019-11-30 14:25:20,828] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', 'input[name="username"] ')上输入administrator 80 | [2019-11-30 14:25:22,924] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', '.pword')上输入administrator123 81 | [2019-11-30 14:25:23,326] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 点击('id', 'btnSubmit')元素 82 | [2019-11-30 14:25:23,694] [ERROR] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : erro:58 , 断言失败'管理系统' != '管理系统首页' 83 | - 管理系统 84 | + 管理系统首页 85 | ? ++ 86 | : 正常登录异常 87 | [2019-11-30 14:25:25,608] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 清除cookies 88 | [2019-11-30 14:25:26,138] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 打开http://129.211.69.116:4070/login 89 | [2019-11-30 14:25:26,539] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', 'input[name="username"] ')上输入administrator 90 | [2019-11-30 14:25:26,841] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', '.pword')上输入administrator123 91 | [2019-11-30 14:25:27,258] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 点击('id', 'btnSubmit')元素 92 | [2019-11-30 14:25:27,377] [ERROR] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : erro:58 , 断言失败'管理系统' != '管理系统首页' 93 | - 管理系统 94 | + 管理系统首页 95 | ? ++ 96 | : 正常登录异常 97 | [2019-11-30 14:25:27,988] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 清除cookies 98 | [2019-11-30 14:25:28,350] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 打开http://129.211.69.116:4070/login 99 | [2019-11-30 14:25:28,512] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', 'input[name="username"] ')上输入administrator 100 | [2019-11-30 14:25:28,872] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', '.pword')上输入administrator123 101 | [2019-11-30 14:25:29,244] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 点击('id', 'btnSubmit')元素 102 | [2019-11-30 14:25:29,360] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 清除cookies 103 | [2019-11-30 14:25:33,363] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , -----测试结束----- 104 | [2019-11-30 14:31:23,002] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , -----开始执行测试----- 105 | [2019-11-30 14:31:38,896] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 创建chrome浏览器 106 | [2019-11-30 14:31:43,251] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 打开http://129.211.69.116:4070/login 107 | [2019-11-30 14:31:54,645] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', 'input[name="username"] ')上输入administrator 108 | [2019-11-30 14:31:56,225] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', '.pword')上输入administrator123 109 | [2019-11-30 14:31:56,672] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 点击('id', 'btnSubmit')元素 110 | [2019-11-30 14:31:56,860] [ERROR] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : erro:58 , 断言失败'管理系统' != '管理系统首页' 111 | - 管理系统 112 | + 管理系统首页 113 | ? ++ 114 | : 正常登录异常 115 | [2019-11-30 14:31:57,798] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 清除cookies 116 | [2019-11-30 14:31:58,047] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 打开http://129.211.69.116:4070/login 117 | [2019-11-30 14:31:58,218] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', 'input[name="username"] ')上输入administrator 118 | [2019-11-30 14:31:58,556] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', '.pword')上输入administrator123 119 | [2019-11-30 14:31:58,896] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 点击('id', 'btnSubmit')元素 120 | [2019-11-30 14:31:59,051] [ERROR] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : erro:58 , 断言失败'管理系统' != '管理系统首页' 121 | - 管理系统 122 | + 管理系统首页 123 | ? ++ 124 | : 正常登录异常 125 | [2019-11-30 14:31:59,611] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 清除cookies 126 | [2019-11-30 14:31:59,668] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 打开http://129.211.69.116:4070/login 127 | [2019-11-30 14:31:59,797] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', 'input[name="username"] ')上输入administrator 128 | [2019-11-30 14:32:00,155] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', '.pword')上输入administrator123 129 | [2019-11-30 14:32:00,555] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 点击('id', 'btnSubmit')元素 130 | [2019-11-30 14:32:00,654] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 清除cookies 131 | [2019-11-30 14:32:03,518] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , -----测试结束----- 132 | [2019-11-30 14:33:26,949] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , -----开始执行测试----- 133 | [2019-11-30 14:33:33,698] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 创建chrome浏览器 134 | [2019-11-30 14:33:37,860] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 打开http://129.211.69.116:4070/login 135 | [2019-11-30 14:33:49,489] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', 'input[name="username"] ')上输入administrator 136 | [2019-11-30 14:33:50,478] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', '.pword')上输入administrator123 137 | [2019-11-30 14:33:50,869] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 点击('id', 'btnSubmit')元素 138 | [2019-11-30 14:33:51,183] [ERROR] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : erro:58 , 断言失败'管理系统' != '管理系统首页' 139 | - 管理系统 140 | + 管理系统首页 141 | ? ++ 142 | : 正常登录异常 143 | [2019-11-30 14:33:52,274] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 清除cookies 144 | [2019-11-30 14:33:52,626] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 打开http://129.211.69.116:4070/login 145 | [2019-11-30 14:33:52,827] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', 'input[name="username"] ')上输入administrator 146 | [2019-11-30 14:33:53,263] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', '.pword')上输入administrator123 147 | [2019-11-30 14:33:53,582] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 点击('id', 'btnSubmit')元素 148 | [2019-11-30 14:33:53,716] [ERROR] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : erro:58 , 断言失败'管理系统' != '管理系统首页' 149 | - 管理系统 150 | + 管理系统首页 151 | ? ++ 152 | : 正常登录异常 153 | [2019-11-30 14:33:54,252] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 清除cookies 154 | [2019-11-30 14:33:54,314] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 打开http://129.211.69.116:4070/login 155 | [2019-11-30 14:33:54,468] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', 'input[name="username"] ')上输入administrator 156 | [2019-11-30 14:33:54,798] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', '.pword')上输入administrator123 157 | [2019-11-30 14:33:55,110] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 点击('id', 'btnSubmit')元素 158 | [2019-11-30 14:33:55,233] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 清除cookies 159 | [2019-11-30 14:33:57,831] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , -----测试结束----- 160 | [2019-11-30 14:35:11,289] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , -----开始执行测试----- 161 | [2019-11-30 14:35:19,804] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 创建chrome浏览器 162 | [2019-11-30 14:35:23,963] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 打开http://129.211.69.116:4070/login 163 | [2019-11-30 14:35:35,294] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', 'input[name="username"] ')上输入administrator 164 | [2019-11-30 14:35:36,366] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', '.pword')上输入administrator123 165 | [2019-11-30 14:35:36,743] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 点击('id', 'btnSubmit')元素 166 | [2019-11-30 14:35:37,043] [ERROR] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : erro:58 , 断言失败'管理系统' != '管理系统首页' 167 | - 管理系统 168 | + 管理系统首页 169 | ? ++ 170 | : 正常登录异常 171 | [2019-11-30 14:35:37,907] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 清除cookies 172 | [2019-11-30 14:35:38,174] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 打开http://129.211.69.116:4070/login 173 | [2019-11-30 14:35:38,382] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', 'input[name="username"] ')上输入administrator 174 | [2019-11-30 14:35:38,735] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', '.pword')上输入administrator123 175 | [2019-11-30 14:35:39,108] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 点击('id', 'btnSubmit')元素 176 | [2019-11-30 14:35:39,237] [ERROR] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : erro:58 , 断言失败'管理系统' != '管理系统首页' 177 | - 管理系统 178 | + 管理系统首页 179 | ? ++ 180 | : 正常登录异常 181 | [2019-11-30 14:35:39,850] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 清除cookies 182 | [2019-11-30 14:35:39,915] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 打开http://129.211.69.116:4070/login 183 | [2019-11-30 14:35:40,082] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', 'input[name="username"] ')上输入administrator 184 | [2019-11-30 14:35:40,413] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', '.pword')上输入administrator123 185 | [2019-11-30 14:35:40,736] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 点击('id', 'btnSubmit')元素 186 | [2019-11-30 14:35:40,866] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 清除cookies 187 | [2019-11-30 14:35:44,843] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , -----测试结束----- 188 | [2019-11-30 14:40:56,966] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , -----开始执行测试----- 189 | [2019-11-30 14:41:05,549] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 创建chrome浏览器 190 | [2019-11-30 14:41:09,955] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 打开http://129.211.69.116:4070/login 191 | [2019-11-30 14:41:21,459] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', 'input[name="username"] ')上输入admin 192 | [2019-11-30 14:41:22,120] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', '.pword')上输入admin123 193 | [2019-11-30 14:41:22,466] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 点击('id', 'btnSubmit')元素 194 | [2019-11-30 14:41:22,654] [ERROR] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : erro:58 , 断言失败'管理系统' != '管理系统首页' 195 | - 管理系统 196 | + 管理系统首页 197 | ? ++ 198 | : 正常登录异常 199 | [2019-11-30 14:41:32,467] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 清除cookies 200 | [2019-11-30 14:41:32,679] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 打开http://129.211.69.116:4070/login 201 | [2019-11-30 14:41:32,898] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', 'input[name="username"] ')上输入admin 202 | [2019-11-30 14:41:33,139] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', '.pword')上输入admin123 203 | [2019-11-30 14:41:33,448] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 点击('id', 'btnSubmit')元素 204 | [2019-11-30 14:41:33,633] [ERROR] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : erro:58 , 断言失败'管理系统' != '管理系统首页' 205 | - 管理系统 206 | + 管理系统首页 207 | ? ++ 208 | : 正常登录异常 209 | [2019-11-30 14:41:34,654] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 清除cookies 210 | [2019-11-30 14:41:34,708] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 打开http://129.211.69.116:4070/login 211 | [2019-11-30 14:41:34,916] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', 'input[name="username"] ')上输入administrator 212 | [2019-11-30 14:41:35,230] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', '.pword')上输入administrator123 213 | [2019-11-30 14:41:35,576] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 点击('id', 'btnSubmit')元素 214 | [2019-11-30 14:41:35,734] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 清除cookies 215 | [2019-11-30 14:41:40,097] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , -----测试结束----- 216 | [2019-11-30 14:46:01,202] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , -----开始执行测试----- 217 | [2019-11-30 14:46:12,292] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 创建chrome浏览器 218 | [2019-11-30 14:46:16,698] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 打开http://129.211.69.116:4070/login 219 | [2019-11-30 14:46:28,064] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', 'input[name="username"] ')上输入admin 220 | [2019-11-30 14:46:29,187] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', '.pword')上输入admin123 221 | [2019-11-30 14:46:29,532] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 点击('id', 'btnSubmit')元素 222 | [2019-11-30 14:46:39,641] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 清除cookies 223 | [2019-11-30 14:46:40,455] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 打开http://129.211.69.116:4070/login 224 | [2019-11-30 14:46:40,669] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', 'input[name="username"] ')上输入administrator 225 | [2019-11-30 14:46:40,914] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 在('css selector', '.pword')上输入administrator123 226 | [2019-11-30 14:46:41,105] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 点击('id', 'btnSubmit')元素 227 | [2019-11-30 14:46:41,205] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , 清除cookies 228 | [2019-11-30 14:46:44,224] [INFO] [F:\Python_Project\ZK_UI_automation_frame\util\logger.py : info:49 , -----测试结束----- 229 | -------------------------------------------------------------------------------- /util/HTMLTestRunner_cn.py: -------------------------------------------------------------------------------- 1 | #-*- coding: utf-8 -*- 2 | """ 3 | A TestRunner for use with the Python unit testing framework. It 4 | generates a HTML report to show the result at a glance. 5 | 6 | The simplest way to use this is to invoke its main method. E.g. 7 | 8 | import unittest 9 | import HTMLTestRunner 10 | 11 | ... define your tests ... 12 | 13 | if __name__ == '__main__': 14 | HTMLTestRunner.main() 15 | 16 | 17 | For more customization options, instantiates a HTMLTestRunner object. 18 | HTMLTestRunner is a counterpart to unittest's TextTestRunner. E.g. 19 | 20 | # output to a file 21 | fp = file('my_report.html', 'wb') 22 | runner = HTMLTestRunner.HTMLTestRunner( 23 | stream=fp, 24 | title='My unit test', 25 | description='This demonstrates the report output by HTMLTestRunner.' 26 | ) 27 | 28 | # Use an external stylesheet. 29 | # See the Template_mixin class for more customizable options 30 | runner.STYLESHEET_TMPL = '' 31 | 32 | # run the test 33 | runner.run(my_test_suite) 34 | 35 | 36 | ------------------------------------------------------------------------ 37 | Copyright (c) 2004-2007, Wai Yip Tung 38 | All rights reserved. 39 | 40 | Redistribution and use in source and binary forms, with or without 41 | modification, are permitted provided that the following conditions are 42 | met: 43 | 44 | * Redistributions of source code must retain the above copyright notice, 45 | this list of conditions and the following disclaimer. 46 | * Redistributions in binary form must reproduce the above copyright 47 | notice, this list of conditions and the following disclaimer in the 48 | documentation and/or other materials provided with the distribution. 49 | * Neither the name Wai Yip Tung nor the names of its contributors may be 50 | used to endorse or promote products derived from this software without 51 | specific prior written permission. 52 | 53 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 54 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 55 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 56 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 57 | OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 58 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 59 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 60 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 61 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 62 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 63 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 64 | """ 65 | 66 | # URL: http://tungwaiyip.info/software/HTMLTestRunner.html 67 | 68 | __author__ = "Wai Yip Tung" 69 | __version__ = "0.8.3" 70 | 71 | 72 | """ 73 | Change History 74 | Version 0.8.4 by GoverSky 75 | * Add sopport for 3.x 76 | * Add piechart for resultpiechart 77 | * Add Screenshot for selenium_case test 78 | * Add Retry on failed 79 | 80 | Version 0.8.3 81 | * Prevent crash on class or module-level exceptions (Darren Wurf). 82 | 83 | Version 0.8.2 84 | * Show output inline instead of popup window (Viorel Lupu). 85 | 86 | Version in 0.8.1 87 | * Validated XHTML (Wolfgang Borgert). 88 | * Added description of test classes and test cases. 89 | 90 | Version in 0.8.0 91 | * Define Template_mixin class for customization. 92 | * Workaround a IE 6 bug that it does not treat 420 | %(heading)s 421 |
422 |
423 | 424 |
425 |
426 | %(report)s 427 | %(ending)s 428 | 429 | 430 | 431 | """ 432 | # variables: (title, generator, stylesheet, heading, report, ending) 433 | 434 | 435 | # ------------------------------------------------------------------------ 436 | # Stylesheet 437 | # 438 | # alternatively use a for external style sheet, e.g. 439 | # 440 | 441 | STYLESHEET_TMPL = """ 442 | 645 | """ 646 | 647 | # ------------------------------------------------------------------------ 648 | # Heading 649 | # 650 | 651 | HEADING_TMPL = """
652 |

%(title)s

653 | %(parameters)s 654 |

%(description)s

655 |
656 | 657 | """ # variables: (title, parameters, description) 658 | 659 | HEADING_ATTRIBUTE_TMPL = """

%(name)s: %(value)s

660 | """ # variables: (name, value) 661 | 662 | # ------------------------------------------------------------------------ 663 | # Report 664 | # 665 | 666 | REPORT_TMPL = """ 667 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | %(test_list)s 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 |
测试组/测试用例总数通过失败错误视图错误截图
统计%(count)s%(Pass)s%(fail)s%(error)s  
706 | 710 | """ 711 | # variables: (test_list, count, Pass, fail, error) 712 | 713 | REPORT_CLASS_TMPL = r""" 714 | 715 | %(desc)s 716 | %(count)s 717 | %(Pass)s 718 | %(fail)s 719 | %(error)s 720 | 详情 721 |   722 | 723 | """ # variables: (style, desc, count, Pass, fail, error, cid) 724 | 725 | REPORT_TEST_WITH_OUTPUT_TMPL = r""" 726 | 727 |
%(desc)s
728 | 729 | 730 | 731 | 732 | 733 | %(status)s 734 | 735 | 744 | 745 | 746 | 747 | %(img)s 748 | 749 | """ # variables: (tid, Class, style, desc, status,img) 750 | 751 | REPORT_TEST_NO_OUTPUT_TMPL = r""" 752 | 753 |
%(desc)s
754 | %(status)s 755 | %(img)s 756 | 757 | """ # variables: (tid, Class, style, desc, status,img) 758 | 759 | REPORT_TEST_OUTPUT_TMPL = r""" 760 | %(id)s: %(output)s 761 | """ # variables: (id, output) 762 | 763 | 764 | IMG_TMPL = r""" 765 | 显示截图 766 | 771 | """ 772 | # ------------------------------------------------------------------------ 773 | # ENDING 774 | # 775 | 776 | ENDING_TMPL = """
 
""" 777 | 778 | # -------------------- The end of the Template class ------------------- 779 | 780 | def __getattribute__(self, item): 781 | value = object.__getattribute__(self, item) 782 | if PY3K: 783 | return value 784 | else: 785 | if isinstance(value, str): 786 | return value.decode("utf-8") 787 | else: 788 | return value 789 | 790 | 791 | TestResult = unittest.TestResult 792 | 793 | 794 | class _TestResult(TestResult): 795 | # note: _TestResult is a pure representation of results. 796 | # It lacks the output and reporting ability compares to unittest._TextTestResult. 797 | 798 | def __init__(self, verbosity=1, retry=0,save_last_try=False): 799 | TestResult.__init__(self) 800 | 801 | self.stdout0 = None 802 | self.stderr0 = None 803 | self.success_count = 0 804 | self.failure_count = 0 805 | self.error_count = 0 806 | self.skip_count = 0 807 | self.verbosity = verbosity 808 | 809 | # result is a list of result in 4 tuple 810 | # ( 811 | # result code (0: success; 1: fail; 2: error;3:skip), 812 | # TestCase object, 813 | # Test output (byte string), 814 | # stack trace, 815 | # ) 816 | self.result = [] 817 | self.retry = retry 818 | self.trys = 0 819 | self.status = 0 820 | 821 | self.save_last_try = save_last_try 822 | self.outputBuffer = StringIO.StringIO() 823 | 824 | def startTest(self, test): 825 | # test.imgs = [] 826 | test.imgs = getattr(test, "imgs", []) 827 | # TestResult.startTest(self, test) 828 | self.outputBuffer.seek(0) 829 | self.outputBuffer.truncate() 830 | stdout_redirector.fp = self.outputBuffer 831 | stderr_redirector.fp = self.outputBuffer 832 | self.stdout0 = sys.stdout 833 | self.stderr0 = sys.stderr 834 | sys.stdout = stdout_redirector 835 | sys.stderr = stderr_redirector 836 | 837 | def complete_output(self): 838 | """ 839 | Disconnect output redirection and return buffer. 840 | Safe to call multiple times. 841 | """ 842 | if self.stdout0: 843 | sys.stdout = self.stdout0 844 | sys.stderr = self.stderr0 845 | self.stdout0 = None 846 | self.stderr0 = None 847 | return self.outputBuffer.getvalue() 848 | 849 | def stopTest(self, test): 850 | # Usually one of addSuccess, addError or addFailure would have been called. 851 | # But there are some path in unittest that would bypass this. 852 | # We must disconnect stdout in stopTest(), which is guaranteed to be called. 853 | if self.retry and self.retry>=1: 854 | if self.status == 1: 855 | self.trys += 1 856 | if self.trys <= self.retry: 857 | if self.save_last_try: 858 | t = self.result.pop(-1) 859 | if t[0]==1: 860 | self.failure_count -=1 861 | else: 862 | self.error_count -= 1 863 | test=copy.copy(test) 864 | sys.stderr.write("Retesting... ") 865 | sys.stderr.write(str(test)) 866 | sys.stderr.write('..%d \n' % self.trys) 867 | doc = getattr(test,'_testMethodDoc',u"") or u'' 868 | if doc.find('_retry')!=-1: 869 | doc = doc[:doc.find('_retry')] 870 | desc ="%s_retry:%d" %(doc, self.trys) 871 | if not PY3K: 872 | if isinstance(desc, str): 873 | desc = desc.decode("utf-8") 874 | test._testMethodDoc = desc 875 | test(self) 876 | else: 877 | self.status = 0 878 | self.trys = 0 879 | self.complete_output() 880 | 881 | def addSuccess(self, test): 882 | self.success_count += 1 883 | self.status = 0 884 | TestResult.addSuccess(self, test) 885 | output = self.complete_output() 886 | self.result.append((0, test, output, '')) 887 | if self.verbosity > 1: 888 | sys.stderr.write('P ') 889 | sys.stderr.write(str(test)) 890 | sys.stderr.write('\n') 891 | else: 892 | sys.stderr.write('P') 893 | 894 | def addFailure(self, test, err): 895 | self.failure_count += 1 896 | self.status = 1 897 | TestResult.addFailure(self, test, err) 898 | _, _exc_str = self.failures[-1] 899 | output = self.complete_output() 900 | self.result.append((1, test, output, _exc_str)) 901 | if not getattr(test, "driver",""): 902 | pass 903 | else: 904 | try: 905 | driver = getattr(test, "driver") 906 | test.imgs.append(driver.get_screenshot_as_base64()) 907 | except Exception as e: 908 | pass 909 | if self.verbosity > 1: 910 | sys.stderr.write('F ') 911 | sys.stderr.write(str(test)) 912 | sys.stderr.write('\n') 913 | else: 914 | sys.stderr.write('F') 915 | 916 | def addError(self, test, err): 917 | self.error_count += 1 918 | self.status = 1 919 | TestResult.addError(self, test, err) 920 | _, _exc_str = self.errors[-1] 921 | output = self.complete_output() 922 | self.result.append((2, test, output, _exc_str)) 923 | if not getattr(test, "driver",""): 924 | pass 925 | else: 926 | try: 927 | driver = getattr(test, "driver") 928 | test.imgs.append(driver.get_screenshot_as_base64()) 929 | except Exception: 930 | pass 931 | if self.verbosity > 1: 932 | sys.stderr.write('E ') 933 | sys.stderr.write(str(test)) 934 | sys.stderr.write('\n') 935 | else: 936 | sys.stderr.write('E') 937 | 938 | def addSkip(self, test, reason): 939 | self.skip_count += 1 940 | self.status = 0 941 | TestResult.addSkip(self, test,reason) 942 | output = self.complete_output() 943 | self.result.append((3, test, output, reason)) 944 | if self.verbosity > 1: 945 | sys.stderr.write('K') 946 | sys.stderr.write(str(test)) 947 | sys.stderr.write('\n') 948 | else: 949 | sys.stderr.write('K') 950 | 951 | class HTMLTestRunner(Template_mixin): 952 | def __init__(self, stream=sys.stdout, verbosity=1, title=None, description=None,is_thread=False, retry=0,save_last_try=True): 953 | self.stream = stream 954 | self.retry = retry 955 | self.is_thread=is_thread 956 | self.threads= 5 957 | self.save_last_try=save_last_try 958 | self.verbosity = verbosity 959 | self.run_times=0 960 | if title is None: 961 | self.title = self.DEFAULT_TITLE 962 | else: 963 | self.title = title 964 | if description is None: 965 | self.description = self.DEFAULT_DESCRIPTION 966 | else: 967 | self.description = description 968 | 969 | def run(self, test): 970 | "Run the given test case or test suite." 971 | self.startTime = datetime.datetime.now() 972 | result = _TestResult(self.verbosity, self.retry, self.save_last_try) 973 | test(result) 974 | self.stopTime = datetime.datetime.now() 975 | self.run_times+=1 976 | self.generateReport(test, result) 977 | if PY3K: 978 | # for python3 979 | # print('\nTime Elapsed: %s' % (self.stopTime - self.startTime),file=sys.stderr) 980 | output = '\nTime Elapsed: %s' % (self.stopTime - self.startTime) 981 | sys.stderr.write(output) 982 | else: 983 | print >> sys.stderr, '\nTime Elapsed: %s' % (self.stopTime - self.startTime) 984 | return result 985 | 986 | def sortResult(self, result_list): 987 | # unittest does not seems to run in any particular order. 988 | # Here at least we want to group them together by class. 989 | rmap = {} 990 | classes = [] 991 | for n, t, o, e in result_list: 992 | cls = t.__class__ 993 | if not cls in rmap: 994 | rmap[cls] = [] 995 | classes.append(cls) 996 | rmap[cls].append((n, t, o, e)) 997 | for cls in classes: 998 | rmap[cls].sort(key=cmp_to_key(lambda a,b:1 if a[1].id()>b[1].id() else ( 1 if a[1].id()==b[1].id() else -1))) 999 | r = [(cls, rmap[cls]) for cls in classes] 1000 | # name = t.id().split('.')[-1] 1001 | r.sort(key=cmp_to_key(lambda a, b: 1 if a[0].__name__ > b[0].__name__ else -1)) 1002 | return r 1003 | 1004 | def getReportAttributes(self, result): 1005 | """ 1006 | Return report attributes as a list of (name, value). 1007 | Override this to add custom attributes. 1008 | """ 1009 | startTime = str(self.startTime)[:19] 1010 | duration = str(self.stopTime - self.startTime) 1011 | status = [] 1012 | if result.success_count: 1013 | status.append(u'Pass:%s' % result.success_count) 1014 | if result.failure_count: 1015 | status.append(u'Failure:%s' % result.failure_count) 1016 | if result.error_count: 1017 | status.append(u'Error:%s' % result.error_count) 1018 | if result.skip_count: 1019 | status.append(u'Skip:%s' % result.skip_count) 1020 | total = result.success_count+result.failure_count+result.error_count # +result.skip_count 1021 | if total>0: 1022 | passed = result.success_count*1.000/total*100 1023 | else: 1024 | passed =0.0 1025 | status.append(u'通过率:%.1f%%' % passed) 1026 | if status: 1027 | status = u' '.join(status) 1028 | else: 1029 | status = 'none' 1030 | return [ 1031 | (u'开始时间', startTime), 1032 | (u'耗时', duration), 1033 | (u'状态', status), 1034 | ] 1035 | 1036 | def generateReport(self, test, result): 1037 | report_attrs = self.getReportAttributes(result) 1038 | generator = 'HTMLTestRunner %s' % __version__ 1039 | stylesheet = self._generate_stylesheet() 1040 | heading = self._generate_heading(report_attrs) 1041 | report = self._generate_report(result) 1042 | ending = self._generate_ending() 1043 | output = self.HTML_TMPL % dict( 1044 | title=saxutils.escape(self.title), 1045 | generator=generator, 1046 | stylesheet=stylesheet, 1047 | heading=heading, 1048 | report=report, 1049 | ending=ending, 1050 | channel=self.run_times, 1051 | ) 1052 | if PY3K: 1053 | self.stream.write(output.encode()) 1054 | else: 1055 | self.stream.write(output.encode('utf8')) 1056 | 1057 | def _generate_stylesheet(self): 1058 | return self.STYLESHEET_TMPL 1059 | 1060 | def _generate_heading(self, report_attrs): 1061 | a_lines = [] 1062 | for name, value in report_attrs: 1063 | line = self.HEADING_ATTRIBUTE_TMPL % dict( 1064 | name=name, 1065 | value=value, 1066 | ) 1067 | a_lines.append(line) 1068 | heading = self.HEADING_TMPL % dict( 1069 | title=saxutils.escape(self.title), 1070 | parameters=''.join(a_lines), 1071 | description=saxutils.escape(self.description), 1072 | ) 1073 | return heading 1074 | 1075 | def _generate_report(self, result): 1076 | rows = [] 1077 | sortedResult = self.sortResult(result.result) 1078 | for cid, (cls, cls_results) in enumerate(sortedResult): 1079 | # subtotal for a class 1080 | np = nf = ne = ns = 0 1081 | for n, t, o, e in cls_results: 1082 | if n == 0: 1083 | np += 1 1084 | elif n == 1: 1085 | nf += 1 1086 | elif n==2: 1087 | ne += 1 1088 | else: 1089 | ns +=1 1090 | 1091 | # format class description 1092 | if cls.__module__ == "__main__": 1093 | name = cls.__name__ 1094 | else: 1095 | name = "%s.%s" % (cls.__module__, cls.__name__) 1096 | doc = cls.__doc__ and cls.__doc__.split("\n")[0] or "" 1097 | desc = doc and '%s: %s' % (name, doc) or name 1098 | if not PY3K: 1099 | if isinstance(desc, str): 1100 | desc = desc.decode("utf-8") 1101 | 1102 | row = self.REPORT_CLASS_TMPL % dict( 1103 | style=ne > 0 and 'errorClass' or nf > 0 and 'failClass' or 'passClass', 1104 | desc=desc, 1105 | count=np + nf + ne, 1106 | Pass=np, 1107 | fail=nf, 1108 | error=ne, 1109 | cid='c%s.%s' % (self.run_times,cid + 1), 1110 | ) 1111 | rows.append(row) 1112 | 1113 | for tid, (n, t, o, e) in enumerate(cls_results): 1114 | self._generate_report_test(rows, cid, tid, n, t, o, e) 1115 | total = result.success_count + result.failure_count + result.error_count #+result.skip_count 1116 | report = self.REPORT_TMPL % dict( 1117 | test_list=u''.join(rows), 1118 | count=str(total), 1119 | Pass=str(result.success_count), 1120 | Pass_p=result.success_count*1.00/total*100 if total else 0.0, 1121 | fail=str(result.failure_count), 1122 | error=str(result.error_count), 1123 | skip=str(result.skip_count), 1124 | total=str(total), 1125 | channel=str(self.run_times), 1126 | ) 1127 | return report 1128 | 1129 | def _generate_report_test(self, rows, cid, tid, n, t, o, e): 1130 | # e.g. 'pt1.1', 'ft1.1', etc 1131 | has_output = bool(o or e) 1132 | if n==0: 1133 | tmp="p" 1134 | elif n==1: 1135 | tmp="f" 1136 | elif n==2: 1137 | tmp = "e" 1138 | else: 1139 | tmp = "s" 1140 | tid = tmp + 't%d.%d.%d' % (self.run_times,cid + 1, tid + 1) 1141 | name = t.id().split('.')[-1] 1142 | if self.verbosity > 1: 1143 | doc = getattr(t,'_testMethodDoc',"") or '' 1144 | else: 1145 | doc = "" 1146 | 1147 | desc = doc and ('%s: %s' % (name, doc)) or name 1148 | if not PY3K: 1149 | if isinstance(desc, str): 1150 | desc = desc.decode("utf-8") 1151 | tmpl = has_output and self.REPORT_TEST_WITH_OUTPUT_TMPL or self.REPORT_TEST_NO_OUTPUT_TMPL 1152 | 1153 | # o and e should be byte string because they are collected from stdout and stderr? 1154 | if isinstance(o, str): 1155 | # uo = unicode(o.encode('string_escape')) 1156 | if PY3K: 1157 | uo = o 1158 | else: 1159 | uo = o.decode('utf-8', 'ignore') 1160 | else: 1161 | uo = o 1162 | if isinstance(e, str): 1163 | # ue = unicode(e.encode('string_escape')) 1164 | if PY3K: 1165 | ue = e 1166 | elif e.find("Error") != -1 or e.find("Exception") != -1: 1167 | es = e.decode('utf-8', 'ignore').split('\n') 1168 | try: 1169 | if es[-2].find("\\u") != -1 or es[-2].find('"\\u') != -1: 1170 | es[-2] = es[-2].decode('unicode_escape') 1171 | except Exception: 1172 | pass 1173 | ue = u"\n".join(es) 1174 | else: 1175 | ue = e.decode('utf-8', 'ignore') 1176 | else: 1177 | ue = e 1178 | 1179 | script = self.REPORT_TEST_OUTPUT_TMPL % dict( 1180 | id=tid, 1181 | output=saxutils.escape(uo + ue), 1182 | ) 1183 | if getattr(t,'imgs',[]): 1184 | # 判断截图列表,如果有则追加 1185 | tmp = u"" 1186 | for i, img in enumerate(t.imgs): 1187 | if i==0: 1188 | tmp+=""" \n""" % img 1189 | else: 1190 | tmp+=""" \n""" % img 1191 | imgs = self.IMG_TMPL % dict(imgs=tmp) 1192 | else: 1193 | imgs = u"""无截图""" 1194 | 1195 | row = tmpl % dict( 1196 | tid=tid, 1197 | Class=(n == 0 and 'hiddenRow' or 'none'), 1198 | style=n == 2 and 'errorCase' or (n == 1 and 'failCase' or 'passCase'), 1199 | desc=desc, 1200 | script=script, 1201 | status=self.STATUS[n], 1202 | img=imgs, 1203 | ) 1204 | rows.append(row) 1205 | if not has_output: 1206 | return 1207 | 1208 | def _generate_ending(self): 1209 | return self.ENDING_TMPL 1210 | 1211 | 1212 | ############################################################################## 1213 | # Facilities for running tests from the command line 1214 | ############################################################################## 1215 | 1216 | # Note: Reuse unittest.TestProgram to launch test. In the future we may 1217 | # build our own launcher to support more specific command line 1218 | # parameters like test title, CSS, etc. 1219 | class TestProgram(unittest.TestProgram): 1220 | """ 1221 | A variation of the unittest.TestProgram. Please refer to the base 1222 | class for command line parameters. 1223 | """ 1224 | 1225 | def runTests(self): 1226 | # Pick HTMLTestRunner as the default test runner. 1227 | # base class's testRunner parameter is not useful because it means 1228 | # we have to instantiate HTMLTestRunner before we know self.verbosity. 1229 | if self.testRunner is None: 1230 | self.testRunner = HTMLTestRunner(verbosity=self.verbosity) 1231 | unittest.TestProgram.runTests(self) 1232 | 1233 | 1234 | main = TestProgram 1235 | 1236 | ############################################################################## 1237 | # Executing this module from the command line 1238 | ############################################################################## 1239 | 1240 | if __name__ == "__main__": 1241 | main(module=None) 1242 | --------------------------------------------------------------------------------