def check_current_activity(self, app_activity): '''验证当前activity是否登录传入app_activity''' current_activity = self.driver.current_activity if current_activity: cb.checkEqual(current_activity, app_activity) else: logging.error('当前没有app_activity')
def open_baidu_url(self, describe=None): '''打开百度首页首页''' if describe: logging.info(describe) self.open(data_web.url[0]) element = WebDriverWait(self.driver, 5, 0.5).until(EC.title_is(data_web.url[1])) cb.checkEqual(element, True)
def input_text_and_search(self, describe=None): '''搜索"poseidon并验证结果"''' if describe: logging.info(describe) try: self.click_element(data_web.search_box, is_button=False) self.set_text(data_web.search_box, 'poseidon') self.click_element(data_web.search_button[0], is_button=True) element = WebDriverWait(self.driver, 5, 0.5).until( EC.title_is(data_web.search_button[1])) cb.checkEqual(element, True) except Exception as e: logging.info(e) raise e
def send_music_and_respnse(self, send_data, check_point, status_exp=0, http_status=200): url = send_data.get('url') method = send_data.get('method') headers = send_data.get('headers') logging.info("[APNSUrl] %s" % (url)) resp_act = super().sendRequest(method=method, url=url, headers=headers, httpStatusExp=http_status, statusExp=status_exp) cb.checkPartInDict(check_point, resp_act)
def pytest_terminal_summary(): ''' :return: 1:通过邮件发送测试报告; ''' # 复制html测试报告,生成最新测试报告,方便jenkins中集成查看 log_path = pyconfig['logfile'].get('html') report_path = os.path.join(cb.report_dir(), 'html_current.html') shutil.copyfile(log_path, report_path) # 发送测试邮件 _section_mail = pyconfig["mail"] if _section_mail: _sender = _section_mail.get('sender', None) _receiver = _section_mail.get('receiver', None) if _receiver: _receiver = _receiver.split(',') _smtp_server = _section_mail.get('smtp_server') _smtp_port = _section_mail.get('smtp_port') _mail_user = _section_mail.get('mail_user') _mail_pwd = _section_mail.get('mail_pwd') _item_name = pyconfig['rootdir'].split('/')[-1] _mail_title = f'{_item_name}自动化测试报告' send_mail_object = SendMail(sender=_sender, receiver=_receiver, mail_title=_mail_title, smtp_server=_smtp_server, smtp_port=int(_smtp_port)) send_mail_object.send_mail()
def dict_info(self): # 帐密登录页面 return cb.get_value_from_env_data_dict({ Env.qa: {'app_package':'com.hjwordgames', # 'app_activity':'com.hujiang.browser.view.X5HJWebViewActivity' 'app_activity':'.activity.GuideLoginActivity' } })
def dict_info(self): # 帐密登录页面 return cb.get_value_from_env_data_dict({ Env.qa: {'app_package':'com.hjwordgames', 'activity_login':'******', 'activity_limit': '.Splash', } })
def limit(self): # 开启手机/电话/存储权限 return cb.get_value_from_env_data_dict({ Env.qa: [(By.ID, "com.hjwordgames:id/btn_start"), # 开启按钮 (By.ID, "com.android.packageinstaller:id/permission_allow_button"), # 存储权限允许 (By.ID, "com.android.packageinstaller:id/permission_allow_button") # 电话权限允许 ], })
def url(self): return cb.get_value_from_env_data_dict({ Env.qa: 'https://api.apiopen.top/searchMusic', Env.yz: 'https://api.apiopen.top/searchMusic', Env.prod: 'https://api.apiopen.top/searchMusic' })
def pytest_addoption(parser): # 自动产生日志文件 pyconfig["rootdir"] = parser._anonymous.parser.extra_info['rootdir'].strpath pyconfig['logfile'] = cb.get_log_path_forPytest() parser.addini(name='log_file' , help="log file" , type=None , default=pyconfig['logfile'].get("log")) # 添加命令行参数 parser.addoption("--env", action="store", default='qa', help="测试环境输入项 如qa、yz、prod 可参考poseidon/base/Env.py") parser.addoption("--frequency", action="store", default='five_min', help="执行间隔输入项 如one_min、five_min、one_hour、one_day、one_day 可参考poseidon/base/Frequency.py")
def setRedisValue(self, key, value, valueType=None, channel="uc", ttl=None): """ 设置redis值和value :param key: :param value: :param valueType: set string list hash :param channel: uc 用户中心 、nc消息中心 :return: """ try: if cb.can_not_do_byEnv(): return except: logging.error("无法读取Env环境参数") r = self.getRedisInstance(channel=channel) try: if valueType is None or valueType.lower() == "string": result = r.set(key, str(value), ex=ttl) elif valueType.lower() == "json": result = r.set(key, json.dumps(value), ex=ttl) elif valueType.lower() == "hash": for iKey, iValue in value.items(): result = r.hset(key, iKey, iValue) logging.info("redis设置hset操作:key={0}成功{1},返回{2}".format( key, iKey, result)) if ttl != None: result = r.expire(key, ttl) logging.info("redis设置hset的expire操作:key={0}成功,返回{1}".format( key, result)) return elif valueType.lower() == "list": for iValue in value: result = r.lpush(key, iValue) logging.info("redis设置lpush操作:key={0}成功{1},返回{2}".format( key, iValue, result)) if ttl != None: result = r.expire(key, ttl) logging.info( "redis设置lpush的expire操作:key={0}成功,返回{1}".format( key, result)) return logging.info("redis设置value操作:key={0}成功,返回{1}".format(key, result)) except Exception as e: logging.error(e)
def delRedisValue(self, key): try: if cb.can_not_do_byEnv(): return except: logging.error("无法读取Env环境参数") try: _key = key.strip() _resp_code = self.client.delete(_key) resp = "成功!!" if _resp_code == 1 else "失败!!" logging.debug("redis 删除key={0}, 结果{1}".format(_key, resp)) except Exception as e: logging.error(e)
def getRedisValue(self, key, valueType=None, channel="uc"): """ 获取redis存储的值 :param key: :param channel:uc 用户中心 、nc消息中心 :param type: 获取的消息类型 默认为None, string, json, hash,list :return: """ try: if cb.can_not_do_byEnv(): return except: logging.error("无法读取Env环境参数") r = self.getRedisInstance(channel=channel) logging.info("开始操作redis") try: logging.info("读取key={0},valueType={1}, channel={2}".format( key, valueType, channel)) if valueType == None or valueType.lower() == "string": result = str(r.get(key)) elif valueType.lower() == "json": result = json.loads(r.get(key)) elif valueType.lower() == "hash": result = dict(r.hgetall(key)) elif valueType.lower() == "list": result = list(r.lrange(key, 0, -1)) elif valueType.lower() == "zset": result = list(r.zrange(key, 0, -1)) elif valueType.lower() == "nset": # 普通set result = list(r.smembers(key)) else: result = None logging.info("读取redis的值={0}".format(result)) return result except Exception as e: logging.error(e)
def delRedisValue(self, key, channel="uc"): """ 删除redis中的key :param key: :param channel:uc 用户中心 、nc消息中心 :return: """ try: if cb.can_not_do_byEnv(): return except: logging.error("无法读取Env环境参数") redisConfig = self.getRedisConfig(channel=channel) redisHost = redisConfig["host"] redisPort = redisConfig["port"] redisDb = redisConfig["db"] r = redis.Redis(host=redisHost, port=redisPort, db=redisDb) try: return r.delete(key) except Exception as e: logging.error(e)
def start_immediately(self): # 点击立即开启 return cb.get_value_from_env_data_dict({ Env.qa: [(By.ID, 'com.hjwordgames:id/guide_action_sign_in_or_login'), ''] })
def cichang_setting_account(self): # 设置-账号 return cb.get_value_from_env_data_dict({ Env.qa: [(By.ID, "com.hjwordgames:id/setting_user"), ''] })
def app_path(self): # 开心词场 return cb.get_value_from_env_data_dict({ Env.qa: {'dict': '/Users/songmengyun/Poseidon-master/tests/hjdict2_hujiang_3.4.1.259.241819.apk', 'setting': '' }, })
def cichang_setting_account_logout(self): # 设置-账号-退出按钮 return cb.get_value_from_env_data_dict({ Env.qa: [(By.ID, "com.hjwordgames:id/logout_bt"), ''] })
def test_parametrize(self, a, b, expected): ''' 验证parametrize''' diff = a - b cb.checkEqual(diff, expected)
def _sendRequest_requests(self, method, url, data=None, headers=None, cookie=None, files=None, httpStatusExp=None, statusExp=None, needJson=True, bText=True, agent=None, **kwargs): ''' request发送发送API请求,支持application/x-www-form-urlencoded,application/json格式 ''' if headers is None: # 如果header为空,默认header headers = self._default_header if isinstance(headers, list): headers = self.conver_header_list_2_dict(headers) new_headers = self.dict_key_to_lower(headers) if 'content-type' in new_headers and new_headers[ 'content-type'] == 'application/x-www-form-urlencoded': data = data if data else None elif 'content-type' in new_headers and new_headers[ 'content-type'] == 'application/json': data = json.dumps(data) if data else None if 'content_tyep' in new_headers and new_headers[ 'content-type'] == 'multipart/form-data': files = files if files else None # files = {'file':open('xxx.xls', 'rb')} logging.info("请求url: {}".format(url)) logging.info("请求method: {}".format(method)) logging.info("请求header: {}".format(headers)) logging.info("请求header type is: {}".format(type(headers))) logging.info("请求data: {}".format(data)) # 增加容错处理,默认retry_num=3 for i in range(self._retry_num): try: self.bContinue = False if method == "GET": if "https" in url: resp = requests.get(url, params=data, headers=headers, cookies=cookie, timeout=self._timeout, stream=True, verify=False) else: resp = requests.get(url, params=data, headers=headers, cookies=cookie, timeout=self._timeout, stream=True) elif method == "PUT": if "https" in url: resp = requests.put(url, data=data, headers=headers, cookies=cookie, timeout=self._timeout, stream=True, verify=False) else: resp = requests.put(url, data=data, headers=headers, cookies=cookie, timeout=self._timeout, stream=True) elif method == "DELETE": if "https" in url: resp = requests.delete(url, data=data, headers=headers, cookies=cookie, timeout=self._timeout, stream=True, verify=False) else: resp = requests.delete(url, data=data, headers=headers, cookies=cookie, timeout=self._timeout, stream=True) elif method == "POST": if "https" in url: resp = requests.post(url, data=data, files=files, headers=headers, cookies=cookie, timeout=self._timeout, stream=True, verify=False) else: resp = requests.post(url, data=data, files=files, headers=headers, cookies=cookie, timeout=self._timeout, stream=True) elif method == "PATCH": if "https" in url: resp = requests.patch(url, data=data, headers=headers, cookies=cookie, timeout=self._timeout, stream=True, verify=False) else: resp = requests.patch(url, data=data, headers=headers, cookies=cookie, timeout=self._timeout, stream=True) else: logging.error( "The request method %s is not in ['post','get','put','delete','patch']" ) assert False break except Exception as e: msg = "send request [%s] %s failed: %s" % (method, url, str(e)) logging.info(e) logging.info(msg) if (str(e).find('Max retries exceeded') > 0 or str(e).find('Read timed out') > 0 or str(e).find('Connection aborted') > -1 ) and i + 1 < self._retry_num: time.sleep(10) self.bContinueb = True continue assert False, msg finally: if not self.bContinue: pass # 校验httpStatusCode if httpStatusExp: logging.info("校验httpStatusExp") cb.checkResultEqual( resp.status_code, httpStatusExp, f'actual status_code: {resp.status_code}, expect status_code: {httpStatusExp}' ) # 输出响应头和响应内容 logging.info('响应headers: {}'.format(resp.headers)) if needJson: if bText: resp_text = resp.text else: resp_text = resp.content resp = cb.loadJsonData(resp_text) logging.info("响应response: {}".format(resp)) if statusExp: cb.checkValueInDict( resp, "status", statusExp, "[Request] resp data['status'] does not match with %s" % str(statusExp)) return resp
def cichang_setting(self): # 设置 return cb.get_value_from_env_data_dict({ Env.qa: [(By.ID, "com.hjwordgames:id/v_setting_img"), ''] })
def cichang_setting_account_logout_right_button(self): # 设置-账号-退出按钮-退出确认 return cb.get_value_from_env_data_dict({ Env.qa: [(By.ID, "com.hjwordgames:id/right_button"), ''] })
def _sendRequest_pycurl(self, method, url, data=None, headers=None, cookie=None, files=None, httpStatusExp=None, statusExp=None, needJson=True, bText=True, agent=None, **kwargs): ''' pycurl发送发送API请求,支持application/x-www-form-urlencoded,application/json格式 同时输出请求时间片段 ''' try: import pycurl b = BytesIO() # 创建缓存对象 c = pycurl.Curl() # 创建一个curl对象 c.setopt(pycurl.URL, url) c.setopt(pycurl.WRITEDATA, b) # 设置资源数据写入到缓存对象 if pyconfig["sections"].get('http').get("debug").lower() == 'true': c.setopt(pycurl.VERBOSE, True) logging.info("请求url: {}".format(url)) logging.info("请求method: {}".format(method)) if headers is None: c.setopt(pycurl.HTTPHEADER, self._default_header) logging.info("请求header: {}".format(self._default_header)) logging.info("请求header type is: {}".format( type(self._default_header))) else: if isinstance(headers, list): c.setopt(pycurl.HTTPHEADER, headers) logging.info("请求header: {}".format(headers)) logging.info("请求header type is: {}".format(type(headers))) else: c.setopt(pycurl.HTTPHEADER, self.conver_header_dict_2_list(headers)) logging.info("请求header: {}".format( self.conver_header_dict_2_list(headers))) logging.info("请求header type is: {}".format( type(self.conver_header_dict_2_list(headers)))) if agent: c.setopt(pycurl.USERAGENT, agent) # agent加入c对象 logging.info("请求agent:{}".format(agent)) else: agent = self.get_agent() c.setopt(pycurl.USERAGENT, agent) # agent加入c对象 if cookie: c.setopt(pycurl.COOKIE, cookie) # cookie加入c对象 if data: new_headers = self.dict_key_to_lower(headers) if 'content-type' in new_headers and new_headers.get( 'content-type') == 'application/x-www-form-urlencoded': c.setopt(pycurl.POSTFIELDS, self.conver_data_dict_to_str(data)) logging.info("请求data: {}".format(data)) elif 'content-type' in new_headers and new_headers.get( 'content-type') == 'application/json': c.setopt(pycurl.POSTFIELDS, json.dumps(data)) logging.info("请求data: {}".format(json.dumps(data))) if method == "GET": pass elif method == "PUT": c.setopt(pycurl.CUSTOMREQUEST, "PUT") elif method == "DELETE": c.setopt(pycurl.CUSTOMREQUEST, "DELETE") elif method == "POST": c.setopt(pycurl.CUSTOMREQUEST, "POST") elif method == "PATCH": c.setopt(pycurl.CUSTOMREQUEST, "PATCH") else: logging.error('not support method: {}'.format(method)) # 增加容错处理,默认retry_num=3 for i in range(self._retry_num): try: self.bContinue = False c.perform() total_time = c.getinfo(pycurl.TOTAL_TIME) # 上一请求总的时间 dns_time = c.getinfo(pycurl.NAMELOOKUP_TIME) # 域名解析时间 connect_time = c.getinfo(pycurl.CONNECT_TIME) # 远程服务器连接时间 redirect_time = c.getinfo( pycurl.REDIRECT_TIME) # 重定向所消耗的时间 ssl_time = c.getinfo(pycurl.APPCONNECT_TIME) # SSL建立握手时间 pretrans_time = c.getinfo( pycurl.PRETRANSFER_TIME) # 连接上后到开始传输时的时间 starttrans_time = c.getinfo( pycurl.STARTTRANSFER_TIME) # 接收到第一个字节的时间 transfer_time = total_time - starttrans_time # 传输时间 serverreq_time = starttrans_time - pretrans_time # 服务器响应时间,包括网络传输时间 if ssl_time == 0: if redirect_time == 0: clientper_time = pretrans_time - connect_time # 客户端准备发送数据时间 redirect_time = 0 else: clientper_time = pretrans_time - redirect_time redirect_time = redirect_time - connect_time ssl_time = 0 else: clientper_time = pretrans_time - ssl_time if redirect_time == 0: ssl_time = ssl_time - connect_time redirect_time = 0 else: ssl_time = ssl_time - redirect_time redirect_time = redirect_time - connect_time connect_time = connect_time - dns_time logging.info('请求总时间: %.3f ms' % (total_time * 1000)) logging.info('DNS域名解析时间 : %.3f ms' % (dns_time * 1000)) logging.info('TCP连接消耗时间 : %.3f ms' % (connect_time * 1000)) logging.info('重定向时间: %.3f ms' % (redirect_time * 1000)) logging.info('SSL握手消耗时间 : %.3f ms' % (ssl_time * 1000)) logging.info('客户端发送请求准备时间: %.3f ms' % (clientper_time * 1000)) logging.info('服务器处理时间: %.3f ms' % (serverreq_time * 1000)) logging.info('数据传输时间: %.3f ms' % (transfer_time * 1000)) reps_code = c.getinfo(pycurl.RESPONSE_CODE) # 返回code body = b.getvalue() c.close() break except Exception as e: msg = "send request [%s] %s failed: %s" % (method, url, str(e)) logging.info(e) logging.info(msg) if (str(e).find('Max retries exceeded') > 0 or str(e).find('Read timed out') > 0 or str(e).find('Connection aborted') > -1 ) and i + 1 < self._retry_num: time.sleep(10) self.bContinueb = True continue assert False, msg finally: if not self.bContinue: pass except Exception as e: logging.error(e) if httpStatusExp: logging.info("校验httpStatusExp") cb.checkEqual(reps_code, httpStatusExp) resp = body.decode('utf-8') logging.info("响应response:{}".format(resp)) if needJson: return json.loads(resp) else: return resp
def _sendRequest_multipart(self, method, url, headers=None, cookie=None, httpStatusExp=None, statusExp=None, needJson=True, bText=True, multipartEncodedContent=None): ''' Send Multipart request with method post or put ''' if not isinstance(multipartEncodedContent, MultipartEncoder): raise RuntimeError("multipartEncodedContent invalid") if headers is None: # 如果header为空,默认header headers = self._default_header if isinstance(headers, list): headers = self.conver_header_list_2_dict(headers) headers.update({'Content-Type': multipartEncodedContent.content_type}) try: if method == "POST": if "https" in url: resp = requests.post(url, data=multipartEncodedContent, headers=headers, verify=False, cookies=cookie) else: resp = requests.post(url, data=multipartEncodedContent, headers=headers, cookies=cookie) elif method == "PUT": if "https" in url: resp = requests.put(url, data=multipartEncodedContent, headers=headers, verify=False, cookies=cookie) else: resp = requests.put(url, data=multipartEncodedContent, headers=headers, cookies=cookie) except Exception as e: print("[Request Exception] {0}: {1}".format(type(e), e)) msg = "send request {%s] %s failed: %s" % (method, url, str(e)) logging.error(e) logging.error(msg) assert False, msg if httpStatusExp: logging.info("校验httpStatusExp") cb.checkResultEqual( resp.status_code, httpStatusExp, f'actual status_code: {resp.status_code}, expect status_code: {httpStatusExp}' ) # 输出响应头信息 logging.info('响应headers: {}'.format(resp.headers)) if needJson: if bText: resp_text = resp.text else: resp_text = resp.content resp = cb.loadJsonData(resp_text) if statusExp: cb.checkValueInDict( resp, "status", statusExp, "[Request] resp data['status'] does not match with %s" % str(statusExp)) return resp
def username_pwd_button(self): # 帐密登录按钮 return cb.get_value_from_env_data_dict({ Env.qa: [(By.XPATH, "//*[@content-desc='帐号密码登录']"), ''] })
def search_box(self): # 百度首页 return cb.get_value_from_env_data_dict( {Env.qa: (By.XPATH, '//*[@id="kw"]')})
def username_pwd_login(self): # 用户名+密码+登录按钮 return cb.get_value_from_env_data_dict({ Env.qa: [(By.XPATH, "//*[@content-desc='手机号/邮箱/用户名']"), (By.XPATH, "//*[@NAF='true']"), (By.XPATH, "//*[@content-desc='登录']")] })
def test_commonbase(self): """验证commonbase中比较参数""" cb.checkEqual(1, 1) # 比较n=expected cb.checkResultIsNotNone(2) # 验证n不为空 cb.checkDictionary({'a': 1}, {'a': 1}) # 字典比较 '''
def url(self): # 百度首页 return cb.get_value_from_env_data_dict( {Env.qa: ['https://www.baidu.com', '百度一下,你就知道']})
def search_button(self): # 百度首页 return cb.get_value_from_env_data_dict( {Env.qa: [(By.XPATH, '//*[@id="su"]'), 'poseidon_百度搜索']})