├── Base ├── __init__.py ├── __pycache__ │ ├── BaseFile.cpython-34.pyc │ ├── BaseInit.cpython-34.pyc │ ├── BaseLog.cpython-34.pyc │ ├── BaseYaml.cpython-34.pyc │ ├── __init__.cpython-34.pyc │ ├── BaseError.cpython-34.pyc │ ├── BaseExcel.cpython-34.pyc │ ├── BasePickle.cpython-34.pyc │ ├── BaseRunner.cpython-34.pyc │ ├── BaseOperate.cpython-34.pyc │ ├── BaseElementEnmu.cpython-34.pyc │ └── BaseStatistics.cpython-34.pyc ├── BaseFile.py ├── BaseInit.py ├── BaseError.py ├── BasePickle.py ├── BaseElementEnmu.py ├── BaseRunner.py ├── BaseYaml.py ├── BaseEmail.py ├── BaseStatistics.py ├── BaseLog.py ├── BaseExcel.py └── BaseOperate.py ├── Runner ├── __init__.py └── runner.py ├── PageObject ├── __init__.py ├── Home │ ├── __init__.py │ ├── __pycache__ │ │ ├── LoginPage.cpython-34.pyc │ │ ├── __init__.cpython-34.pyc │ │ └── LoginFailPage.cpython-34.pyc │ ├── LoginPage.py │ └── LoginFailPage.py ├── __pycache__ │ ├── Pages.cpython-34.pyc │ ├── SumResult.cpython-34.pyc │ └── __init__.cpython-34.pyc ├── My │ ├── __pycache__ │ │ └── HotTopicPage.cpython-34.pyc │ └── HotTopicPage.py ├── cnblogs │ ├── __pycache__ │ │ └── AspNetPage.cpython-34.pyc │ └── AspNetPage.py ├── SumResult.py └── Pages.py ├── TestCase ├── __init__.py ├── CnblogsTest.py ├── MyTest.py └── HomeTest.py ├── use.md ├── CHANGELOG.md ├── Img ├── sum.png └── detail.jpg ├── Yamls ├── config.yaml ├── home │ ├── LoginFail.yaml │ └── Login.yaml ├── my │ └── HotTopic.yaml └── cnblogs │ └── AspNet.yaml ├── log ├── info.pickle └── sum.pickle ├── start_test.bat ├── Report └── Report.xlsx ├── exe └── chromedriver.exe ├── .idea ├── vcs.xml ├── misc.xml ├── modules.xml ├── wise-oper.iml └── workspace.xml └── README.md /Base/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Runner/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /PageObject/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /TestCase/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /PageObject/Home/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /use.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Louis-me/selenium/HEAD/use.md -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Louis-me/selenium/HEAD/CHANGELOG.md -------------------------------------------------------------------------------- /Img/sum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Louis-me/selenium/HEAD/Img/sum.png -------------------------------------------------------------------------------- /Img/detail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Louis-me/selenium/HEAD/Img/detail.jpg -------------------------------------------------------------------------------- /Yamls/config.yaml: -------------------------------------------------------------------------------- 1 | #url: https://www.cnblogs.com 2 | url: https://testerhome.com/ -------------------------------------------------------------------------------- /log/info.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Louis-me/selenium/HEAD/log/info.pickle -------------------------------------------------------------------------------- /start_test.bat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Louis-me/selenium/HEAD/start_test.bat -------------------------------------------------------------------------------- /Report/Report.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Louis-me/selenium/HEAD/Report/Report.xlsx -------------------------------------------------------------------------------- /exe/chromedriver.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Louis-me/selenium/HEAD/exe/chromedriver.exe -------------------------------------------------------------------------------- /Base/__pycache__/BaseFile.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Louis-me/selenium/HEAD/Base/__pycache__/BaseFile.cpython-34.pyc -------------------------------------------------------------------------------- /Base/__pycache__/BaseInit.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Louis-me/selenium/HEAD/Base/__pycache__/BaseInit.cpython-34.pyc -------------------------------------------------------------------------------- /Base/__pycache__/BaseLog.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Louis-me/selenium/HEAD/Base/__pycache__/BaseLog.cpython-34.pyc -------------------------------------------------------------------------------- /Base/__pycache__/BaseYaml.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Louis-me/selenium/HEAD/Base/__pycache__/BaseYaml.cpython-34.pyc -------------------------------------------------------------------------------- /Base/__pycache__/__init__.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Louis-me/selenium/HEAD/Base/__pycache__/__init__.cpython-34.pyc -------------------------------------------------------------------------------- /Base/__pycache__/BaseError.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Louis-me/selenium/HEAD/Base/__pycache__/BaseError.cpython-34.pyc -------------------------------------------------------------------------------- /Base/__pycache__/BaseExcel.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Louis-me/selenium/HEAD/Base/__pycache__/BaseExcel.cpython-34.pyc -------------------------------------------------------------------------------- /Base/__pycache__/BasePickle.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Louis-me/selenium/HEAD/Base/__pycache__/BasePickle.cpython-34.pyc -------------------------------------------------------------------------------- /Base/__pycache__/BaseRunner.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Louis-me/selenium/HEAD/Base/__pycache__/BaseRunner.cpython-34.pyc -------------------------------------------------------------------------------- /Base/__pycache__/BaseOperate.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Louis-me/selenium/HEAD/Base/__pycache__/BaseOperate.cpython-34.pyc -------------------------------------------------------------------------------- /PageObject/__pycache__/Pages.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Louis-me/selenium/HEAD/PageObject/__pycache__/Pages.cpython-34.pyc -------------------------------------------------------------------------------- /Base/__pycache__/BaseElementEnmu.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Louis-me/selenium/HEAD/Base/__pycache__/BaseElementEnmu.cpython-34.pyc -------------------------------------------------------------------------------- /Base/__pycache__/BaseStatistics.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Louis-me/selenium/HEAD/Base/__pycache__/BaseStatistics.cpython-34.pyc -------------------------------------------------------------------------------- /PageObject/__pycache__/SumResult.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Louis-me/selenium/HEAD/PageObject/__pycache__/SumResult.cpython-34.pyc -------------------------------------------------------------------------------- /PageObject/__pycache__/__init__.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Louis-me/selenium/HEAD/PageObject/__pycache__/__init__.cpython-34.pyc -------------------------------------------------------------------------------- /PageObject/Home/__pycache__/LoginPage.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Louis-me/selenium/HEAD/PageObject/Home/__pycache__/LoginPage.cpython-34.pyc -------------------------------------------------------------------------------- /PageObject/Home/__pycache__/__init__.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Louis-me/selenium/HEAD/PageObject/Home/__pycache__/__init__.cpython-34.pyc -------------------------------------------------------------------------------- /PageObject/My/__pycache__/HotTopicPage.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Louis-me/selenium/HEAD/PageObject/My/__pycache__/HotTopicPage.cpython-34.pyc -------------------------------------------------------------------------------- /PageObject/Home/__pycache__/LoginFailPage.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Louis-me/selenium/HEAD/PageObject/Home/__pycache__/LoginFailPage.cpython-34.pyc -------------------------------------------------------------------------------- /PageObject/cnblogs/__pycache__/AspNetPage.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Louis-me/selenium/HEAD/PageObject/cnblogs/__pycache__/AspNetPage.cpython-34.pyc -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /log/sum.pickle: -------------------------------------------------------------------------------- 1 | (dp0 2 | Vfail 3 | p1 4 | L0L 5 | sVversion 6 | p2 7 | V2018.4.28 8 | p3 9 | sVsum 10 | p4 11 | L3L 12 | sVtestDate 13 | p5 14 | V2018-04-30 12:46:43 15 | p6 16 | sVpass 17 | p7 18 | L3L 19 | sVtestSumDate 20 | p8 21 | V75\u79d2 22 | p9 23 | s. -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /PageObject/SumResult.py: -------------------------------------------------------------------------------- 1 | from Base.BaseStatistics import countInfo, countSum 2 | 3 | 4 | def statistics_result(**kwargs): 5 | countInfo(result=kwargs["result"], testInfo=kwargs["testInfo"], caseName=kwargs["caseName"], name="chrome", 6 | driver=kwargs["driver"], logTest=kwargs["logTest"], testCase=kwargs["testCase"], 7 | testCheck=kwargs["testCheck"]) 8 | countSum(kwargs["result"]) 9 | 10 | -------------------------------------------------------------------------------- /.idea/wise-oper.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | -------------------------------------------------------------------------------- /PageObject/Home/LoginPage.py: -------------------------------------------------------------------------------- 1 | from PageObject import Pages 2 | from Base.BaseYaml import getYam 3 | 4 | 5 | class LoginPage: 6 | def __init__(self, kwargs): 7 | _init = {"driver": kwargs["driver"], "test_msg": getYam(kwargs["path"]), 8 | "logTest": kwargs["logTest"], "caseName": kwargs["caseName"]} 9 | self.page = Pages.PagesObjects(_init) 10 | 11 | def operate(self): # 操作步骤 12 | self.page.operate() 13 | 14 | def checkPoint(self): # 检查点 15 | self.page.checkPoint() 16 | 17 | 18 | if __name__ == "__main__": 19 | pass 20 | -------------------------------------------------------------------------------- /PageObject/Home/LoginFailPage.py: -------------------------------------------------------------------------------- 1 | from PageObject import Pages 2 | from Base.BaseYaml import getYam 3 | 4 | 5 | class LoginFailPage: 6 | def __init__(self, kwargs): 7 | _init = {"driver": kwargs["driver"], "test_msg": getYam(kwargs["path"]), 8 | "logTest": kwargs["logTest"], "caseName": kwargs["caseName"]} 9 | self.page = Pages.PagesObjects(_init) 10 | 11 | def operate(self): # 操作步骤 12 | self.page.operate() 13 | 14 | def checkPoint(self): # 检查点 15 | self.page.checkPoint() 16 | 17 | 18 | if __name__ == "__main__": 19 | pass 20 | -------------------------------------------------------------------------------- /PageObject/My/HotTopicPage.py: -------------------------------------------------------------------------------- 1 | from PageObject import Pages 2 | from Base.BaseYaml import getYam 3 | 4 | ''' 5 | 热门话题 6 | ''' 7 | 8 | 9 | class HotTopicPage: 10 | def __init__(self, kwargs): 11 | _init = {"driver": kwargs["driver"], "test_msg": getYam(kwargs["path"]), 12 | "logTest": kwargs["logTest"], "caseName": kwargs["caseName"]} 13 | self.page = Pages.PagesObjects(_init) 14 | 15 | def operate(self): # 操作步骤 16 | self.page.operate() 17 | 18 | def checkPoint(self): # 检查点 19 | self.page.checkPoint() 20 | 21 | 22 | if __name__ == "__main__": 23 | pass 24 | -------------------------------------------------------------------------------- /PageObject/cnblogs/AspNetPage.py: -------------------------------------------------------------------------------- 1 | from PageObject import Pages 2 | from Base.BaseYaml import getYam 3 | 4 | ''' 5 | 博客园下的asp.net新手区 6 | ''' 7 | 8 | 9 | class AspNetPage: 10 | def __init__(self, kwargs): 11 | _init = {"driver": kwargs["driver"], "test_msg": getYam(kwargs["path"]), 12 | "logTest": kwargs["logTest"], "caseName": kwargs["caseName"]} 13 | self.page = Pages.PagesObjects(_init) 14 | 15 | def operate(self): # 操作步骤 16 | self.page.operate() 17 | 18 | def checkPoint(self): # 检查点 19 | self.page.checkPoint() 20 | 21 | 22 | if __name__ == "__main__": 23 | pass 24 | -------------------------------------------------------------------------------- /Base/BaseFile.py: -------------------------------------------------------------------------------- 1 | __author__ = 'shikun' 2 | 3 | import os 4 | 5 | 6 | ''' 7 | 操作文件 8 | ''' 9 | 10 | 11 | def write_data(f, method='w+', data=""): 12 | if not os.path.isfile(f): 13 | print('文件不存在,写入数据失败') 14 | else: 15 | with open(f, method, encoding="utf-8") as fs: 16 | fs.write(data + "\n") 17 | 18 | 19 | def mkdir_file(f, method='w+'): 20 | if not os.path.isfile(f): 21 | with open(f, method, encoding="utf-8") as fs: 22 | print("创建文件%s成功" % f) 23 | pass 24 | else: 25 | print("%s文件已经存在,创建失败" % f) 26 | pass 27 | 28 | 29 | def remove_file(f): 30 | if os.path.isfile(f): 31 | os.remove(f) 32 | else: 33 | print("%s文件不存在,无法删除" % f) 34 | -------------------------------------------------------------------------------- /Yamls/home/LoginFail.yaml: -------------------------------------------------------------------------------- 1 | testinfo: 2 | - id: test001 3 | title: 登录失败 4 | launch: 1 5 | info: 打开testerhome 6 | testcase: 7 | - element_info: div.container>ul>li:nth-child(2) 8 | find_type: css 9 | operate_type: click 10 | info: 点击登录 11 | - element_info: input-lg 12 | find_type: class_name 13 | operate_type: send_keys 14 | msg: lose1 15 | info: 输入用户名lose1 16 | - element_info: user_password 17 | find_type: id 18 | operate_type: send_keys 19 | msg: 1231231232 20 | info: 输入密码1231231232 21 | - element_info: div.form-actions 22 | find_type: css 23 | operate_type: click 24 | info: 点击登录 25 | 26 | check: 27 | - element_info: div.alert-warning 28 | find_type: css 29 | info: 出现错误的密码登录不成功提示框 -------------------------------------------------------------------------------- /TestCase/CnblogsTest.py: -------------------------------------------------------------------------------- 1 | from Base.BaseRunner import ParametrizedTestCase 2 | import os 3 | import sys 4 | from PageObject.cnblogs.AspNetPage import AspNetPage 5 | 6 | PATH = lambda p: os.path.abspath( 7 | os.path.join(os.path.dirname(__file__), p) 8 | ) 9 | 10 | 11 | class CnblogsTest(ParametrizedTestCase): 12 | def testAspNet(self): 13 | app = {"logTest": self.logTest, "driver": self.driver, "path": PATH("../Yamls/cnblogs/AspNet.yaml"), 14 | "caseName": sys._getframe().f_code.co_name} 15 | 16 | page = AspNetPage(app) 17 | page.operate() 18 | page.checkPoint() 19 | 20 | @classmethod 21 | def setUpClass(cls): 22 | super(CnblogsTest, cls).setUpClass() 23 | 24 | @classmethod 25 | def tearDownClass(cls): 26 | super(CnblogsTest, cls).tearDownClass() 27 | -------------------------------------------------------------------------------- /Yamls/my/HotTopic.yaml: -------------------------------------------------------------------------------- 1 | testinfo: 2 | - id: test003 3 | title: 热门话题 4 | info: 打开testerhome 5 | testcase: 6 | - element_info: dropdown-avatar 7 | find_type: class_name 8 | operate_type: click 9 | info: 点击图像 10 | - element_info: //ul[@class='dropdown-menu']/li/a 11 | find_type: xpath 12 | operate_type: click 13 | info: 点击用户名 14 | - element_info: //ul[@class="list-group"]/li[1]/div/a[2] 15 | find_type: xpath 16 | operate_type: get_text 17 | info: 获取热门话题下的第一条标题 18 | - element_info: ul.list-group > li:nth-child(1) > div.title > a:nth-child(2) 19 | find_type: css 20 | operate_type: click 21 | info: 点击热门话题下的第一条标题 22 | 23 | check: 24 | - element_info: /html/head/title 25 | find_type: xpath 26 | operate_type: get_text 27 | check: compare 28 | info: 详情页的标题和历史标题相等 -------------------------------------------------------------------------------- /Yamls/cnblogs/AspNet.yaml: -------------------------------------------------------------------------------- 1 | testinfo: 2 | - id: test005 3 | title: .net分类下的详情页 4 | info: 打开博客园 5 | testcase: 6 | - element_info: cate_item_108698 7 | find_type: id 8 | operate_type: move_to_element 9 | info: 鼠标悬停到.net分类上 10 | - element_info: div.cate_content_block > ul > li:nth-child(1) 11 | find_type: css 12 | operate_type: click 13 | info: 点击新手区 14 | - element_info: div#post_list > div:nth-child(1) > div.post_item_body > h3 > a 15 | find_type: css 16 | operate_type: get_text 17 | info: 获取新手区推荐的第一条数据 18 | - element_info: div#post_list > div:nth-child(1) > div.post_item_body > h3 > a 19 | find_type: css 20 | operate_type: click 21 | info: 点击新手区推荐的第一条数据 22 | 23 | check: 24 | - element_info: /html/head/title 25 | find_type: xpath 26 | operate_type: get_text 27 | check: compare 28 | info: 详情页的标题和历史标题相等 -------------------------------------------------------------------------------- /Yamls/home/Login.yaml: -------------------------------------------------------------------------------- 1 | testinfo: 2 | - id: test001 3 | title: 登录成功 4 | info: 打开testerhome 5 | testcase: 6 | - element_info: div.container>ul>li:nth-child(2) 7 | find_type: css 8 | operate_type: click 9 | info: 点击登录 10 | - element_info: input-lg 11 | find_type: class_name 12 | operate_type: send_keys 13 | msg: lose 14 | info: 输入用户名lose 15 | - element_info: user_password 16 | find_type: id 17 | operate_type: send_keys 18 | msg: XXXXX 19 | info: 输入密码XXXXXX 20 | - element_info: div.form-actions 21 | find_type: css 22 | operate_type: click 23 | info: 点击登录 24 | - element_info: dropdown-avatar 25 | find_type: class_name 26 | operate_type: click 27 | info: 点击图像 28 | 29 | check: 30 | - element_info: //ul[@class='dropdown-menu']/li/a[contains(text(),'lose')] 31 | find_type: xpath 32 | info: 查找用户名lose成功 -------------------------------------------------------------------------------- /Base/BaseInit.py: -------------------------------------------------------------------------------- 1 | from Base.BaseElementEnmu import Element 2 | from Base.BasePickle import * 3 | from Base.BaseFile import * 4 | 5 | PATH = lambda p: os.path.abspath( 6 | os.path.join(os.path.dirname(__file__), p) 7 | ) 8 | 9 | 10 | def mk_file(): 11 | destroy() 12 | mkdir_file(PATH("../Log/"+Element.INFO_FILE)) 13 | mkdir_file(PATH("../Log/"+Element.SUM_FILE)) 14 | 15 | data = read(PATH("../Log/"+Element.INFO_FILE)) 16 | 17 | data["version"] = "2018.4.28" 18 | data["sum"] = 0 19 | data["pass"] = 0 20 | data["fail"] = 0 21 | write(data=data, path=PATH("../Log/"+Element.SUM_FILE)) 22 | 23 | 24 | 25 | 26 | 27 | def destroy(): 28 | remove_file(PATH("../Log/"+Element.INFO_FILE)) 29 | remove_file(PATH("../Log/"+Element.SUM_FILE)) 30 | # remove_file(PATH("../Log/"+Element.DEVICES_FILE)) 31 | 32 | 33 | if __name__ == '__main__': 34 | print(destroy()) 35 | # print(datetime.now().strftime('%Y-%m-%d %H:%M:%S')) 36 | -------------------------------------------------------------------------------- /Runner/runner.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | __author__ = 'shikun' 4 | import sys 5 | sys.path.append("..") 6 | from Base.BaseRunner import ParametrizedTestCase 7 | from TestCase.HomeTest import HomeTest 8 | import unittest 9 | from Base.BaseInit import mk_file 10 | from Base.BaseStatistics import countDate, writeExcel 11 | from datetime import datetime 12 | from TestCase.MyTest import MyTest 13 | from TestCase.CnblogsTest import CnblogsTest 14 | 15 | def runnerCaseApp(): 16 | start_time = datetime.now() 17 | suite = unittest.TestSuite() 18 | suite.addTest(ParametrizedTestCase.parametrize(HomeTest)) 19 | suite.addTest(ParametrizedTestCase.parametrize(MyTest)) 20 | # suite.addTest(ParametrizedTestCase.parametrize(CnblogsTest)) 21 | unittest.TextTestRunner(verbosity=2).run(suite) 22 | end_time = datetime.now() 23 | countDate(datetime.now().strftime('%Y-%m-%d %H:%M:%S'), str((end_time - start_time).seconds) + "秒") 24 | 25 | 26 | if __name__ == '__main__': 27 | mk_file() 28 | runnerCaseApp() 29 | writeExcel() 30 | -------------------------------------------------------------------------------- /TestCase/MyTest.py: -------------------------------------------------------------------------------- 1 | from Base.BaseRunner import ParametrizedTestCase 2 | import os 3 | import sys 4 | from PageObject.My.HotTopicPage import HotTopicPage 5 | from PageObject.Home.LoginPage import LoginPage 6 | PATH = lambda p: os.path.abspath( 7 | os.path.join(os.path.dirname(__file__), p) 8 | ) 9 | 10 | 11 | class MyTest(ParametrizedTestCase): 12 | def login(self): 13 | app = {"logTest": self.logTest, "driver": self.driver, "path": PATH("../Yamls/home/Login.yaml"), 14 | "caseName": sys._getframe().f_code.co_name} 15 | page = LoginPage(app) 16 | page.operate() 17 | 18 | def testHotTopic(self): 19 | self.login() 20 | app = {"logTest": self.logTest, "driver": self.driver, "path": PATH("../Yamls/my/HotTopic.yaml"), 21 | "caseName": sys._getframe().f_code.co_name} 22 | 23 | page = HotTopicPage(app) 24 | page.operate() 25 | page.checkPoint() 26 | 27 | @classmethod 28 | def setUpClass(cls): 29 | super(MyTest, cls).setUpClass() 30 | 31 | @classmethod 32 | def tearDownClass(cls): 33 | super(MyTest, cls).tearDownClass() 34 | -------------------------------------------------------------------------------- /Base/BaseError.py: -------------------------------------------------------------------------------- 1 | from Base.BaseElementEnmu import Element 2 | 3 | """ 4 | element_info: 元素 5 | info: 用例说明 6 | current: 当前值 7 | history: 历史值 8 | type: 错误类型 9 | """ 10 | 11 | 12 | def get_error(kw): 13 | elements = { 14 | Element.TIME_OUT: lambda: "==%s请求超时==" % kw["element_info"], 15 | Element.NO_SUCH: lambda: "==%s不存在==" % kw["element_info"], 16 | Element.WEB_DROVER_EXCEPTION: lambda: "==%s的driver错误==" % kw["element_info"], 17 | Element.INDEX_ERROR: lambda: "==%s索引错误==" % kw["element_info"], 18 | Element.STALE_ELEMENT_REFERENCE_EXCEPTION: lambda: "==%s页面元素已经发生==" % kw["element_info"], 19 | Element.DEFAULT_ERROR: lambda: "==请检查%s==" % kw["element_info"], 20 | Element.CONTRARY: lambda: "==检查点_%s失败_%s依然在页面==" % (kw["info"], kw["element_info"]), 21 | Element.CONTRARY_GETVAL: lambda: "==检查点_对比数据失败,当前取到到数据为:%s,历史取到数据为:%s" % (kw["current"], kw["history"]), 22 | Element.DEFAULT_CHECK: lambda: "==检查点_%s失败,请检查_%s==" % (kw["info"], kw["element_info"]), 23 | Element.COMPARE: lambda: "==检查点_对比数据失败,当前取到到数据为:%s,历史取到数据为:%s" % (kw["current"], kw["history"]) 24 | } 25 | return elements[kw["type"]]() 26 | -------------------------------------------------------------------------------- /TestCase/HomeTest.py: -------------------------------------------------------------------------------- 1 | from Base.BaseRunner import ParametrizedTestCase 2 | import os 3 | import sys 4 | from PageObject.Home.LoginPage import LoginPage 5 | from PageObject.Home.LoginFailPage import LoginFailPage 6 | 7 | PATH = lambda p: os.path.abspath( 8 | os.path.join(os.path.dirname(__file__), p) 9 | ) 10 | 11 | 12 | class HomeTest(ParametrizedTestCase): 13 | def testALoginFail(self): 14 | app = {"logTest": self.logTest, "driver": self.driver, "path": PATH("../Yamls/home/LoginFail.yaml"), 15 | "caseName": sys._getframe().f_code.co_name} 16 | 17 | page = LoginFailPage(app) 18 | page.operate() 19 | page.checkPoint() 20 | 21 | def testBLogin(self): 22 | app = {"logTest": self.logTest, "driver": self.driver, "path": PATH("../Yamls/home/Login.yaml"), 23 | "caseName": sys._getframe().f_code.co_name} 24 | 25 | page = LoginPage(app) 26 | page.operate() 27 | page.checkPoint() 28 | 29 | @classmethod 30 | def setUpClass(cls): 31 | super(HomeTest, cls).setUpClass() 32 | 33 | @classmethod 34 | def tearDownClass(cls): 35 | super(HomeTest, cls).tearDownClass() 36 | -------------------------------------------------------------------------------- /Base/BasePickle.py: -------------------------------------------------------------------------------- 1 | __author__ = "shikun" 2 | import pickle 3 | import os 4 | 5 | def write(data, path="data.pickle"): 6 | with open(path, 'wb') as f: 7 | pickle.dump(data, f, 0) 8 | def read(path): 9 | data = {} 10 | with open(path, 'rb') as f: 11 | try: 12 | data = pickle.load(f) 13 | except EOFError: 14 | data = {} 15 | # print("读取文件错误") 16 | return data 17 | 18 | def readInfo(path): 19 | data = [] 20 | with open(path, 'rb') as f: 21 | try: 22 | data = pickle.load(f) 23 | print(data) 24 | except EOFError: 25 | data = [] 26 | # print("读取文件错误") 27 | return data 28 | 29 | 30 | 31 | def writeInfo(data="", path="data.pickle"): 32 | """ 33 | 34 | :type data: dict 35 | """ 36 | _read = readInfo(path) 37 | result = [] 38 | if _read: 39 | _read.append(data) 40 | result = _read 41 | else: 42 | result.append(data) 43 | with open(path, 'wb') as f: 44 | # print("------writeInfo-------") 45 | # print(result) 46 | pickle.dump(result, f) 47 | 48 | if __name__ == "__main__": 49 | # write("用例失败重连过一次,失败原因:", "../Log/connect64dd15b8-ca91-11e7-87ae-38c98647adce.pickle") 50 | read_reconnect("../Log/connect64dd15b8-ca91-11e7-87ae-38c98647adce.pickle") 51 | -------------------------------------------------------------------------------- /Base/BaseElementEnmu.py: -------------------------------------------------------------------------------- 1 | class Element(object): 2 | 3 | # selenium关键字 4 | find_element_by_id = "id" 5 | find_elements_by_id = "ids" 6 | INDEX = "index" 7 | find_elements_by_xpath = "xpaths" 8 | find_element_by_xpath = "xpath" 9 | find_element_by_css_selector = "css" 10 | find_element_by_class_name = "class_name" 11 | find_element_by_link_text = "link_text" 12 | CLICK = "click" 13 | GET_TEXT = "get_text" 14 | SEND_KEYS = "send_keys" 15 | GET_VALUE = "get_value" 16 | WAIT_TIME = 20 # 查找元素等待时间 17 | MOVE_TO_ELEMENT = "move_to_element" # 鼠标悬停 18 | DEFAULT_OPERATE= "default_operate" # 默认值 19 | 20 | 21 | # 错误日志 22 | TIME_OUT = "timeout" 23 | NO_SUCH = "noSuch" 24 | WEB_DROVER_EXCEPTION = "WebDriverException" 25 | INDEX_ERROR = "index_error" 26 | STALE_ELEMENT_REFERENCE_EXCEPTION = "StaleElementReferenceException" 27 | DEFAULT_ERROR = "default_error" 28 | 29 | # 检查点 30 | CONTRARY = "contrary" # 相反检查点,表示如果检查元素存在就说明失败,如删除后,此元素依然存在 31 | CONTRARY_GETVAL = "contrary_getval" # 检查点关键字contrary_getval: 相反值检查点,如果对比成功,说明失败 32 | DEFAULT_CHECK = "default_check" # 默认检查点,就是查找页面元素 33 | COMPARE = "compare" # 历史数据和实际数据对比 34 | 35 | RE_CONNECT = 1 # 是否打开失败后再次运行一次用例 36 | 37 | # 文件名字 38 | INFO_FILE = "info.pickle" 39 | SUM_FILE = "sum.pickle" 40 | DEVICES_FILE = "devices.pickle" 41 | REPORT_FILE = "Report.xlsx" 42 | -------------------------------------------------------------------------------- /Base/BaseRunner.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from Base.BaseLog import myLog 3 | import unittest 4 | from selenium import webdriver 5 | import os 6 | from Base.BaseYaml import getYam 7 | 8 | PATH = lambda p: os.path.abspath( 9 | os.path.join(os.path.dirname(__file__), p) 10 | ) 11 | 12 | 13 | def get_driver(): 14 | chromedriver = PATH("../exe/chromedriver.exe") 15 | os.environ["webdriver.chrome.driver"] = chromedriver 16 | driver = webdriver.Chrome(chromedriver) 17 | driver.maximize_window() # 将浏览器最大化 18 | openurl = getYam(PATH("../Yamls/config.yaml"))[1]["url"] 19 | driver.get(openurl) 20 | return driver 21 | 22 | 23 | class ParametrizedTestCase(unittest.TestCase): 24 | """ TestCase classes that want to be parametrized should 25 | inherit from this class. 26 | """ 27 | 28 | def __init__(self, methodName='runTest', param=None): 29 | super(ParametrizedTestCase, self).__init__(methodName) 30 | 31 | @classmethod 32 | def setUpClass(cls): 33 | pass 34 | cls.driver = get_driver() 35 | cls.logTest = myLog().getLog("chrome") # 每个设备实例化一个日志记录器 36 | 37 | def setUp(self): 38 | pass 39 | 40 | @classmethod 41 | def tearDownClass(cls): 42 | cls.driver.close() 43 | cls.driver.quit() 44 | pass 45 | 46 | def tearDown(self): 47 | pass 48 | 49 | @staticmethod 50 | def parametrize(testcase_klass, param=None): 51 | # print("---parametrize-----") 52 | # print(param) 53 | testloader = unittest.TestLoader() 54 | testnames = testloader.getTestCaseNames(testcase_klass) 55 | suite = unittest.TestSuite() 56 | for name in testnames: 57 | suite.addTest(testcase_klass(name, param=param)) 58 | return suite 59 | -------------------------------------------------------------------------------- /Base/BaseYaml.py: -------------------------------------------------------------------------------- 1 | __author__ = 'shikun' 2 | import yaml 3 | from yaml.scanner import ScannerError 4 | import os 5 | 6 | 7 | # -*- coding:utf-8 -*- 8 | def getYam(path): 9 | try: 10 | with open(path, encoding='utf-8') as f: 11 | x = yaml.load(f) 12 | return [True, x] 13 | except FileNotFoundError: 14 | print("==用例文件不存在==") 15 | app = {'check': [{'element_info': '', 'operate_type': 'get_value', 'find_type': 'ids', 'info': '用例文件不存在'}], 16 | 'testinfo': [{'title': '', 'id': '', 'info': ''}], 'testcase': [{'element_info': '', 'info': '', 'operate_type': '', 'find_type': ''}, 17 | {'element_info': '', 'msg': "", 'operate_type': '', 'find_type': '', 'info': ''}, {'element_info': '', 'msg': '', 'operate_type': '', 'find_type': '', 'info': ''}, 18 | {'element_info': '', 'info': '', 'operate_type': '', 'find_type': ''}]} 19 | 20 | return [False, app] 21 | except yaml.scanner.ScannerError: 22 | app = {'check': [{'element_info': '', 'operate_type': 'get_value', 'find_type': 'ids', 'info': '用例文件格式错误'}], 23 | 'testinfo': [{'title': '', 'id': '', 'info': ''}], 24 | 'testcase': [{'element_info': '', 'info': '', 'operate_type': '', 'find_type': ''}, 25 | {'element_info': '', 'msg': "", 'operate_type': '', 'find_type': '', 'info': ''}, 26 | {'element_info': '', 'msg': '', 'operate_type': '', 'find_type': '', 'info': ''}, 27 | {'element_info': '', 'info': '', 'operate_type': '', 'find_type': ''}]} 28 | print("==用例格式错误==") 29 | return [False, app] 30 | 31 | 32 | if __name__ == '__main__': 33 | 34 | PATH = lambda p: os.path.abspath( 35 | os.path.join(os.path.dirname(__file__), p) 36 | ) 37 | t = getYam(PATH("../Yamls/Home/Login.yaml")) 38 | print(t) 39 | -------------------------------------------------------------------------------- /Base/BaseEmail.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from email.header import Header 3 | from email.mime.text import MIMEText 4 | from email.utils import parseaddr, formataddr 5 | from email.mime.multipart import MIMEMultipart 6 | from email.mime.application import MIMEApplication 7 | import smtplib 8 | import os 9 | PATH = lambda p: os.path.abspath( 10 | os.path.join(os.path.dirname(__file__), p) 11 | ) 12 | def _format_addr(s): 13 | name, addr = parseaddr(s) 14 | return formataddr((Header(name, 'utf-8').encode(), addr)) 15 | def send_mail(**kwargs): 16 | ''' 17 | :param f: 附件路径 18 | :param to_addr:发给的人 [] 19 | :return: 20 | ''' 21 | from_addr = kwargs["mail_user"] 22 | password = kwargs["mail_pass"] 23 | # to_addr = "ashikun@126.com" 24 | smtp_server = kwargs["mail_host"] 25 | 26 | msg = MIMEMultipart() 27 | 28 | # msg = MIMEText('hello, send by Python...', 'plain', 'utf-8') 29 | msg['From'] = _format_addr('来自<%s>接口测试' % from_addr) 30 | msg['To'] = _format_addr(' <%s>' % kwargs["to_addr"]) 31 | msg['Subject'] = Header(kwargs["header_msg"], 'utf-8').encode() 32 | msg.attach(MIMEText(kwargs["attach"], 'plain', 'utf-8')) 33 | 34 | if kwargs.get("report", "0") != "0": 35 | part = MIMEApplication(open(kwargs["report"], 'rb').read()) 36 | part.add_header('Content-Disposition', 'attachment', filename=('gb2312', '', kwargs["report_name"])) 37 | msg.attach(part) 38 | 39 | server = smtplib.SMTP_SSL(smtp_server, kwargs["port"]) 40 | server.set_debuglevel(1) 41 | server.login(from_addr, password) 42 | server.sendmail(from_addr, kwargs["to_addr"], msg.as_string()) 43 | server.quit() 44 | if __name__ == '__main__': 45 | to_addr = ["284772894@qq.com"] 46 | mail_host = "smtp.qq.com" 47 | mail_user = "284772894@qq.com" 48 | mail_pass = "oftllbhnknegbjhb" 49 | port = "465" 50 | header_msg = "接口测试" 51 | attach = "接口测试" 52 | report = PATH("../Runner/report.xlsx") 53 | send_mail(to_addr = to_addr, mail_host = mail_host, mail_user=mail_user, port=port, mail_pass=mail_pass, header_msg=header_msg, report=report, attach=attach, report_name="接口测试报告") 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 项目名及简介 2 | * python3 + selenium自动化测试 3 | 4 | # 介绍 5 | * unittest参数化 6 | * PageObject分层管理 7 | * 用例编写基于yaml配置多关键字驱动 8 | * 自动生成excel测试报告 9 | 10 | 11 | 12 | ## 命令运行 13 | 14 | ``` 15 | python runner.py 16 | ``` 17 | - 或者修改start.test.bat中的路径后,直接点击运行 18 | 19 | 20 | 21 | # 结果展示 22 | 23 | **日志目录** 24 | 25 | - 文件夹:chrome_XXXXX,包含截图 26 | 27 | ``` 28 | 2018-04-29 23:28:09,357 - INFO - ---- test001_登录失败_div.container>ul>li:nth-child(2)_css_click_ ---- 29 | 2018-04-29 23:28:09,970 - INFO - ---- test001_登录失败_input-lg_class_name_send_keys_lose1 ---- 30 | 2018-04-29 23:28:10,066 - INFO - ---- test001_登录失败_user_password_id_send_keys_1231231232 ---- 31 | 2018-04-29 23:28:10,187 - INFO - ---- test001_登录失败_div.form-actions_css_click_ ---- 32 | 2018-04-29 23:28:10,784 - INFO - ---- test001_登录失败_div.alert-warning_css_ _ ---- 33 | 2018-04-29 23:28:10,785 - INFO - [CheckPoint_1]: testALoginFail_ : OK 34 | 2018-04-29 23:28:36,116 - INFO - ---- test001_登录_div.container>ul>li:nth-child(2)_css_click_ ---- 35 | 2018-04-29 23:29:41,881 - INFO - ---- test001_登录_input-lg_class_name_send_keys_lose1 ---- 36 | 2018-04-29 23:30:16,331 - INFO - ---- test001_登录_user_password_id_send_keys_1231231232 ---- 37 | 2018-04-29 23:30:16,433 - INFO - ---- test001_登录_div.form-actions_css_click_ ---- 38 | 2018-04-29 23:31:02,425 - INFO - [CheckPoint_2]: testBLogin_==请检查dropdown-avatar==: NG 39 | ``` 40 | - 实时日志 41 | 42 | ```buildoutcfg 43 | 44 | testALoginFail (TestCase.HomeTest.HomeTest) ... ==操作步骤:div.container>ul>li:nth-child(2)_css_click_ == 45 | ==操作步骤:input-lg_class_name_send_keys_lose1== 46 | ==操作步骤:user_password_id_send_keys_1231231232== 47 | ==操作步骤:div.form-actions_css_click_ == 48 | ==操作步骤:div.alert-warning_css_ _ == 49 | ==用例_登录失败检查点成功== 50 | ok 51 | testBLogin (TestCase.HomeTest.HomeTest) ... ==操作步骤:div.container>ul>li:nth-child(2)_css_click_ == 52 | ==操作步骤:input-lg_class_name_send_keys_lose== 53 | ==操作步骤:user_password_id_send_keys_XXX== 54 | ==操作步骤:div.form-actions_css_click_ == 55 | ==操作步骤:dropdown-avatar_class_name_click_ == 56 | ==操作步骤://ul[@class='dropdown-menu']/li/a[contains(text(),'lose')]_xpath_ _ == 57 | ==用例_登录检查点成功== 58 | [{'caseName': 'testALoginFail', 'step': '点击登录\n输入用户名\n输入密码\n点击登录\n', 'info': '打开testerhome', 'title': '登录失败', 'checkStep': '错误的密码登录不成功\n', 'id': 'test001', 'msg': '', 'name': 'chrome', 'result': '通过'}] 59 | ok 60 | testHotTopic (TestCase.MyTest.MyTest) ... ==操作步骤:div.container>ul>li:nth-child(2)_css_click_ == 61 | ==操作步骤:input-lg_class_name_send_keys_lose== 62 | ==操作步骤:user_password_id_send_keys_xxxx== 63 | ==操作步骤:div.form-actions_css_click_ == 64 | ==操作步骤:dropdown-avatar_class_name_click_ == 65 | ==操作步骤:dropdown-avatar_class_name_click_ == 66 | ==操作步骤://ul[@class='dropdown-menu']/li/a_xpath_click_ == 67 | ==操作步骤://ul[@class="list-group"]/li[1]/div/a[2]_xpath_get_text_ == 68 | ==操作步骤:ul.list-group > li:nth-child(1) > div.title > a:nth-child(2)_css_click_ == 69 | ==操作步骤:/html/head/title_xpath_get_text_ == 70 | ==用例_热门话题检查点成功== 71 | ``` 72 | 73 | 74 | **测试报告** 75 | 76 | ![sum.png](Img/sum.png "sum.png") 77 | 78 | ![detail.jpg](Img/detail.jpg "detail.jpg") 79 | 80 | # 其他 81 | * [使用实例](use.md) 82 | * [changelog](CHANGELOG.md) 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /Base/BaseStatistics.py: -------------------------------------------------------------------------------- 1 | import xlsxwriter 2 | 3 | from Base.BaseElementEnmu import Element 4 | from Base.BaseExcel import OperateReport 5 | from Base.BaseInit import destroy 6 | from Base.BasePickle import * 7 | from datetime import datetime 8 | 9 | PATH = lambda p: os.path.abspath( 10 | os.path.join(os.path.dirname(__file__), p) 11 | ) 12 | 13 | ''' 14 | 统计数据相关 15 | ''' 16 | 17 | ''' 18 | result bool 19 | logTest 记录日志类 class 20 | driver 21 | testinfo 22 | 23 | ''' 24 | 25 | 26 | def countInfo(**kwargs): 27 | _info = {} 28 | step = "" # 操作步骤信息 29 | check_step = "" # 检查点步骤信息 30 | 31 | for case in kwargs["testCase"]: 32 | step = step + case["info"] + "\n" 33 | 34 | if type(kwargs["testCheck"]) == list: # 检查点为列表 35 | for check in kwargs["testCheck"]: 36 | check_step = check_step + check["info"] + "\n" 37 | elif type(kwargs["testCheck"]) == dict: 38 | check_step = kwargs["testCheck"]["info"] 39 | else: 40 | print("获取检查点步骤数据错误,请检查") 41 | print(kwargs["testCheck"]) 42 | 43 | _info["step"] = step # 用例操作步骤 44 | _info["checkStep"] = check_step # 用例检查点 45 | 46 | if kwargs["result"]: 47 | _info["result"] = "通过" 48 | kwargs["logTest"].checkPointOK(driver=kwargs["driver"], caseName=kwargs["testInfo"][0]["title"], 49 | checkPoint=kwargs["caseName"] + "_" + kwargs["testInfo"][0].get( 50 | "msg", " ")) 51 | else: 52 | _info["result"] = "失败" # 用例接开关 53 | _info["img"] = kwargs["logTest"].checkPointNG(driver=kwargs["driver"], caseName=kwargs["testInfo"][0]["title"], 54 | checkPoint=kwargs["caseName"] + "_" + kwargs["testInfo"][0].get( 55 | "msg", " ")) 56 | _info["id"] = kwargs["testInfo"][0]["id"] # 用例id 57 | _info["title"] = kwargs["testInfo"][0]["title"] # 用例名称 58 | _info["caseName"] = kwargs["caseName"] # 测试函数 59 | _info["name"] = kwargs["name"] # 设备名 60 | _info["msg"] = kwargs["testInfo"][0].get("msg", "") # 备注 61 | _info["info"] = kwargs["testInfo"][0]["info"] # 前置条件 62 | 63 | writeInfo(data=_info, path=PATH("../Log/" + Element.INFO_FILE)) 64 | # print(read(PATH("../Log/info.pickle"))) 65 | 66 | 67 | # 统计所有用例数 68 | def countSum(result): 69 | # print("----countSum----") 70 | data = {"sum": 0, "pass": 0, "fail": 0} 71 | _read = read(PATH("../Log/sum.pickle")) 72 | if _read: 73 | data = _read 74 | data["sum"] += 1 75 | if result: 76 | data["pass"] += 1 77 | else: 78 | data["fail"] += 1 79 | write(data=data, path=PATH("../Log/" + Element.SUM_FILE)) 80 | # print(read(PATH("../Log/sum.pickle"))) 81 | 82 | 83 | def countDate(testDate, testSumDate): 84 | data = read(PATH("../Log/" + Element.SUM_FILE)) 85 | if data: 86 | data["testDate"] = testDate 87 | data["testSumDate"] = testSumDate 88 | write(data=data, path=PATH("../Log/" + Element.SUM_FILE)) 89 | else: 90 | print("统计数据失败") 91 | data = read(PATH("../Log/" + Element.SUM_FILE)) 92 | print("==统计数据:%s==" % data) 93 | 94 | 95 | ''' 96 | 测试报告 97 | ''' 98 | 99 | 100 | def writeExcel(): 101 | workbook = xlsxwriter.Workbook(PATH('../Report/' + Element.REPORT_FILE)) 102 | worksheet = workbook.add_worksheet("测试总况") 103 | worksheet2 = workbook.add_worksheet("测试详情") 104 | operateReport = OperateReport(workbook) 105 | operateReport.init(worksheet, read(PATH("../Log/" + Element.SUM_FILE))) 106 | operateReport.detail(worksheet2, readInfo(PATH("../Log/" + Element.INFO_FILE))) 107 | operateReport.close() 108 | 109 | # destroy() # 删除文件 110 | 111 | 112 | if __name__ == '__main__': 113 | # data = {'result': '失败', 'caseName': 'FirstOpenTest', 'title': '第一次打开', 'phoneName': 'samsung_GT-I9500_android_4.4.2', 'img': 'D:\\app\\appium\\log\\samsung_GT-I9500_android_4.4.220170607184558\\第一次打开CheckPoint_1_NG.png', 'id': 'test001'} 114 | # writeInfo(data, PATH("../Log/info.pickle")) 115 | # writeInfo(data, PATH("../Log/info.pickle")) 116 | # _read = readInfo(PATH("../Log/info.pickle")) 117 | writeExcel() 118 | -------------------------------------------------------------------------------- /PageObject/Pages.py: -------------------------------------------------------------------------------- 1 | from Base.BaseYaml import getYam 2 | from Base.BaseOperate import OperateElement 3 | import time 4 | from Base.BaseElementEnmu import Element as be 5 | import os 6 | from PageObject.SumResult import statistics_result 7 | from Base.BaseError import get_error 8 | 9 | PATH = lambda p: os.path.abspath( 10 | os.path.join(os.path.dirname(__file__), p) 11 | ) 12 | 13 | 14 | class PagesObjects: 15 | ''' 16 | page层 17 | kwargs: WebDriver driver, String path(yaml配置参数) 18 | isOperate: 操作失败,检查点就失败 19 | testInfo: 20 | testCase: 21 | ''' 22 | 23 | def __init__(self, kwargs): 24 | self.driver = kwargs["driver"] 25 | 26 | if kwargs.get("launch", "0") == "0": # 若为空, 刷新页面 27 | self.driver.get(self.driver.current_url) 28 | self.operateElement = "" 29 | self.isOperate = True 30 | self.test_msg = kwargs["test_msg"] 31 | self.testInfo = self.test_msg[1]["testinfo"] 32 | self.testCase = self.test_msg[1]["testcase"] 33 | self.test_check = self.test_msg[1]["check"] 34 | self.logTest = kwargs["logTest"] 35 | self.caseName = kwargs["caseName"] 36 | self.get_value = [] 37 | self.is_get = False # 检查点特殊标志,结合get_value使用。若为真,说明检查点要对比历史数据和实际数据 38 | self.msg = "" 39 | 40 | ''' 41 | 操作步骤 42 | ''' 43 | 44 | def operate(self): 45 | 46 | if self.test_msg[0] is False: 47 | self.isOperate = False 48 | return False 49 | self.operateElement = OperateElement(self.driver) 50 | for item in self.testCase: 51 | result = self.operateElement.operate(item, self.testInfo, self.logTest) 52 | if not result["result"]: 53 | msg = get_error({"type": be.DEFAULT_ERROR, "element_info": item["element_info"]}) 54 | print(msg) 55 | self.testInfo[0]["msg"] = msg 56 | self.isOperate = False 57 | return False 58 | if item.get("is_time", "0") != "0": 59 | time.sleep(item["is_time"]) # 等待时间 60 | print("==等待%s秒==" % item["is_time"]) 61 | if item.get("operate_type", "0") == be.GET_VALUE or item.get("operate_type", "0") == be.GET_TEXT: 62 | self.get_value.append(result["text"]) 63 | self.is_get = True # 对比数据 64 | 65 | return True 66 | 67 | def checkPoint(self, kwargs={}): 68 | result = self.check(kwargs) 69 | statistics_result(result=result, testInfo=self.testInfo, caseName=self.caseName, 70 | driver=self.driver, logTest=self.logTest, 71 | testCase=self.testCase, 72 | testCheck=self.test_check) 73 | 74 | ''' 75 | 检查点 76 | caseName:测试用例函数名 用作统计 77 | logTest: 日志记录 78 | ''' 79 | 80 | def check(self, kwargs): 81 | result = True 82 | # if kwargs.get("check_point", "0") != "0": 83 | # return kwargs["check_point"] 84 | 85 | if self.isOperate: 86 | for item in self.test_check: 87 | 88 | resp = self.operateElement.operate(item, self.testInfo, self.logTest) 89 | # 默认检查点,就是查找页面元素 90 | if kwargs.get("check", be.DEFAULT_CHECK) == be.DEFAULT_CHECK and not resp["result"]: 91 | m = get_error({"type": be.DEFAULT_CHECK, "element_info": item["element_info"], "info": item["info"]}) 92 | print(m) 93 | self.testInfo[0]["msg"] = m 94 | result = False 95 | break 96 | # 历史数据和实际数据对比 97 | if kwargs.get("check", be.DEFAULT_CHECK) == be.COMPARE and self.is_get and resp["text"]\ 98 | not in self.get_value: # 历史数据和实际数据对比 99 | result = False 100 | m = get_error({"type": be.COMPARE, "current": item["element_info"], "history": resp["text"]}) 101 | print(m) 102 | self.testInfo[0]["msg"] = m 103 | break 104 | # 相反检查点,表示如果检查元素存在就说明失败,如删除后,此元素依然存在 105 | if kwargs.get("check", be.DEFAULT_CHECK) == be.CONTRARY and resp["result"]: 106 | m = get_error({"type": be.CONTRARY, "element_info": item["element_info"], "info": item["info"]}) 107 | print(m) 108 | self.testInfo[0]["msg"] = m 109 | result = False 110 | break 111 | # 检查点关键字contrary_getval: 相反值检查点,如果对比成功,说明失败 112 | if kwargs.get("check", be.DEFAULT_CHECK) == be.CONTRARY_GETVAL and self.is_get and resp["result"] \ 113 | in self.get_value: 114 | m = get_error( 115 | {"type": be.CONTRARY_GETVAL, "current": item["element_info"], "history": resp["text"]}) 116 | print(m) 117 | self.testInfo[0]["msg"] = m 118 | result = False 119 | break 120 | 121 | else: 122 | result = False 123 | return result 124 | -------------------------------------------------------------------------------- /Base/BaseLog.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import time 3 | import os 4 | from time import sleep 5 | import threading 6 | 7 | PATH = lambda p: os.path.abspath( 8 | os.path.join(os.path.dirname(__file__), p) 9 | ) 10 | 11 | 12 | class Log: 13 | def __init__(self, name): 14 | phone_name = name 15 | global logger, resultPath, logPath 16 | resultPath = PATH("../log") 17 | logPath = os.path.join(resultPath, (phone_name + time.strftime('%Y%m%d%H%M%S', time.localtime()))) 18 | if not os.path.exists(logPath): 19 | os.makedirs(logPath) 20 | self.checkNo = 0 21 | self.logger = logging.getLogger() 22 | self.logger.setLevel(logging.INFO) 23 | 24 | # create handler,write log 25 | fh = logging.FileHandler(os.path.join(logPath, "outPut.log")) 26 | # Define the output format of formatter handler 27 | formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') 28 | fh.setFormatter(formatter) 29 | 30 | self.logger.addHandler(fh) 31 | 32 | def getMyLogger(self): 33 | """get the logger 34 | :return:logger 35 | """ 36 | return self.logger 37 | 38 | def buildStartLine(self, caseNo): 39 | """build the start log 40 | :param caseNo: 41 | :return: 42 | """ 43 | startLine = "---- " + caseNo + " " + " " + \ 44 | " ----" 45 | # startLine = "---- " + caseNo + " " + "START" + " " + \ 46 | # " ----" 47 | self.logger.info(startLine) 48 | 49 | def buildEndLine(self, caseNo): 50 | """build the end log 51 | :param caseNo: 52 | :return: 53 | """ 54 | endLine = "---- " + caseNo + " " + "END" + " " + \ 55 | " ----" 56 | self.logger.info(endLine) 57 | self.checkNo = 0 58 | 59 | def writeResult(self, result): 60 | """write the case result(OK or NG) 61 | :param result: 62 | :return: 63 | """ 64 | reportPath = os.path.join(logPath, "report.txt") 65 | flogging = open(reportPath, "a") 66 | try: 67 | flogging.write(result + "\n") 68 | finally: 69 | flogging.close() 70 | pass 71 | 72 | def resultOK(self, caseNo): 73 | self.writeResult(caseNo + ": OK") 74 | 75 | def resultNG(self, caseNo, reason): 76 | self.writeResult(caseNo + ": NG--" + reason) 77 | 78 | def checkPointOK(self, driver, caseName, checkPoint): 79 | """write the case's checkPoint(OK) 80 | :param driver: 81 | :param caseName: 82 | :param checkPoint: 83 | :return: 84 | """ 85 | self.checkNo += 1 86 | 87 | self.logger.info("[CheckPoint_" + str(self.checkNo) + "]: " + checkPoint + ": OK") 88 | print("==用例_%s检查点成功==" % caseName) 89 | 90 | # take shot 默认去掉成功截图 91 | # self.screenshotOK(driver, caseName) 92 | 93 | def checkPointNG(self, driver, caseName, checkPoint): 94 | """write the case's checkPoint(NG) 95 | :param driver: 96 | :param caseName: 97 | :param checkPoint: 98 | :return: 99 | """ 100 | self.checkNo += 1 101 | 102 | self.logger.info("[CheckPoint_" + str(self.checkNo) + "]: " + checkPoint + ": NG") 103 | # take shot 104 | return self.screenshotNG(driver, caseName) 105 | 106 | def screenshotOK(self, driver, caseName): 107 | """screen shot 108 | :param driver: 109 | :param caseName: 110 | :return: 111 | """ 112 | screenshotPath = os.path.join(logPath, caseName) 113 | screenshotName = "CheckPoint_" + str(self.checkNo) + "_OK.png" 114 | 115 | # wait for animations to complete before taking screenshot 116 | sleep(1) 117 | # driver.get_screenshot_as_file(os.path.join(screenshotPath, screenshotName)) 118 | driver.get_screenshot_as_file(os.path.join(screenshotPath + screenshotName)) 119 | 120 | def screenshotNG(self, driver, caseName): 121 | """screen shot 122 | :param driver: 123 | :param caseName: 124 | :return: 125 | """ 126 | screenshotPath = os.path.join(logPath, caseName) 127 | screenshotName = "CheckPoint_" + str(self.checkNo) + "_NG.png" 128 | 129 | # wait for animations to complete before taking screenshot 130 | sleep(1) 131 | driver.get_screenshot_as_file(os.path.join(screenshotPath + screenshotName)) 132 | return os.path.join(screenshotPath + screenshotName) 133 | 134 | def screenshotERROR(self, driver, caseName): 135 | """screen shot 136 | :param driver: 137 | :param caseName: 138 | :return: 139 | """ 140 | screenshotPath = os.path.join(logPath, caseName) 141 | screenshotName = "ERROR.png" 142 | 143 | # wait for animations to complete before taking screenshot 144 | sleep(1) 145 | driver.get_screenshot_as_file(os.path.join(screenshotPath, screenshotName)) 146 | 147 | 148 | class myLog: 149 | """ 150 | This class is used to get log 151 | """ 152 | 153 | log = None 154 | mutex = threading.Lock() 155 | 156 | def __init__(self): 157 | pass 158 | 159 | @staticmethod 160 | def getLog(devices): 161 | if myLog.log is None: 162 | myLog.mutex.acquire() 163 | myLog.log = Log(devices) 164 | myLog.mutex.release() 165 | 166 | return myLog.log 167 | 168 | 169 | if __name__ == "__main__": 170 | logTest = myLog.getLog("devices") 171 | # logger = logTest.getMyLogger() 172 | logTest.buildStartLine("11111111111111111111111") -------------------------------------------------------------------------------- /Base/BaseExcel.py: -------------------------------------------------------------------------------- 1 | __author__ = 'shikun' 2 | import xlsxwriter 3 | import os 4 | 5 | PATH = lambda p: os.path.abspath( 6 | os.path.join(os.path.dirname(__file__), p) 7 | ) 8 | 9 | 10 | class OperateReport: 11 | def __init__(self, wd): 12 | self.wd = wd 13 | 14 | def init(self, worksheet, data): 15 | # 设置列行的宽高 16 | worksheet.set_column("A:A", 15) 17 | worksheet.set_column("B:B", 20) 18 | worksheet.set_column("C:C", 20) 19 | worksheet.set_column("D:D", 20) 20 | 21 | worksheet.set_row(1, 30) 22 | worksheet.set_row(2, 30) 23 | worksheet.set_row(3, 30) 24 | worksheet.set_row(4, 30) 25 | 26 | define_format_H1 = get_format(self.wd, {'bold': True, 'font_size': 18}) 27 | define_format_H2 = get_format(self.wd, {'bold': True, 'font_size': 14}) 28 | define_format_H1.set_border(1) 29 | 30 | define_format_H2.set_border(1) 31 | define_format_H1.set_align("center") 32 | define_format_H2.set_align("center") 33 | define_format_H2.set_bg_color("blue") 34 | define_format_H2.set_color("#ffffff") 35 | 36 | worksheet.merge_range('A1:D1', '测试报告总概况', define_format_H1) 37 | worksheet.merge_range('A2:D2', 'XXXX项目', define_format_H2) 38 | 39 | _write_center(worksheet, "A3", '测试日期', self.wd) 40 | _write_center(worksheet, "A4", '耗时', self.wd) 41 | _write_center(worksheet, "A5", '总用例', self.wd) 42 | 43 | _write_center(worksheet, "B3", data['testDate'], self.wd) 44 | _write_center(worksheet, "B4", data['testSumDate'], self.wd) 45 | _write_center(worksheet, "B5", data['sum'], self.wd) 46 | 47 | _write_center(worksheet, "C3", "通过", self.wd) 48 | _write_center(worksheet, "C4", "失败", self.wd) 49 | _write_center(worksheet, "C5", "脚本版本", self.wd) 50 | 51 | _write_center(worksheet, "D3", data['pass'], self.wd) 52 | _write_center(worksheet, "D4", data['fail'], self.wd) 53 | _write_center(worksheet, "D5", data['version'], self.wd) 54 | 55 | pie(self.wd, worksheet) 56 | 57 | def detail(self, worksheet, info): 58 | # 设置列行的宽高 59 | worksheet.set_column("A:A", 30) 60 | worksheet.set_column("B:B", 20) 61 | worksheet.set_column("C:C", 20) 62 | worksheet.set_column("D:D", 20) 63 | worksheet.set_column("E:E", 20) 64 | worksheet.set_column("F:F", 20) 65 | worksheet.set_column("G:G", 20) 66 | worksheet.set_column("H:H", 20) 67 | worksheet.set_column("I:I", 20) 68 | worksheet.set_column("J:J", 20) 69 | 70 | worksheet.set_row(1, 30) 71 | worksheet.set_row(2, 30) 72 | worksheet.set_row(3, 30) 73 | worksheet.set_row(4, 30) 74 | worksheet.set_row(5, 30) 75 | worksheet.set_row(6, 30) 76 | worksheet.set_row(7, 30) 77 | worksheet.set_row(8, 30) 78 | worksheet.set_row(9, 30) 79 | worksheet.set_row(10, 30) 80 | 81 | worksheet.merge_range('A1:J1', '测试详情', get_format(self.wd, {'bold': True, 'font_size': 18, 'align': 'center', 82 | 'valign': 'vcenter', 'bg_color': 'blue', 83 | 'font_color': '#ffffff'})) 84 | _write_center(worksheet, "A2", '设备', self.wd) 85 | _write_center(worksheet, "B2", '用例ID', self.wd) 86 | _write_center(worksheet, "C2", '用例介绍', self.wd) 87 | _write_center(worksheet, "D2", '用例函数', self.wd) 88 | _write_center(worksheet, "E2", '前置条件', self.wd) 89 | _write_center(worksheet, "F2", '操作步骤 ', self.wd) 90 | _write_center(worksheet, "G2", '检查点 ', self.wd) 91 | _write_center(worksheet, "H2", '测试结果 ', self.wd) 92 | _write_center(worksheet, "I2", '截图', self.wd) 93 | 94 | temp = 3 95 | for item in info: 96 | _write_center(worksheet, "A" + str(temp), item["name"], self.wd) 97 | _write_center(worksheet, "B" + str(temp), item["id"], self.wd) 98 | _write_center(worksheet, "C" + str(temp), item["title"], self.wd) 99 | _write_center(worksheet, "D" + str(temp), item["caseName"], self.wd) 100 | _write_center(worksheet, "E" + str(temp), item["info"], self.wd) 101 | _write_center(worksheet, "F" + str(temp), item["step"], self.wd) 102 | _write_center(worksheet, "G" + str(temp), item["checkStep"], self.wd) 103 | _write_center(worksheet, "H" + str(temp), item["result"], self.wd) 104 | if item.get("img", "false") == "false": 105 | _write_center(worksheet, "I" + str(temp), "", self.wd) 106 | worksheet.set_row(temp, 30) 107 | else: 108 | worksheet.insert_image('J' + str(temp), item["img"], 109 | {'x_scale': 0.1, 'y_scale': 0.1, 'border': 1}) 110 | worksheet.set_row(temp - 1, 110) 111 | temp += 1 112 | 113 | def close(self): 114 | self.wd.close() 115 | 116 | 117 | def get_format(wd, option={}): 118 | return wd.add_format(option) 119 | 120 | 121 | def get_format_center(wd, num=1): 122 | return wd.add_format({'align': 'center', 'valign': 'vcenter', 'border': num}) 123 | 124 | 125 | def set_border_(wd, num=1): 126 | return wd.add_format({}).set_border(num) 127 | 128 | 129 | def _write_center(worksheet, cl, data, wd): 130 | return worksheet.write(cl, data, get_format_center(wd)) 131 | 132 | 133 | def set_row(worksheet, num, height): 134 | worksheet.set_row(num, height) 135 | 136 | # 生成饼形图 137 | 138 | 139 | def pie(workbook, worksheet): 140 | chart1 = workbook.add_chart({'type': 'pie'}) 141 | chart1.add_series({ 142 | 'name': '自动化测试统计', 143 | 'categories': '=测试总况!$C$3:$C$4', 144 | 'values': '=测试总况!$D$3:$D$4', 145 | }) 146 | chart1.set_title({'name': '测试统计'}) 147 | chart1.set_style(10) 148 | worksheet.insert_chart('A9', chart1, {'x_offset': 25, 'y_offset': 10}) 149 | 150 | 151 | if __name__ == '__main__': 152 | pass 153 | -------------------------------------------------------------------------------- /Base/BaseOperate.py: -------------------------------------------------------------------------------- 1 | 2 | # -*- coding: utf-8 -*- 3 | from selenium.webdriver.support.ui import WebDriverWait 4 | import selenium.common.exceptions 5 | from Base.BaseElementEnmu import Element as be 6 | from selenium.webdriver.common.action_chains import * 7 | import time 8 | import re 9 | 10 | ''' 11 | # 此脚本主要用于查找元素是否存在,操作页面元素 12 | ''' 13 | 14 | 15 | class OperateElement: 16 | def __init__(self, driver=""): 17 | self.driver = driver 18 | 19 | def findElement(self, operate): 20 | ''' 21 | 查找元素.operate,dict|list 22 | operate_type:对应的操作 23 | element_info:元素详情 24 | find_type: find类型 25 | ''' 26 | try: 27 | t = operate["check_time"] if operate.get("check_time", 28 | "0") != "0" else be.WAIT_TIME # 如果自定义检测时间为空,就用默认的检测等待时间 29 | if type(operate) == list: # 多检查点 30 | for item in operate: 31 | t = item["check_time"] if item.get("check_time", "0") != "0" else be.WAIT_TIME 32 | WebDriverWait(self.driver, t).until(lambda x: self.elements_by(item)) 33 | return {"result": True} 34 | if type(operate) == dict: # 单检查点 35 | if operate.get("element_info", "0") == "0": # 如果没有页面元素,就不检测是页面元素,可能是滑动等操作 36 | return {"result": True} 37 | WebDriverWait(self.driver, t).until(lambda x: self.elements_by(operate)) # 操作元素是否存在 38 | return {"result": True} 39 | except selenium.common.exceptions.TimeoutException: 40 | # print("==查找元素超时==") 41 | return {"result": False, "type": be.TIME_OUT} 42 | except selenium.common.exceptions.NoSuchElementException: 43 | # print("==查找元素不存在==") 44 | return {"result": False, "type": be.NO_SUCH} 45 | except selenium.common.exceptions.WebDriverException: 46 | # print("WebDriver出现问题了") 47 | return {"result": False, "type": be.WEB_DROVER_EXCEPTION} 48 | 49 | ''' 50 | 查找元素.mOperate是字典:operate_type,element_info,find_type: 51 | testInfo: 用例介绍 52 | logTest: 记录日志 53 | ''' 54 | 55 | def operate(self, operate, testInfo, logTest): 56 | res = self.findElement(operate) 57 | if res["result"]: 58 | return self.operate_by(operate, testInfo, logTest) 59 | else: 60 | return res 61 | 62 | def operate_by(self, operate, testInfo, logTest): 63 | try: 64 | info = "%s_%s_%s_%s" % ( 65 | operate.get("element_info", " "), operate.get("find_type"), operate.get("operate_type", " "), 66 | operate.get("msg", " ")) 67 | print("==操作步骤:%s==" % info) 68 | logTest.buildStartLine(testInfo[0]["id"] + "_" + testInfo[0]["title"] + "_" + info) # 记录日志 69 | 70 | if operate.get("operate_type", "0") == "0": # 如果没有此字段,说明没有相应操作,一般是检查点,直接判定为成功 71 | return {"result": True} 72 | elements = { 73 | be.CLICK: lambda: self.click(operate), 74 | be.GET_VALUE: lambda: self.get_value(operate), 75 | be.GET_TEXT: lambda: self.get_text(operate), 76 | be.SEND_KEYS: lambda: self.send_keys(operate), 77 | be.MOVE_TO_ELEMENT: lambda: self.move_to_element(operate) 78 | 79 | } 80 | return elements[operate.get("operate_type")]() 81 | except IndexError: 82 | logTest.buildStartLine( 83 | testInfo[0]["id"] + "_" + testInfo[0]["title"] + "_" + operate["element_info"] + "索引错误") # 记录日志 84 | # print(operate["element_info"] + "索引错误") 85 | return {"result": False, "type": be.INDEX_ERROR} 86 | 87 | except selenium.common.exceptions.NoSuchElementException: 88 | logTest.buildStartLine( 89 | testInfo[0]["id"] + "_" + testInfo[0]["title"] + "_" + operate[ 90 | "element_info"] + "页面元素不存在或没加载完成") # 记录日志 91 | # print(operate["element_info"] + "页面元素不存在或没有加载完成") 92 | return {"result": False, "type": be.NO_SUCH} 93 | except selenium.common.exceptions.StaleElementReferenceException: 94 | logTest.buildStartLine( 95 | testInfo[0]["id"] + "_" + testInfo[0]["title"] + "_" + operate[ 96 | "element_info"] + "页面元素已经变化") # 记录日志 97 | # print(operate["element_info"] + "页面元素已经变化") 98 | return {"result": False, "type": be.STALE_ELEMENT_REFERENCE_EXCEPTION} 99 | except KeyError: 100 | # 如果key不存在,一般都是在自定义的page页面去处理了,这里直接返回为真 101 | return {"result": True} 102 | 103 | # 点击事件 104 | def click(self, operate): 105 | # print(self.driver.page_source) 106 | if operate["find_type"] == be.find_element_by_id or operate["find_type"] == be.find_element_by_xpath \ 107 | or be.find_element_by_css_selector or operate["find_type"] == be.find_element_by_class_name or \ 108 | operate["find_type"] == be.find_element_by_link_text: 109 | self.elements_by(operate).click() 110 | elif operate.get("find_type") == be.find_elements_by_id: 111 | self.elements_by(operate)[operate["index"]].click() 112 | return {"result": True} 113 | 114 | def send_keys(self, operate): 115 | """ 116 | :param operate: 117 | :return: 118 | """ 119 | time.sleep(0.5) 120 | self.elements_by(operate).send_keys(operate["msg"]) 121 | return {"result": True} 122 | 123 | def get_text(self, operate): 124 | ''' 125 | :param operate: 126 | :return: {} 127 | ''' 128 | 129 | if operate.get("find_type") == be.find_elements_by_id: 130 | element_info = self.elements_by(operate)[operate["index"]] 131 | 132 | result = element_info.get_attribute("text") 133 | re_reulst = re.findall(r'[a-zA-Z\d+\u4e00-\u9fa5]', result) # 只匹配中文,大小写,字母 134 | return {"result": True, "text": "".join(re_reulst)} 135 | 136 | element_info = self.elements_by(operate) 137 | result = element_info.get_attribute("text") 138 | 139 | re_reulst = re.findall(r'[a-zA-Z\d+\u4e00-\u9fa5]', result) 140 | return {"result": True, "text": "".join(re_reulst)} 141 | 142 | def get_value(self, operate): 143 | if operate.get("find_type") == be.find_elements_by_id: 144 | element_info = self.elements_by(operate)[operate["index"]] 145 | 146 | result = element_info.get_attribute("value") 147 | re_reulst = re.findall(r'[a-zA-Z\d+\u4e00-\u9fa5]', result) # 只匹配中文,大小写,字母 148 | return {"result": True, "text": "".join(re_reulst)} 149 | 150 | element_info = self.elements_by(operate) 151 | result = element_info.get_attribute("value") 152 | 153 | re_reulst = re.findall(r'[a-zA-Z\d+\u4e00-\u9fa5]', result) 154 | return {"result": True, "text": "".join(re_reulst)} 155 | ''' 156 | 鼠标悬停 157 | ''' 158 | def move_to_element(self, operate): 159 | ActionChains(self.driver).move_to_element(self.elements_by(operate)).perform() 160 | return {"result": True} 161 | 162 | # 封装常用的标签 163 | def elements_by(self, operate): 164 | elements = { 165 | be.find_element_by_id: lambda: self.driver.find_element_by_id(operate["element_info"]), 166 | be.find_element_by_xpath: lambda: self.driver.find_element_by_xpath(operate["element_info"]), 167 | be.find_element_by_class_name: lambda: self.driver.find_element_by_class_name(operate['element_info']), 168 | be.find_elements_by_id: lambda: self.driver.find_elements_by_id(operate['element_info']), 169 | be.find_element_by_css_selector: lambda: self.driver.find_element_by_css_selector(operate['element_info']), 170 | be.find_element_by_link_text: lambda: self.driver.find_element_by_link_text(operate['element_info']) 171 | 172 | } 173 | return elements[operate["find_type"]]() 174 | -------------------------------------------------------------------------------- /.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 17 | 18 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 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 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 119 | 120 | 121 | 122 | //*[@id="applyContinue"]/div/div/div/div[4]/div/textarea 123 | GetAppCase 124 | workbook 125 | ro 126 | root 127 | 128 | 129 | webcase 130 | self.wd 131 | 132 | 133 | 134 | 136 | 137 | 175 | 176 | 177 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 207 | 208 | 211 | 212 | 213 | 214 | 217 | 218 | 221 | 222 | 225 | 226 | 227 | 228 | 231 | 232 | 235 | 236 | 239 | 240 | 243 | 244 | 245 | 246 | 249 | 250 | 253 | 254 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 289 | 290 | 291 | 292 | 308 | 309 | 325 | 326 | 342 | 343 | 359 | 360 | 376 | 377 | 393 | 394 | 405 | 406 | 424 | 425 | 443 | 444 | 464 | 465 | 486 | 487 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 530 | 531 | 532 | 533 | 1492738167267 534 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | --------------------------------------------------------------------------------