class AppPractice(unittest.TestCase): """练习""" def setUp(self): try: self.base_page = BasePage() self.log = MyLog().get_log().logger except Exception as e: self.log.error(e) raise Exception("出现异常!") def test_app_practice(self): """app练习""" try: # 用例开始,输入负责人姓名,必须 self.base_page.case_start("李彬") if not self.base_page.home_page_to("练习"): # 判断待办事项中是否有练习 if AppPractice.enter(self) is False: # 首页进入考试 # 如果没有练习试卷就结束用例 self.base_page.case_pass() return # 开始答题 AppPractice.answer(self) # 用例成功,必须 self.base_page.case_pass() except Exception as e: self.log.error(e) # 用例失败,必须 self.base_page.case_failed() raise Exception("出现异常!") finally: self.base_page.back_to() def enter(self): """进入练习""" practice_module = ("css_selector", "img[src='img/practice-icon.png']" ) # 首页“练习”menu practice_title = ("css_selector", "div.title.ng-binding") # “练习”title practice_list = ( "css_selector", "ion-item.item-remove-animate.item-text-wrap.item.item-complex" ) # “练习”试卷 practice_state = ("css_selector", "span.tag.ng-binding.tag-orange.tag-right" ) # “未练习”标签 try: self.assertIs(self.base_page.displayed(practice_module), True, "首页异常!") self.log.debug("从首页进入练习模块") self.base_page.click_elem_tag(practice_module) self.base_page.screen_shot() self.assertIs(self.base_page.displayed(practice_title), True, "进入练习模块异常!") if self.base_page.displayed(practice_list): if self.base_page.displayed(practice_state): self.log.debug("进入练习试卷") self.base_page.click_elem_tag(practice_state) else: self.log.debug("暂无未练习的试卷,进入已完成试卷中重新练习!") practice_count = len( self.base_page.find_elements(practice_list)) i = random.randint(0, practice_count - 1) self.base_page.click_elem_tag(practice_list, tag=i, roll=True) return True else: self.log.debug("暂无内容!") return False except Exception as e: self.log.error(e) raise Exception("出现异常!") def answer(self): """答题""" title = ("css_selector", "div.title.ng-binding") # “练习”title exam_name = ("css_selector", "h2.exam-title.font-large.text-center.ng-binding") # 试卷名称 start_answer_button = ("xpath", "//a[contains(text(), '开始练习')]") continue_answer_button = ("xpath", "//a[contains(text(), '继续练习')]") re_answer_button = ("xpath", "//a[contains(text(), '重新练习')]") count_num_elem = ("css_selector", "span.item-num.ng-binding" ) # 题号和试题数量 option_elem = ("css_selector", "li.option-list-li") # 选项 next_elem = ("css_selector", "div.col.col-33.col-center.text-right" ) # 下一题 submit_elem = ("css_selector", "a.button.button-clear.button-calm" ) # 提交 result_element = ("css_selector", "div.title.ng-binding") # 练习结果title answer_sheet_elem = ("css_selector", "button.button.button-calm.exam-button" ) # 答题页面的右上角的“答题卡” submit_button = ("css_selector", "button.button.button-block.button-calm") # 提交按钮 # 登录成功提示,有未答题提示,自动提交试卷提示 popup_title = ("css_selector", "h3.popup-title.ng-binding") # 提示框title try: self.base_page.screen_shot() self.assertIs(self.base_page.displayed(title), True, "进入试卷异常!") self.base_page.get_text(exam_name, text="试卷名称:") if self.base_page.displayed(re_answer_button): self.log.debug("点击[重新练习]按钮") self.base_page.click_elem_tag(re_answer_button) elif self.base_page.displayed(start_answer_button): self.log.debug("点击[开始练习]按钮") self.base_page.click_elem_tag(start_answer_button) elif self.base_page.displayed(continue_answer_button): self.log.debug("点击[继续练习]按钮") self.base_page.click_elem_tag(continue_answer_button) # 用答题页面的“答题卡”元素断言 self.assertIs(self.base_page.displayed(answer_sheet_elem), True, "进入答题页异常!") # 获取题目总数 num = self.base_page.get_text(count_num_elem) count_num_str = num.split('/')[1:][0] # 考题总数 current_num_str = num.split('/')[:1][0] # 当前题号 self.log.debug("开始答题") count_num = int(count_num_str) current_num = int(current_num_str) while current_num <= count_num: self.log.debug("第%s题" % current_num) count_option = len( self.base_page.find_elements(option_elem)) # 选项数量 i = random.randint(0, count_option - 1) self.base_page.click_elem_tag(option_elem, tag=i) self.base_page.screen_shot() # 点击下一题按钮 if current_num < count_num: self.base_page.click_elem_tag(next_elem) current_num += 1 else: break if current_num >= 6: self.log.debug("中断答题") self.base_page.screen_shot() self.base_page.click_elem_tag(answer_sheet_elem) self.base_page.screen_shot() if not self.base_page.displayed(popup_title): self.log.debug("进入答题卡页面提交试卷!") # 点击提交按钮 self.base_page.click_elem_tag(submit_button, roll=True) self.base_page.screen_shot() break else: break # 手动提交试卷 if current_num == count_num and not self.base_page.displayed( popup_title): self.log.debug("答题完毕后点击'提交'按钮,手动提交试卷!") self.base_page.click_elem_tag(submit_elem, tag=1) self.base_page.screen_shot() # 自动提交试卷(中断答题/超过离开次数) elif self.base_page.displayed(popup_title): self.base_page.popup() # 用成绩单页面的“成绩单”元素断言 self.assertIs(self.base_page.displayed(result_element), True, "考试结果提交异常!") self.log.debug("练习完成!") except Exception as e: self.log.error(e) raise Exception("出现异常!") def tearDown(self): self.base_page.case_end()
class AppExams(unittest.TestCase): """考试""" def setUp(self): try: self.common = Common() self.base_page = BasePage() self.log = MyLog().get_log().logger except Exception as e: self.log.error(e) raise Exception("出现异常!") def test_app_exams(self): """app考试""" try: # 用例开始,输入负责人姓名,必须 self.base_page.case_start("李彬") if not self.base_page.home_page_to("考试"): # 判断待办事项中是否有考试 if AppExams.enter(self) is False: # 首页进入考试 # 如果没有可以考试的试卷就结束用例 self.base_page.case_pass() return # 开始答题 AppExams.answer(self) # 用例成功,必须 self.base_page.case_pass() except Exception as e: self.log.error(e) # 用例失败,必须 self.base_page.case_failed() raise Exception("出现异常!") finally: self.base_page.back_to() def answer(self): """答题""" exam_name = ("css_selector", "h2.exam-title.font-large.text-center.ng-binding") # 试卷名称 start_answer_button = ("xpath", "//a[contains(text(), '开始答题')]") re_answer_button = ("xpath", "//a[contains(text(), '重新答题')]") exam_time_elem = ("css_selector", "span.countdown.ng-binding") # 考试时长 count_num_elem = ("css_selector", "span.item-num.ng-binding" ) # 题号和试题数量 option_elem = ("css_selector", "li.option-list-li") # 选项 next_elem = ("css_selector", "div.col.col-33.col-center.text-right" ) # 下一题 submit_elem = ("css_selector", "a.button.button-clear.button-calm" ) # 提交 result_element = ("css_selector", "div.title.ng-binding") # 成绩单title answer_sheet_elem = ("css_selector", "button.button.button-calm.exam-button" ) # 答题页面的右上角的“答题卡” submit_button = ("css_selector", "button.button.button-block.button-calm") # 提交按钮 # 登录成功提示,有未答题提示,自动提交试卷提示 popup_title = ("css_selector", "h3.popup-title.ng-binding") # 提示框title try: self.base_page.screen_shot() self.assertIs(self.base_page.displayed(exam_name), True, "进入试卷异常!") self.base_page.get_text(exam_name, text="试卷名称:") if self.base_page.displayed(start_answer_button): self.log.debug("点击开始答题按钮") self.base_page.click_elem_tag(start_answer_button) elif self.base_page.displayed(re_answer_button): self.log.debug("点击重新答题按钮") self.base_page.click_elem_tag(re_answer_button) # 提示框 self.base_page.popup() # 用答题页面的“答题卡”元素断言 self.assertIs(self.base_page.displayed(answer_sheet_elem), True, "进入答题页异常!") # 获取考试时常 self.base_page.get_text(exam_time_elem, text="剩余考试时长:") # 获取题目总数 num = self.base_page.get_text(count_num_elem) count_num_str = num.split('/')[1:][0] # 考题总数 current_num_str = num.split('/')[:1][0] # 当前题号 self.log.debug("开始答题") count_num = int(count_num_str) current_num = int(current_num_str) # 自动提交试卷的提示框和总题数 try: while not self.base_page.displayed( popup_title) and current_num <= count_num: self.log.debug("第%s题" % current_num) count_option = len( self.base_page.find_elements(option_elem)) # 选项数量 i = random.randint(0, count_option - 1) self.base_page.click_elem_tag(option_elem, tag=i) self.base_page.screen_shot() # 点击下一题按钮 if current_num < count_num: self.base_page.click_elem_tag(next_elem) current_num += 1 if current_num >= 6: self.log.debug("中断答题") self.base_page.screen_shot() self.base_page.click_elem_tag(answer_sheet_elem) self.base_page.screen_shot() if not self.base_page.displayed(popup_title): self.log.debug("进入答题卡页面提交试卷!") # 点击提交按钮 self.base_page.click_elem_tag(submit_button, roll=True) self.base_page.screen_shot() break else: break else: self.log.debug("置于后台") self.base_page.app_driver.background_app(0) # 手动提交试卷 if current_num == count_num and not self.base_page.displayed( popup_title): self.log.debug("答题完毕后点击'提交'按钮,手动提交试卷!") self.base_page.click_elem_tag(submit_elem, tag=1) self.base_page.screen_shot() self.assertIs(self.base_page.displayed(submit_button), True, "答题完毕后提交失败,未进入答题卡页面!") # 答题卡页面点击提交按钮 self.base_page.click_elem_tag(submit_button, roll=True) # 答题卡页点击提交按钮后的提示框 self.assertIs(self.base_page.displayed(popup_title), True, "答题卡页面点击提交按钮后未出现提示框!") # 提示框 self.base_page.popup() # 自动提交试卷(时间到了/超过离开次数) elif self.base_page.displayed(popup_title): self.base_page.popup() # 用成绩单页面的“成绩单”元素断言 self.assertIs(self.base_page.displayed(result_element), True, "考试结果提交异常!") self.log.debug("提交成功!") except Exception as e: # 过程中出现弹框(自动提交弹框) if self.base_page.displayed(popup_title): self.base_page.popup() # 用成绩单页面的“成绩单”元素断言 self.assertIs(self.base_page.displayed(result_element), True, "考试结果提交异常!") self.log.debug("提交成功!") else: self.log.error(e) raise Exception("出现异常!") except Exception as e: self.log.error(e) raise Exception("出现异常!") def enter(self): """进入试卷""" try: exams_module = ("css_selector", "img[src='img/exam.png']" ) # 首页“考试”menu exam_state = ("css_selector", "span.tag.ng-binding.tag-warning.tag-right" ) # “未考试”标签 prompt_nothing = ("css_selector", "div.nw-nothing-text" ) # “暂无内容”提示 exams_type = ("css_selector", "div.exam-tabs.text-center.font-small.ng-binding" ) # “未考试/已交卷”menu self.assertIs(self.base_page.displayed(exams_module), True, "首页异常!") self.log.debug("从首页进入考试模块") self.base_page.click_elem_tag(exams_module) self.base_page.screen_shot() self.assertIs(self.base_page.displayed(exams_type), True, "进入考试模块异常!") if self.base_page.displayed(exam_state): self.log.debug("进入试卷") self.base_page.click_elem_tag(exam_state) return True else: if self.base_page.displayed(prompt_nothing): self.log.debug("未考试中暂无内容!") else: self.log.debug("暂无未考试的试卷!") self.log.debug("进入已交卷中查看") self.base_page.click_elem_tag(exams_type, tag=1) self.base_page.screen_shot() if self.base_page.displayed(prompt_nothing): self.log.debug("已交卷中暂无内容!") return False else: return AppExams.find_the_paper(self) except Exception as e: self.log.error(e) raise Exception("出现异常!") def find_the_paper(self): """已交卷中查找可以重新答题的试卷""" exams_type = ("css_selector", "div.exam-tabs.text-center.font-small.ng-binding" ) # 已交卷menu exam_state = ("css_selector", "span.tag.ng-binding.tag-success.tag-right") # "已考试"标签 exam_name = ("css_selector", "h2.exam-title.font-large.text-center.ng-binding") # 试卷名称 re_answer_button = ("xpath", "//a[contains(text(), '重新答题')]") # 重新答题按钮 try: count = len(self.base_page.find_elements(exam_state)) # "已考试"标签总数 # self.log.debug("[已考试]标签数量:%s" % str(count)) i = 0 j = 1 n = 0 if count != 0: while j <= count: self.base_page.click_elem_tag(exams_type, tag=1) self.log.debug("进入试卷") self.base_page.click_elem_tag(exam_state, tag=i, roll=True) if self.base_page.displayed(re_answer_button): n += 1 self.log.debug("该试卷可以重新答题") return True self.base_page.get_text("试卷名称:", exam_name) self.base_page.screen_shot() self.log.debug("该试卷不能重新答题,继续查看下一个试卷") self.base_page.back() i += 2 j += 1 if j % 10 == 0: self.log.debug("向上滑动屏幕!") self.base_page.swipe_up() self.base_page.screen_shot() elements = self.base_page.find_elements(exam_state) count = len(elements) if n == 0: self.log.debug("已交卷中没有可以重新答题的试卷!") return False except Exception as e: self.log.error(e) raise Exception("出现异常!") def tearDown(self): # 用例结束,必须 self.base_page.case_end()
class AppSurvey(unittest.TestCase): """问卷调查""" def setUp(self): try: self.base_page = BasePage() self.log = MyLog().get_log().logger except Exception as e: self.log.error(e) raise Exception("出现异常!") def test_app_survey(self): """app问卷调查""" try: # 用例开始,输入负责人姓名,必须 self.base_page.case_start("李彬") if AppSurvey.enter(self) is False: # 首页进入问卷调查 # 如果没有有效的问卷调查就结束用例 self.base_page.case_pass() return # 开始答题 AppSurvey.answer(self) # 用例成功,必须 self.base_page.case_pass() except Exception as e: self.log.error(e) # 用例失败,必须 self.base_page.case_failed() raise Exception("出现异常!") finally: self.base_page.back_to() def enter(self): """进入问卷调查""" survey_module = ("css_selector", "img[src='img/survey.png']" ) # 首页“问卷调查”menu survey_title = ("css_selector", "div.title.ng-binding") # “问卷调查”title survey_list = ( "css_selector", "ion-item.item-remove-animate.item-text-wrap.item.item-complex" ) # “问卷调查”问卷 survey_state = ("css_selector", "span.tag.ng-binding.tag-warning.tag-right") # “未填写”标签 try: self.assertIs(self.base_page.displayed(survey_module), True, "首页异常!") self.log.debug("从首页进入问卷调查模块") self.base_page.click_elem_tag(survey_module) self.base_page.screen_shot() self.assertIs(self.base_page.displayed(survey_title), True, "进入问卷调查模块异常!") if self.base_page.displayed(survey_list): if self.base_page.displayed(survey_state): self.log.debug("进入问卷") self.base_page.click_elem_tag(survey_state) return True else: self.log.debug("暂无未填写的问卷!") return False else: self.log.debug("暂无内容!") return False except Exception as e: self.log.error(e) raise Exception("出现异常!") def answer(self): """答题""" survey_name = ("css_selector", "h2.exam-title.font-large.text-center.ng-binding" ) # 问卷名称 start_answer_button = ("css_selector", "a.button.button-block.button-calm") # “开始填写”按钮 problem_name = ("css_selector", "div.paper-title.font-big.ng-binding" ) # 问题名称 submit_button = ( "css_selector", "button.button.button-full.button-calm.lesson-footer-button" ) # 提交按钮 text = u"大幅度dddz12345,,kds。,.,m@!~" * 5 try: self.base_page.screen_shot() self.assertIs(self.base_page.displayed(survey_name), True, "进入问卷异常!") self.base_page.get_text(survey_name, text="问卷名称:") if self.base_page.displayed(start_answer_button): self.log.debug("点击[开始填写]按钮") self.base_page.click_elem_tag(start_answer_button) self.base_page.screen_shot() # 用答题页面的“提交”按钮断言 self.assertIs(self.base_page.displayed(submit_button), True, "进入答题页异常!") # 问题数量 problem_count = len(self.base_page.find_elements(problem_name)) self.log.debug("问题数量:%s" % problem_count) i = 1 while i <= problem_count: problem_type_elem = ( "xpath", "//div[@class='survey-content'][%s]//span[2]" % i ) # 问题类型 problem_type = self.base_page.get_text(problem_type_elem, text="第%s题:" % i) if "单选题" in problem_type or "评分题" in problem_type: radio = ("xpath", "//div[@class='survey-content'][%s]//label" % i ) # 单选题和评分题选项 radio_count = len(self.base_page.find_elements(radio)) a = random.randint(0, radio_count - 1) self.base_page.click_elem_tag(radio, tag=a) elif "多选题" in problem_type: multiple = ( "xpath", "//div[@class='survey-content'][%s]//div[@ng-repeat='option in question.questionOptionList']" % i) # 多选题选项 multiple_count = len( self.base_page.find_elements(multiple)) b = random.randint(0, multiple_count - 1) self.base_page.click_elem_tag(multiple, tag=b) c = b while c == b: c = random.randint(0, multiple_count - 1) self.base_page.click_elem_tag(multiple, tag=c) elif "简答题" in problem_type: textarea = ( "xpath", "//div[@class='survey-content'][%s]//textarea[@class='no-resize ng-pristine ng-untouched ng-invalid ng-invalid-required']" % i) # 问答题输入框 self.base_page.input_tag(textarea, text=text) i += 1 self.base_page.screen_shot() self.base_page.click_elem_tag(submit_button) self.base_page.screen_shot() self.base_page.popup() except Exception as e: self.log.error(e) raise Exception("出现异常!") def tearDown(self): self.base_page.case_end() self.base_page.quit()
class BasePage(object): """页面元素基本操作,page module""" web_case_num = 0 # web用例编号 web_driver = None # 用于标记web用例 app_driver = None # 用于标记APP用例 current_driver = "" # 用于标记当前是web端还是APP端的用例 _instance_lock = threading.Lock() # 设置单例锁 def __new__(cls, *args, **kwargs): """单例模式(支持多线程)""" if not hasattr(cls, "_instance"): with cls._instance_lock: if not hasattr(cls, "_instance"): cls._instance = object.__new__(cls) return cls._instance def __init__(self): try: self.config = ReadConfig() self.log = MyLog().get_log().logger self.common = Common() except Exception as e: self.log.error(e) raise Exception("出现异常!") def open_app(self): """打开app""" try: # 读取app参数 system = self.config.get_app_param("system") # 系统类型 udid = self.config.get_app_param("udid") # 手机udid version = self.config.get_app_param("version") # 手机系统版本 app_package = self.config.get_app_param("app_package") # 待测app包名 app_activity = self.config.get_app_param( "app_activity") # 待测app的activity名 # app_address = self.config.get_app_param("app_address") # app安装包路径 # android_process = self.config.get_app_param("androidProcess") # 小程序线程名 desired_caps = { 'platformName': system, 'platformVersion': version, 'automationName': 'appium', 'deviceName': 'udid', 'udid': udid, 'newCommandTimeout': 60, 'appActivity': app_activity, 'appPackage': app_package, 'unicodeKeyboard': True, 'resetKeyboard': True, 'setWebContentsDebuggingEnabled': True, 'recreateChromeDriverSessions': True, 'noReset': True, # 'app': app_address, # 'chromeOptions': {'androidProcess': android_process} } # 操作APP端元素的webdriver实例 self.app_driver = appium.webdriver.Remote( 'http://127.0.0.1:4723/wd/hub', desired_caps) self.current_driver = "app_driver" # 标记为APP端用例 time.sleep(10) self.switch_context() # H5时需要切换context except Exception as e: self.log.error(e) raise Exception("打开app时异常!") def open_browser(self, browser="chrome"): """打开浏览器""" try: if browser == "chrome" or browser == "Chrome": driver = selenium.webdriver.Chrome() elif browser == "firefox" or browser == "Firefox" or browser == "FireFox" or browser == "ff": driver = selenium.webdriver.Firefox() elif browser == "ie" or browser == "IE" or browser == "internet explorer": driver = selenium.webdriver.Ie() else: raise Exception( self.log.error("没有找到浏览器 %s, 你可以输入'Chrome,Firefox or Ie'" % browser)) self.web_driver = driver # 操作web端元素的webdriver实例 self.current_driver = "web_driver" # 标记为web端用例 self.web_driver.maximize_window() except Exception as e: self.log.error(e) raise Exception("打开%s浏览器时异常!" % browser) def get(self, url, element): """打开web端URL, element用于等待页面加载完成""" try: if url != "" or url != " ": self.web_driver.get(url) self.wait_elem(element) # 等待元素出现 else: raise Exception("URL地址错误!") except Exception as e: self.log.error(e) raise Exception("打开网址时异常!") def refresh(self): """刷新页面""" if self.current_driver == "web_driver": self.web_driver.refresh() else: self.swipe_down() def back(self): """返回上一页""" try: if self.current_driver == "web_driver": self.web_driver.back() else: self.app_driver.keyevent(4) time.sleep(0.5) except Exception as e: self.log.error(e) raise Exception("返回时异常!") def quit(self): """退出程序""" try: if self.current_driver == "web_driver": self.web_driver.quit() else: # H5时用 self.app_driver.switch_to.context("NATIVE_APP") # input_name = self.app_driver.active_ime_engine # self.log.debug("当前输入法:%s" % input_name) input_list = self.app_driver.available_ime_engines # self.log.debug("现有输入法:%s" % input_list) self.app_driver.activate_ime_engine(input_list[0]) # input_name = self.app_driver.active_ime_engine # self.log.debug("更改当前输入法为:%s" % input_name) self.app_driver.quit() except Exception as e: self.log.error(e) raise Exception("退出程序时异常!") def click_elem_tag(self, elements, tag=0, roll=False, t=1): """根据元素下标点击元素""" for i in range(1, 6, +1): # 操作失败后重试 try: if roll: # 是否需要滚动页面 self.location(elements, tag) # app端页面上下微调 elem = self.find_elements_tag(elements, tag) elem.click() time.sleep(t) break except Exception as e: time.sleep(1) self.log.debug("等待 %s s %s" % (i, e)) else: self.log.error("%s元素未出现!" % str(elements)) raise Exception("点击元素时异常!") def input_tag(self, elements, text, tag=0, roll=False): """输入文本""" for i in range(1, 6, +1): # 操作失败后重试 try: if roll: # 是否需要滚动页面 self.location(elements, tag) # app端页面上下微调 elem = self.find_elements_tag(elements, tag) elem.clear() elem.send_keys(text) break except Exception as e: time.sleep(1) self.log.debug("等待 %s s %s" % (i, e)) else: self.log.error("%s元素未出现!" % str(elements)) raise Exception("输入文本时异常!") def get_text(self, *args, text="", tag=0): """获取文本内容,可多条,text为获取内容的标题/属性""" value = "" # 文本内容 for param in args: for i in range(1, 6, +1): # 操作失败后重试 try: elem = self.find_elements_tag(param, tag) value = elem.text # web端获取文本内容 if value == "": value = elem.get_attribute("name") # app获取文本内容 if value != "": self.log.debug("%s%s" % (text, value)) break except Exception as e: time.sleep(1) self.log.debug("等待 %s s %s" % (i, e)) else: self.log.error("%s元素未出现!" % str(param)) raise Exception("获取元素文本时异常!") return value def switch_context(self, tag=1): """切换环境,tag=0时为android原生context""" try: contexts = self.app_driver.contexts # 获取当前所有context self.log.debug("contexts:%s" % contexts) if len(contexts) != 1: # 需要切换context self.app_driver.switch_to.context(contexts[tag]) # 切换context self.log.debug("切换context") context = self.app_driver.current_context # 获取当前context self.log.debug("current_context: %s" % context) except Exception as e: self.log.error(e) raise Exception("切换context时异常!") def switch_handle(self, element): """切换句柄""" try: handles = self.app_driver.window_handles if len(handles) != 1: # 需要切换句柄 self.log.debug("handles:%s" % handles) self.app_driver.switch_to.window(handles[-1]) if self.displayed(element): # 判断该句柄下是否有该元素 self.log.debug("切换handle") return self.displayed(element) except Exception as e: self.log.error(e) raise Exception("切换handle时异常!") def home_page_to(self, module): """首页待办事项进入功能模块""" module_elem = ("xpath", "//span[contains(text(), '%s')]" % module ) # 待办事项中的模块标签 result = False try: if self.displayed(module_elem): self.log.debug("从首页的待办事项进入%s" % module) self.screen_shot() self.click_elem_tag(module_elem) self.screen_shot() result = True return result except Exception as e: self.log.error(e) raise Exception("从首页的待办事项进入%s时异常!" % module) def back_to(self, *args): """返回(首页)或指定元素页面(须该页独有元素)""" try: home_menu = ("css_selector", "span.tab-title.ng-binding" ) # 首页底部menu username_input = ("css_selector", "input[type='text']" ) # 用户名输入框定位信息 menu_elem = () if args != (): menu_elem = args[0] self.log.debug("返回") i = 1 while i <= 5: # 最多返回5级页面 if self.displayed(username_input): # 判断是否处于登录页 raise Exception("登录失败!") self.back() self.screen_shot() if args == (): # 返回首页 if self.switch_handle(home_menu): self.click_elem_tag(home_menu) break elif args != (): # 返回指定元素页面 if self.switch_handle(menu_elem): break self.log.debug("返回:%s" % i) i += 1 else: raise Exception("返回时异常!") except Exception as e: self.log.error(e) raise Exception("返回时异常!") def popup(self): """获取弹框信息,点击按钮""" popup_title = ("css_selector", "h3.popup-title.ng-binding") # 提示框title popup_info = ("css_selector", "div.popup-body") # 提示信息 popup_ok_button = ("css_selector", "button.button.ng-binding.button-calm") # 确定按钮 try: n = len(self.find_elements(popup_title)) # self.log.debug("弹框数量:%s" % n) self.get_text(popup_title, popup_info, tag=n - 1) self.screen_shot() self.click_elem_tag(popup_ok_button, tag=n - 1) self.screen_shot() except Exception as e: self.log.error(e) raise Exception("操作弹框时异常!") def roll(self, elements): """web端页面下滑""" elem = self.find_elements_tag(elements) selenium.webdriver.ActionChains( self.web_driver).move_to_element(elem).perform() time.sleep(1) self.log.debug("滚动页面!") def screen_shot(self): """截图""" try: current_time = str(self.common.get_now_time()) # 获取当前时间 func_name = sys._getframe().f_back.f_code.co_name # 获取调用函数名 line_number = sys._getframe().f_back.f_lineno # 获取调用行号 path = self.common.get_result_path( case_name, "%s %s %s.png" % (current_time, func_name, line_number)) if self.current_driver == "web_driver": # web端直接截图 self.web_driver.get_screenshot_as_file(path) else: # 移动端截图 contexts = self.app_driver.contexts # 获取所有的context current_context = self.app_driver.current_context # 获取当前的context if current_context == contexts[0]: # 如果是android原生环境直接截图 self.app_driver.get_screenshot_as_file(path) else: # 如果是H5页面先切换到android原生环境再截图 self.app_driver.switch_to.context(contexts[0]) self.app_driver.get_screenshot_as_file(path) self.app_driver.switch_to.context( contexts[1]) # 截完图后回到原来的context except Exception as e: self.log.error(e) raise Exception("截图保存时异常!") def case_start(self, principal, api_case_name="", api_case_num=0): """用例开始,参数为负责人姓名,api测试名,api测试编号""" try: global case_name # 获取调用函数名作为截图文件夹名 if api_case_name == "" and api_case_num == 0: case_name = sys._getframe().f_back.f_code.co_name self.web_case_num += 1 self.log.debug("web用例%s:%s,负责人:%s" % (self.web_case_num, case_name, principal)) else: case_name = api_case_name # 将全局变量case_name重新赋值 self.log.debug("api用例%s:%s,负责人:%s" % (api_case_num, api_case_name, principal)) except Exception as e: self.log.error(e) raise Exception("用例开始时异常!") def case_end(self): """用例结束""" self.log.debug("*" * 100 + "\n") # "*"号不可改,用于提取用例失败的日志 def case_pass(self): """用例通过""" self.log.debug("=" * 10 + "%s: pass!" % case_name + "=" * 10) def case_failed(self): """用例失败""" self.log.debug("=" * 10 + "%s: failed!" % case_name + "=" * 10) # "failed!"不可改,用于标记用例失败的日志 def find_elements_tag(self, elements, tag=0): """查找元素(一个具体的元素点击和输入时定位)""" try: key = elements[0] # 定位方式 value = elements[1] # 值 if self.current_driver == "web_driver": # web定位 if key == "css_selector": elem = self.web_driver.find_elements_by_css_selector( value)[tag] elif key == "xpath": elem = self.web_driver.find_elements_by_xpath(value)[tag] elif key == "id": elem = self.web_driver.find_elements_by_id(value)[tag] elif key == "name": elem = self.web_driver.find_elements_by_name(value)[tag] elif key == "class": elem = self.web_driver.find_elements_by_class_name( value)[tag] elif key == "link_text": elem = self.web_driver.find_elements_by_link_text(value) elif key == "partial_link_text": elem = self.web_driver.find_elements_by_partial_link_text( value) elif key == "tag_name": elem = self.web_driver.find_elements_by_tag_name( value)[tag] else: self.log.error("定位类型书写错误:%s" % str(elements)) raise Exception return elem else: # app定位 if key == "css_selector": elem = self.app_driver.find_elements_by_css_selector( value)[tag] elif key == "xpath": elem = self.app_driver.find_elements_by_xpath(value)[tag] elif key == "accessibility_id": elem = self.app_driver.find_elements_by_accessibility_id( value)[tag] elif key == "id": elem = self.app_driver.find_elements_by_id(value)[tag] elif key == "name": elem = self.app_driver.find_elements_by_name(value)[tag] elif key == "class": elem = self.app_driver.find_elements_by_class_name( value)[tag] elif key == "link_text": elem = self.app_driver.find_elements_by_link_text(value) elif key == "partial_link_text": elem = self.app_driver.find_elements_by_partial_link_text( value) elif key == "tag_name": elem = self.app_driver.find_elements_by_tag_name( value)[tag] else: self.log.error("定位类型书写错误:%s" % str(elements)) raise Exception return elem except Exception as e: # self.log.debug("元素不存在:%s,%s" % (str(elements), e)) raise Exception def find_elements(self, elements): """查找元素集合""" try: key = elements[0] value = elements[1] if self.current_driver == "web_driver": # web查找元素 if key == "css_selector": elem = self.web_driver.find_elements_by_css_selector(value) elif key == "xpath": elem = self.web_driver.find_elements_by_xpath(value) elif key == "id": elem = self.web_driver.find_elements_by_id(value) elif key == "name": elem = self.web_driver.find_elements_by_name(value) elif key == "class_name": elem = self.web_driver.find_elements_by_class_name(value) elif key == "link_text": elem = self.web_driver.find_elements_by_link_text(value) elif key == "partial_link_text": elem = self.web_driver.find_elements_by_partial_link_text( value) elif key == "tag_name": elem = self.web_driver.find_element_by_tag_name(value) else: self.log.error("函数类型书写错误:%s" % str(elements)) raise Exception return elem else: # APP查找元素 if key == "css_selector": elem = self.app_driver.find_elements_by_css_selector(value) elif key == "xpath": elem = self.app_driver.find_elements_by_xpath(value) elif key == "accessibility_id": elem = self.app_driver.find_elements_by_accessibility_id( value) elif key == "id": elem = self.app_driver.find_elements_by_id(value) elif key == "name": elem = self.app_driver.find_elements_by_name(value) elif key == "class": elem = self.app_driver.find_elements_by_class_name(value) elif key == "link_text": elem = self.app_driver.find_elements_by_link_text(value) elif key == "partial_link_text": elem = self.app_driver.find_elements_by_partial_link_text( value) elif key == "tag_name": elem = self.app_driver.find_elements_by_tag_name(value) else: self.log.error("函数类型书写错误:%s" % str(elements)) raise Exception return elem except Exception as e: # self.log.debug("元素不存在:%s,%s" % (str(elements), e)) raise Exception def wait_elem(self, element): """等待元素出现""" key = element[0] value = element[1] locator = None try: if key == "css_selector": locator = (By.CSS_SELECTOR, value) elif key == "xpath": locator = (By.XPATH, value) elif key == "id": locator = (By.ID, value) elif key == "name": locator = (By.NAME, value) elif key == "class": locator = (By.CLASS_NAME, value) elif key == "link_text": locator = (By.LINK_TEXT, value) elif key == "partial_link_text": locator = (By.PARTIAL_LINK_TEXT, value) elif key == "tag_name": locator = (By.TAG_NAME, value) if self.current_driver == "web_driver": WebDriverWait(self.web_driver, 20, 0.5).until( ec.presence_of_element_located(locator), "%s元素未出现!" % str(element)) else: WebDriverWait(self.app_driver, 20, 0.5).until( ec.presence_of_element_located(locator), "%s元素未出现!" % str(element)) except Exception as e: self.log.error(e) raise Exception("等待元素出现时异常!") # def judgment(self, elements, tag=0): # """判断元素是否存在""" # for i in range(1, 6, +1): # time.sleep(1) # try: # self.find_elements_tag(elements, tag) # return True # except Exception as e: # return False # 方式二(速度较慢): # key = elements[0] # value = elements[1] # locator = None # # if key == "css_selector": # locator = (By.CSS_SELECTOR, value) # elif key == "xpath": # locator = (By.XPATH, value) # elif key == "id": # locator = (By.ID, value) # elif key == "name": # locator = (By.NAME, value) # elif key == "class": # locator = (By.CLASS_NAME, value) # elif key == "link_text": # locator = (By.LINK_TEXT, value) # elif key == "partial_link_text": # locator = (By.PARTIAL_LINK_TEXT, value) # elif key == "tag_name": # locator = (By.TAG_NAME, value) # # if current_driver == "web_driver": # try: # WebDriverWait(self.web_driver, 20, 0.5).until(lambda x: x.find_element(*locator)) # return True # except: # return False # else: # try: # WebDriverWait(self.app_driver, 20, 0.5).until(lambda x: x.find_element(*locator)) # return True # except: # return False def displayed(self, elements, tag=0): """判断元素是否可见""" try: elem = self.find_elements_tag(elements, tag) return elem.is_displayed() # 元素可见为True,隐藏为False except Exception as e: return False # 没有找到元素 def swipe_up(self, x=0.5, y1=0.85, y2=0.15, t=500): """屏幕向上滑动""" try: self.swipe(x, y1, y2, t) self.log.debug("上滑") except Exception as e: self.log.error(e) raise Exception("屏幕向上滑动时异常!") def swipe_down(self, x=0.5, y1=0.15, y2=0.85, t=500): """屏幕向下滑动""" try: self.swipe(x, y1, y2, t) self.log.debug("下滑") except Exception as e: self.log.error(e) raise Exception("屏幕向下滑动时异常!") def swipe(self, x, y1, y2, t): """上下滑动""" try: coordinate_x = self.app_driver.get_window_size()['width'] # 获取屏幕宽度 coordinate_y = self.app_driver.get_window_size()[ 'height'] # 获取屏幕高度 x1 = int(coordinate_x * x) # x坐标 y1 = int(coordinate_y * y1) # 起始y坐标 y2 = int(coordinate_y * y2) # 终点y坐标 self.app_driver.swipe(x1, y1, x1, y2, t) time.sleep(1) except Exception as e: raise Exception(e) def location(self, element, tag=0): """屏幕内容上下微调""" current_context = "" try: elem = self.find_elements_tag( element, tag) # css_selector不能在android环境下定位,所以定位完成后再切换环境 y1 = elem.location["y"] # 获取元素y坐标 # self.log.debug(y1) contexts = self.app_driver.contexts # 获取所有的context current_context = self.app_driver.current_context # 获取当前的context if current_context != contexts[ 0]: # 当前为非android环境时需要切换为APP_context才能进行滑动操作 self.app_driver.switch_to.context(contexts[0]) y2 = self.app_driver.get_window_size()['height'] # 获取屏幕高度 # self.log.debug(y2) while y1 + 200 > y2 or y1 < 100: # 判断是否需要滑动 if y1 + 200 > y2: self.swipe(x=0.02, y1=0.85, y2=0.45, t=500) # 向上滑 self.screen_shot() n = y1 if current_context == contexts[ 1]: # 当前为非H5环境时需要切换为H5环境才能获取元素坐标 self.app_driver.switch_to.context(contexts[1]) y1 = elem.location["y"] # self.log.debug(y1) if current_context != contexts[ 0]: # 当前为非android环境时需要切换为APP_context才能进行滑动操作 self.app_driver.switch_to.context(contexts[0]) if y1 < 100: self.swipe(x=0.02, y1=0.60, y2=0.75, t=500) # 向下滑 self.screen_shot() if n == y1: break except Exception as e: self.log.error(e) raise Exception("位置调整时异常!") finally: self.app_driver.switch_to.context(current_context) # 微调完成后切换为原来的环境
class AppCurriculum(unittest.TestCase): """课程""" def setUp(self): try: self.base_page = BasePage() self.log = MyLog().get_log().logger except Exception as e: self.log.error(e) raise Exception("出现异常!") def test_app_curriculum(self): """app课程""" try: # 用例开始,输入负责人姓名,必须 self.base_page.case_start("李彬") if not self.base_page.home_page_to("课程"): # 判断待办事项中是否有课程 if AppCurriculum.enter(self) is False: # 首页进入课程 # 如果没有课程就结束用例 self.base_page.case_pass() return # 开始学习 AppCurriculum.study(self) # 用例成功,必须 self.base_page.case_pass() except Exception as e: self.log.error(e) # 用例失败,必须 self.base_page.case_failed() raise Exception("出现异常!") finally: self.base_page.back_to() def enter(self): """进入课程""" module = ("css_selector", "img[src='img/lesson.png']") # 首页“课程”menu title = ("css_selector", "div.title.ng-binding") # “课程”title curriculum_list = ( "css_selector", "ion-item.item-remove-animate.item-text-wrap.item.item-complex" ) # “课程”list state = ("css_selector", "span.tag.ng-binding.tag-orange.tag-right" ) # “未学习”标签 try: self.assertIs(self.base_page.displayed(module), True, "首页异常!") self.log.debug("从首页进入课程模块") self.base_page.click_elem_tag(module) self.base_page.screen_shot() self.assertIs(self.base_page.displayed(title), True, "进入课程模块异常!") if self.base_page.displayed(curriculum_list): if self.base_page.displayed(state): self.log.debug("进入课程") self.base_page.click_elem_tag(state, roll=True) else: self.log.debug("暂无未学习的课程,进入已学习的课程中重新学习!") count = len( self.base_page.find_elements(curriculum_list)) # 课程数量 i = random.randint(0, count - 1) self.base_page.click_elem_tag(curriculum_list, tag=i, roll=True) return True else: self.log.debug("暂无内容!") return False except Exception as e: self.log.error(e) raise Exception("出现异常!") def study(self): """学习""" curriculum_name = ("css_selector", "h1.exam-title.font-large.text-center.ng-binding" ) # 课程名称 curriculum_exam = ( "css_selector", "button.button.button-full.button-calm.lesson-footer-button" ) # 随堂考试 curriculum_list = ( "css_selector", "ion-item.item-remove-animate.item-complex.item-text-wrap.item" ) # 课件list # state = ("css_selector", "span.tag.ng-binding.tag-orange.tag-right") # “未学习”标签 courseware_name = ("css_selector", "h2.exam-title.font-large.text-center.ng-binding" ) # 课件名称 start_answer_button = ("xpath", "//a[contains(text(), '开始学习')]") try: self.base_page.screen_shot() self.assertIs(self.base_page.displayed(curriculum_name), True, "进入课程异常!") self.base_page.get_text(curriculum_name, text="课程名称:") # 随堂考试(只能放课件学习的前面,不然会有句柄切换问题) if self.base_page.displayed(curriculum_exam): AppCurriculum.answer(self) self.base_page.back_to(curriculum_name) curriculum_count = len( self.base_page.find_elements(curriculum_list)) if curriculum_count != 0: self.base_page.switch_handle(curriculum_list) i = random.randint(0, curriculum_count - 1) self.base_page.click_elem_tag(curriculum_list, tag=i, roll=True) self.assertIs(self.base_page.displayed(courseware_name), True, "进入课件异常!") self.base_page.get_text(courseware_name, text="进入课件:") self.base_page.screen_shot() if self.base_page.displayed(start_answer_button): self.log.debug("点击开始学习按钮") self.base_page.click_elem_tag(start_answer_button, t=2) self.base_page.screen_shot() self.base_page.back_to(curriculum_name) self.log.debug("学习完成!") else: self.log.debug("暂无内容!") except Exception as e: self.log.error(e) raise Exception("出现异常!") def answer(self): """随堂考试""" curriculum_exam = ( "css_selector", "button.button.button-full.button-calm.lesson-footer-button" ) # 随堂考试 count_num_elem = ("css_selector", "span.item-num.ng-binding" ) # 题号和试题数量 option_elem = ("css_selector", "li.option-list-li") # 选项 next_elem = ("css_selector", "div.col.col-33.col-center.text-right" ) # 下一题 submit_elem = ("css_selector", "a.button.button-clear.button-calm" ) # 提交 result_element = ("css_selector", "div.title.ng-binding") # 练习结果title answer_sheet_elem = ("css_selector", "button.button.button-calm.exam-button" ) # 答题页面的右上角的“答题卡” submit_button = ("css_selector", "button.button.button-block.button-calm") # 提交按钮 # 登录成功提示,有未答题提示,自动提交试卷提示 popup_title = ("css_selector", "h3.popup-title.ng-binding") # 提示框title try: self.log.debug("进入随堂考试") self.base_page.click_elem_tag(curriculum_exam) self.base_page.screen_shot() # 用答题页面的“答题卡”元素断言 self.assertIs(self.base_page.displayed(answer_sheet_elem), True, "进入答题页异常!") # 获取题目总数 num = self.base_page.get_text(count_num_elem) count_num_str = num.split('/')[1:][0] # 考题总数 current_num_str = num.split('/')[:1][0] # 当前题号 self.log.debug("开始答题") count_num = int(count_num_str) current_num = int(current_num_str) while current_num <= count_num: self.log.debug("第%s题" % current_num) count_option = len( self.base_page.find_elements(option_elem)) # 选项数量 i = random.randint(0, count_option - 1) self.base_page.click_elem_tag(option_elem, tag=i) self.base_page.screen_shot() # 点击下一题按钮 if current_num < count_num: self.base_page.click_elem_tag(next_elem) current_num += 1 else: break if current_num >= 6: self.log.debug("中断答题") self.base_page.screen_shot() self.base_page.click_elem_tag(answer_sheet_elem) self.base_page.screen_shot() if not self.base_page.displayed(popup_title): self.log.debug("进入答题卡页面提交试卷!") # 点击提交按钮 self.base_page.click_elem_tag(submit_button, roll=True) self.base_page.screen_shot() break else: break # 手动提交试卷 if current_num == count_num and not self.base_page.displayed( popup_title): self.log.debug("答题完毕后点击'提交'按钮,手动提交试卷!") self.base_page.click_elem_tag(submit_elem, tag=1) self.base_page.screen_shot() # 自动提交试卷(中断答题/超过离开次数) elif self.base_page.displayed(popup_title): self.base_page.popup() # 用成绩单页面的“成绩单”元素断言 self.assertIs(self.base_page.displayed(result_element), True, "考试结果提交异常!") self.log.debug("提交成功!") except Exception as e: self.log.error(e) raise Exception("出现异常!") def tearDown(self): self.base_page.case_end()
class SendEmail(object): """发送测试报告""" _instance_lock = threading.Lock() # 设置单例锁 def __new__(cls, *args, **kwargs): """单例模式(支持多线程)""" if not hasattr(cls, "_instance"): with cls._instance_lock: if not hasattr(cls, "_instance"): cls._instance = object.__new__(cls) return cls._instance def __init__(self): try: self.common = Common() # 实例化一个common调用公用函数 self.log = MyLog().get_log().logger # 实例化一个log打印日志 self.config = ReadConfig() # 实例化一个read_config读取email的配置信息 self.msg = email.mime.multipart.MIMEMultipart( 'alternative') # 实例化一个email发送email self.log_dir = self.common.get_result_path() # 获取存储日志的时间目录 self.principal_name_list = [] # 错误用例负责人list self.zip_path = self.log_dir + ".zip" # 设置存放zip的路径 self.result = False # 发送结果标志 self.num = 0 # 发送失败后重试次数 except Exception as e: self.log.error(e) raise Exception("SendEmail.__init__异常!") def with_zip(self): """附件以zip格式发送邮件""" while self.result is False and self.num < 3: # 发送失败后重试3次 try: # 提取错误用例负责人姓名 file_list = os.listdir(self.log_dir) # 获取时间目录下的文件列表 for file in file_list: file_name = os.path.splitext(file)[0] # 文件名 # 用正则表达式查找文件名为汉字的文件(负责人对应的错误日志文件),正则表达式为:非汉字的字符用""替换掉 if file_name == re.sub("[^\u4e00-\u9fa5]+", "", file_name): self.principal_name_list.append( file_name) # 添加负责人姓名到principal_name_list中 # 创建一个写入的zip对象 with zipfile.ZipFile( self.zip_path, mode='w', compression=zipfile.ZIP_DEFLATED) as zip_obj: for path, folders, files in os.walk(self.log_dir): for file in files: zip_obj.write(os.path.join(path, file)) # 将内容写入zip # 添加附件 part = MIMEApplication(open(self.zip_path, 'rb').read()) # 读取内容 part.add_header('Content-Disposition', 'attachment', filename=('gbk', '', "result.zip")) # 设置附件名 self.msg.attach(part) self.send() # 发送邮件 self.result = True except Exception as e: self.log.error("发送失败 %s" % e) self.num += 1 finally: os.remove(self.zip_path) # 删除zip文件 self.remove_result() # 删除之前的结果文件夹 def with_file(self): """附件以单个文件形式发送邮件""" while self.result is False and self.num < 3: # 发送失败后重试3次 try: file_list = os.listdir(self.log_dir) # 获取时间目录下的文件列表 for file in file_list: file_name = os.path.splitext(file)[0] # 文件名 file_type = os.path.splitext(file)[1] # 文件类型 # 用正则表达式查找文件名为汉字的文件(负责人对应的错误日志文件),正则表达式为:非汉字的字符用""替换掉 if file_name == re.sub("[^\u4e00-\u9fa5]+", "", file_name): self.principal_name_list.append( file_name) # 添加负责人姓名到错误负责人list中 current_file = os.path.join(self.log_dir, file) # 拼接当前的日志路径 part = MIMEApplication( open(current_file, 'rb').read()) # 读取当前的日志 part.add_header('Content-Disposition', 'attachment', filename=('gbk', '', file)) # 设置附件名 self.msg.attach(part) elif file_type == ".html": # 查找html文件 current_file = os.path.join(self.log_dir, file) # 拼接当前的日志路径 part = MIMEApplication( open(current_file, 'rb').read()) # 读取当前的日志 part.add_header('Content-Disposition', 'attachment', filename=('gbk', '', file)) # 设置附件名 self.msg.attach(part) elif "error" in file_name: # 查找错误日志文件 current_file = os.path.join(self.log_dir, file) # 拼接当前的日志路径 part = MIMEApplication( open(current_file, 'rb').read()) # 读取当前的日志 part.add_header('Content-Disposition', 'attachment', filename=('gbk', '', file)) # 设置附件名 self.msg.attach(part) self.send() # 发送邮件 self.result = True except Exception as e: self.log.error("发送失败 %s" % e) self.num += 1 finally: self.remove_result() # 删除之前的结果文件夹 def send(self): """发送邮件""" try: # 从配置文件中读取发件人信息 sender_name = "" # 发件人 sender_email = "" # 发件箱 sender_dict = json.loads(self.config.get_email("sender")) for key, value in sender_dict.items(): sender_name = key # 发件人 sender_email = value # 发件箱 # 从配置文件中读取收件人信息 # receivers内容为字典时使用(receivers = {"蓝梦":"*****@*****.**", "孟冰":"*****@*****.**") receivers_dict = json.loads(self.config.get_email("receivers")) name_list = [] # 收件人list receivers = [] # 收件箱list for key, value in receivers_dict.items(): if key in self.principal_name_list: name_list.append(key) receivers.append(value) # 邮件信息 name_list_str = ",".join(name_list) # 收件人姓名,将list转换为str mail_host = self.config.get_email("email_host") # 设置邮箱服务器域名 mail_port = self.config.get_email("email_port") # 设置邮箱服务器接口 mail_user = self.config.get_email("email_user") # 发件人用户名 mail_pass = self.config.get_email("email_pass") # 发件人口令 subject = self.config.get_email("subject") # 主题 content = self.config.get_email("content") # 正文 if len(name_list_str) == 0: self.log.debug("所有用例都正常通过!") else: self.log.debug("发件人:%s" % sender_name) self.log.debug("收件人:%s" % name_list_str) txt = email.mime.text.MIMEText(content, 'plain', 'utf-8') self.msg.attach(txt) self.msg['Subject'] = Header(subject, 'utf-8') self.msg['From'] = Header(sender_name, 'utf-8') self.msg['To'] = Header("%s" % name_list_str, 'utf-8') # 调用邮箱服务器 smt_obj = smtplib.SMTP_SSL(mail_host, mail_port) # 登录邮箱 smt_obj.login(mail_user, mail_pass) # 发送邮件 smt_obj.sendmail(sender_email, receivers, self.msg.as_string()) # 关闭邮箱 smt_obj.quit() self.log.debug("发送成功!") except Exception as e: self.log.error(e) raise Exception("发送email时异常!") def remove_result(self): """发送报告后删除其他的文件夹""" try: result_path = os.path.dirname(self.log_dir) # 获取result目录路径 result_list = os.listdir(result_path) # 获取result下的文夹列表 i = len(result_list) # 统计文件夹数量 for file in result_list: path = os.path.join(result_path, file) # 拼接每个文件夹的路径 if i > 1: # 保留最新的文件夹 shutil.rmtree(path) i -= 1 except Exception as e: self.log.error(e) raise Exception("删除result下文件夹时异常!")
class AppOperation(unittest.TestCase): """技术操作""" def setUp(self): try: self.base_page = BasePage() self.log = MyLog().get_log().logger except Exception as e: self.log.error(e) raise Exception("出现异常!") def test_app_operation(self): """app技术操作""" bottom_menu = ("css_selector", "span.tab-title.ng-binding") # 底部menu operation_tool_menu = ("css_selector", "i.iconfont.nw-tool.mine-icon") # 操作工具 operation_exam_menu = ("css_selector", "a.item.item-icon-right") # 技术操作考核 prompt_nothing = ("css_selector", "div.nw-nothing-text") # “暂无内容”提示 operation_list = ("css_selector", "ion-item.item-remove-animate.item-text-wrap.item") # 试卷list operation_name = ("css_selector", "h1.operate-title.text-center.font-large.ng-binding") # 试卷名称 scan_button = ("css_selector", "button.button.button-block.button-calm") # 扫码考核按钮 popup_title = ("css_selector", "h3.popup-title.ng-binding") # 提示框title deduction_button = ("xpath", "//android.view.View[@content-desc='扣分']") # 扣分按钮 remark = ("class", "android.widget.EditText") # 扣分备注 deduction_value = ("xpath", "//android.view.View[@content-desc='0.5分']") # 扣分值 determine_button = ("xpath", "//android.view.View[@content-desc='确认']") # 确认扣分 remark_value = u"agaa vjvvabaz v测试,./!,。?!1234567890" * 10 submit_button = ("css_selector", "button.button.button-calm.exam-button") # 判分零和提交按钮 try: # 用例开始,输入负责人姓名,必须 self.base_page.case_start("李彬") self.assertIs(self.base_page.displayed(bottom_menu), True, "首页异常!") self.log.debug("进入[我的]") self.base_page.click_elem_tag(bottom_menu, tag=-1) self.base_page.screen_shot() if self.base_page.displayed(operation_tool_menu): self.log.debug("进入[操作工具]") self.base_page.click_elem_tag(operation_tool_menu) self.base_page.screen_shot() if self.base_page.displayed(prompt_nothing): self.log.debug("暂无内容!") self.base_page.case_pass() return else: self.log.debug("没有[操作工具]选项,结束测试!") self.base_page.case_pass() return self.log.debug("进入[技术操作列表]") self.base_page.click_elem_tag(operation_exam_menu) self.base_page.screen_shot() self.assertIs(self.base_page.displayed(operation_list), True, "进入[技术操作列表]异常!") self.log.debug("进入[技术操作详情]") operation_count = len(self.base_page.find_elements(operation_list)) # 技术操作试卷数量 i = random.randint(0, operation_count - 1) self.base_page.click_elem_tag(operation_list, tag=i, roll=True) self.base_page.screen_shot() self.assertIs(self.base_page.displayed(scan_button), True, "进入[技术操作详情]异常!") self.base_page.get_text(operation_name, text="考核表名称:") self.log.debug("请扫描考生的二维码!") self.base_page.click_elem_tag(scan_button) i = 0 while self.base_page.displayed(scan_button): self.log.debug("等待扫码!") time.sleep(2) # 开始考核/重新考核/非本院学员/扫码失败 if self.base_page.displayed(popup_title): self.base_page.popup() break i += 1 if i == 30: raise Exception("扫码超时!") self.assertIs(self.base_page.displayed(submit_button), True, "进入答题页面异常!") self.log.debug("扫码完成,进入答题页面!") self.base_page.switch_context(tag=0) elements = len(self.base_page.find_elements(deduction_button)) self.log.debug("总题数:%s" % str(elements)) if elements != 0: i = 0 while i < elements: self.log.debug("点击第%s题的扣分按钮" % (i+1)) self.base_page.click_elem_tag(deduction_button, tag=i, roll=True) self.base_page.screen_shot() self.log.debug("选择扣分") self.assertIs(self.base_page.displayed(deduction_value), True, "点击扣分按钮时异常!") tag_id = len(self.base_page.find_elements(deduction_value)) self.base_page.click_elem_tag(deduction_value, tag=(tag_id - 1)) self.base_page.get_text(deduction_value, text="第%s题扣除:" % (i+1), tag=(tag_id-1)) self.base_page.screen_shot() self.base_page.click_elem_tag(determine_button) # 确认扣分 self.base_page.screen_shot() if self.base_page.displayed(remark, tag=i): self.log.debug("输入第%s题的扣分备注" % (i+1)) self.base_page.input_tag(remark, remark_value, tag=i, roll=True) self.base_page.screen_shot() i += 1 if i >= 5: self.log.debug("中断答题") self.base_page.screen_shot() break self.base_page.switch_context() self.log.debug("点击[提交]按钮,提交本次考核成绩!") self.base_page.click_elem_tag(submit_button, tag=1) self.assertIs(self.base_page.displayed(popup_title), True, "点击[提交]按钮后未出现确定提交的提示框!") self.base_page.popup() self.assertIs(self.base_page.displayed(popup_title), True, "点击[确定]按钮后未出现提交成功的提示框!") self.base_page.popup() self.assertIs(self.base_page.displayed(scan_button), True, "提交考试结果后未回到技术操作详情页面!") self.log.debug("本次考核成绩已提交!") # 用例成功,必须 self.base_page.case_pass() else: self.base_page.screen_shot() raise Exception("页面显示异常!") except Exception as e: self.log.error(e) # 用例失败,必须 self.base_page.case_failed() raise Exception("出现异常!") finally: self.base_page.back_to() def tearDown(self): self.base_page.case_end()
class AppLogin(unittest.TestCase): """登录""" def setUp(self): try: config = ReadConfig() self.base_page = BasePage() self.log = MyLog().get_log().logger self.user_name = config.get_app_param("user_name") self.password = config.get_app_param("password") except Exception as e: self.log.error(e) raise Exception("出现异常!") def test_app_login(self): """app登录""" username_input = ("css_selector", "input[type='text']") # 用户名输入框定位信息 password_input = ("css_selector", "input[type='password']" ) # 密码输入框定位信息 login_button = ("css_selector", "button.button.button-block.button-calm") # 登录按钮定位信息 bottom_menu = ("css_selector", "span.tab-title.ng-binding") # 底部menu answer_sheet_elem = ("css_selector", "button.button.button-calm.exam-button" ) # 答题页面的“答题卡”按钮 submit_button = ("css_selector", "button.button.button-block.button-calm") # 提交按钮 # 登录成功提示,有未答题提示,自动提交试卷提示 popup_title = ("css_selector", "h3.popup-title.ng-binding") # 提示框title try: # 用例开始,输入负责人姓名,必须 self.base_page.case_start("李彬") self.log.debug("打开app") self.base_page.open_app() for i in range(1, 6, +1): # 未登录状态 if self.base_page.displayed(login_button): self.log.debug("输入用户名") self.base_page.input_tag(username_input, self.user_name) self.log.debug("输入密码") self.base_page.input_tag(password_input, self.password) self.base_page.screen_shot() self.log.debug("点击登录按钮") self.base_page.click_elem_tag(login_button) self.base_page.screen_shot() # 用弹框的标题断言 self.assertEqual(self.base_page.displayed(login_button), False, "登录失败!") # 打印弹框上的信息 self.base_page.popup() self.log.debug("进入首页!") self.base_page.case_pass() break # 有提示框(登录成功提示,自动交卷提示) elif self.base_page.displayed(popup_title): # 打印弹框上的信息 self.base_page.popup() self.base_page.back_to() # 返回首页 self.log.debug("返回首页!") self.base_page.case_pass() break # 处于答题页面 elif self.base_page.displayed(answer_sheet_elem): self.log.debug("进入app后正处于答题页面,进入答题卡页面提交试卷,结束该次考试!") self.base_page.screen_shot() self.base_page.click_elem_tag(answer_sheet_elem) # 点击提交按钮 self.base_page.click_elem_tag(submit_button) # 未答题的提示框 self.base_page.popup() self.base_page.back_to() # 返回首页 self.log.debug("返回首页!") self.base_page.case_pass() break # 进入首页 elif self.base_page.displayed(bottom_menu): self.base_page.screen_shot() self.log.debug("进入首页!") self.base_page.case_pass() break else: raise Exception("打开app时异常!") except Exception as e: self.log.error(e) # 用例失败,必须 self.base_page.case_failed() raise Exception("出现异常!") def tearDown(self): self.base_page.case_end()
class AppCourseware(unittest.TestCase): """课件""" def setUp(self): try: self.base_page = BasePage() self.log = MyLog().get_log().logger except Exception as e: self.log.error(e) raise Exception("出现异常!") def test_app_courseware(self): """app课件""" top_menu = ("xpath", "//div[@class='nw-tab']/div") # 课件类型menu search_menu = ("css_selector", "button.button.button-calm.exam-button") # 搜索按钮 search_input = ("css_selector", "input[type='text']") # 搜索内容 search_button = ("css_selector", "button.button.button-small.button-clear.button-dark.search-button.font-small") # 搜索按钮 global text text = "" try: # 用例开始,输入负责人姓名,必须 self.base_page.case_start("李彬") if not self.base_page.home_page_to("课件"): # 判断待办事项中是否有课件 if AppCourseware.enter(self) is False: # 首页进入课件 # 如果没有课件就结束用例 self.base_page.case_pass() return # 开始学习 self.log.debug("进入全部:") AppCourseware.study(self) type_count = len(self.base_page.find_elements(top_menu)) # 课件类型数量 i = 1 while i < type_count: self.base_page.get_text(top_menu, tag=i, text="进入:") self.base_page.click_elem_tag(top_menu, tag=i) AppCourseware.study(self, tag=i) i += 1 self.log.debug("进入搜索:") self.base_page.click_elem_tag(search_menu) self.base_page.input_tag(search_input, text) self.base_page.screen_shot() self.base_page.switch_handle(search_button) self.base_page.click_elem_tag(search_button) self.base_page.screen_shot() self.base_page.switch_handle(search_button) AppCourseware.study(self) # 用例成功,必须 self.base_page.case_pass() except Exception as e: self.log.error(e) # 用例失败,必须 self.base_page.case_failed() raise Exception("出现异常!") finally: self.base_page.back_to() def enter(self): """进入课件""" module = ("css_selector", "img[src='img/courseware.png']") # 首页“课件”menu title = ("css_selector", "div.title.ng-binding") # “课件”title all_list = ("css_selector", "ion-item.item-remove-animate.item-text-wrap.item.item-complex") # “课件”list try: self.assertIs(self.base_page.displayed(module), True, "首页异常!") self.log.debug("从首页进入课件模块") self.base_page.click_elem_tag(module) self.base_page.screen_shot() self.base_page.switch_handle(title) self.assertIs(self.base_page.displayed(title), True, "进入课件模块异常!") if self.base_page.displayed(all_list): return True else: self.log.debug("暂无内容!") return False except Exception as e: self.log.error(e) raise Exception("出现异常!") def study(self, tag=0): """学习课件""" top_menu = ("xpath", "//div[@class='nw-tab']/div") # 课件类型menu courseware_list = ("css_selector", "ion-item.item-remove-animate.item-complex.item-text-wrap.item") # 课件list courseware_name = ("css_selector", "h2.exam-title.font-large.text-center.ng-binding") # 课件名称 start_answer_button = ("xpath", "//a[contains(text(), '开始学习')]") try: courseware_count = len(self.base_page.find_elements(courseware_list)) if courseware_count != 0: self.base_page.click_elem_tag(courseware_list) self.assertIs(self.base_page.displayed(courseware_name), True, "进入课件列表异常!") name = self.base_page.get_text(courseware_name, text="进入课件:") self.base_page.screen_shot() global text text = name self.base_page.switch_handle(start_answer_button) self.assertIs(self.base_page.displayed(start_answer_button), True, "进入课件主页异常!") self.log.debug("点击开始学习按钮") self.base_page.click_elem_tag(start_answer_button, t=2) self.base_page.screen_shot() self.base_page.back_to(top_menu) self.base_page.click_elem_tag(top_menu, tag=tag) self.log.debug("学习完成!") else: self.log.debug("暂无内容!") except Exception as e: self.log.error(e) raise Exception("出现异常!") def tearDown(self): self.base_page.case_end()