Menu:企业微信移动app测试实战(2)、(3)

参考链接
uiautomator 定位:
https://developer.android.com/reference/android/support/test/uiautomator/UiSelector.html 8XPath 用法:https://www.w3.org/TR/xpath-functions/#func-matcheshttps://www.w3.org/TR/xpath-functions/元素定位测试步骤三要素:定位、交互、断言定位Id 定位(优先级最高)XPath 定位(速度慢,定位灵活)Accessibility ID 定位(content-desc)Uiautomator 定位(速度快,语法复杂)xpath w3chttps://www.w3.org/TR/xpath-functions/#func-matches https://www.w3.org/TR/xpath-functions/ xpath表达式常用用法:not 、contains、ends_with、starts_withand 、orXPath 常用方法绝对定位: 不推荐相对定位://*//*[contains(@resource-id, ‘login’)] (重点)//*[@text=‘登录’] (重点)//*[contains(@resource-id, ‘login’) and contains(@text, ‘登录’)] (重点)//*[contains(@text, ‘登录’) or contains(@class, ‘EditText’)] (了解)//[ends-with(@text,‘号’) ] | //[starts-with(@text,‘姓名’) ] 两个定位的集合列表 (了解)//*[@clickable=“true"]//android.widget.TextView[string-length(@text)>0 and string-length(@text)<20] (了解)//[contains(@text, ‘看点’)]/ancestor:://*[contains(@class, ‘EditText’)] (轴) (了解)#XPATH定位下一级目录直接/*[@text="定位文本元素"] ,如果定位元素是在孙子节点使用//*[@text="定位文本元素"]原生定位:Uiautomator 定位写法:’new UiSelector().text(“text")’滚动查找:new UiScrollable(new UiSelector().scrollable(true).instance(0)).scrollIntoView(new UiSelector().text(“查找的文本”).instance(0));toast:XPath 定位显式等待,动态的等待元素出现

案例 【联系人用例】:
1.使用pytest
2.使用xpath定位、使用原生定位滚动查找
3.使用参数化@pytest.mark.parametrize
4.验证 成功Toast #该案例如果用toast直接验证会报错,该案例必须使用XPath显式等待方式去完成toast验证

#datas/addcontact.yml文件内容
-- "霍格name2"- "男"- "13700000002"- - "霍格name3"- "女"- "13700000003"
#datas/delcontact.yml文件内容
- "霍格name4"
-  "霍格name5"
#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
联系人用例
'''
from time import sleepimport pytest
import yaml
from appium import webdriver
from appium.webdriver.common.mobileby import MobileBy
from selenium.webdriver.support.wait import WebDriverWaitwith open('datas/addcontact.yml') as f:addcontactdatas = yaml.safe_load(f)with open('datas/delcontact.yml') as f:delcontactdatas = yaml.safe_load(f)class TestContact:#setup和teardown改成类方法时就不用每次重新去启动APP,从而节省时间#改成类方法后,类不会再去实例化driver,节省时间def setup_class(self):caps = {}caps["platformName"] = "android"caps["deviceName"] = "emulator-5554"caps["appPackage"] = "com.tencent.wework"caps["appActivity"] = ".launch.LaunchSplashActivity"caps["noReset"] = "true"caps["noReset"] = "true"caps['skipServerInstallation'] = 'true'  # 跳过 uiautomator2 server的安装caps['skipDeviceInitialization'] = 'true'  # 跳过设备初始化# caps['dontStopAppOnReset'] = 'true'    # 启动之前不停止appcaps['settings[waitForIdleTimeout]'] = 0# 与server 建立连接,初始化一个driver 创建session,返回一个sessionidself.driver = webdriver.Remote("http://localhost:4723/wd/hub", caps)self.driver.implicitly_wait(15)#setup和teardown改成类方法时就不用每次重新去启动APP,从而节省时间#改成类方法后,类不会再去实例化driver,节省时间def teardown_class(self):self.driver.quit()@pytest.mark.parametrize('name,gender,phonenum',addcontactdatas)def test_addcontact(self, name, gender, phonenum):'''添加联系人用例设计1、打开应用2、点击通讯录3、点击添加成员4、手动输入添加5、输入【用户名】,姓别,手机号6、点击保存7、验证添加成功'''# name = "霍格name1"# gender = "女"# phonenum = "13700000001"self.driver.find_element(MobileBy.XPATH,"//android.widget.TextView[@text='通讯录']").click()self.driver.find_element(MobileBy.ANDROID_UIAUTOMATOR,'new UiScrollable(new UiSelector()''.scrollable(true).instance(0))''.scrollIntoView(new UiSelector()''.text("添加成员").instance(0));').click()self.driver.find_element(MobileBy.XPATH,"//android.widget.TextView[@text='手动输入添加']").click()# 设置姓名self.driver.find_element(MobileBy.XPATH,"//*[contains(@text, '姓名')]/../*[@class='android.widget.EditText']").send_keys(name)# 设置性别self.driver.find_element(MobileBy.XPATH,"//*[contains(@text, '性别')]/..//*[@text='男']").click()if gender == '男':self.driver.find_element(MobileBy.XPATH, "//*[@text='男']").click()else:self.driver.find_element(MobileBy.XPATH, "//*[@text='女']").click()# 设置手机号self.driver.find_element(MobileBy.XPATH, "//*[@text='手机号']").send_keys(phonenum)# 点击保存self.driver.find_element(MobileBy.ID, "com.tencent.wework:id/gq7").click()# 验证成功 Toast# self.driver.find_element(MobileBy.XPATH, "//*[@class='android.widget.Toast']").text#该案例如果用toast直接验证会报错,该案例必须使用XPath显式等待方式去完成toast验证element = WebDriverWait(self.driver, 10).until(lambda x: x.find_element(MobileBy.XPATH, "//*[@class='android.widget.Toast']"))result = element.textassert '成功' in result#setup和teardown改成类方法时就不用每次重新去启动APP,从而节省时间#改成类方法后,类不会再去实例化driver,节省时间#此时需要使用back()方法返回到上一个页面,不然找不到 【通讯录】,就不能完成下一次 添加联系人self.driver.back()@pytest.mark.parametrize('name', delcontactdatas)def test_delcontact(self, name):'''删除联系人用例设计:1.打开应用2.点击通讯录3.找到要删除的联系人4.进入联系人页面5.点击右上角三个点进入个人信息页面,编辑成员6.删除联系人7.确认删除8.验证删除成功'''# name = "霍格沃兹name3"self.driver.find_element(MobileBy.XPATH,"//android.widget.TextView[@text='通讯录']").click()# 点击 搜索框self.driver.find_element(MobileBy.ID, "com.tencent.wework:id/gq_").click()# 输入 联系人姓名self.driver.find_element(MobileBy.ID,"com.tencent.wework:id/ffq").send_keys(name)sleep(3)# 获取联系人列表elelist = self.driver.find_elements(MobileBy.XPATH, f"//*[@text='{name}']")# 判断 搜索出来的列表长度#搜索的时候,通过text属性进行定位,搜索框有该元素,至少会存在一个,所以此时列表长度判断<2if len(elelist) < 2:print("没有这个联系人")#此处添加return,完成元素列表长度判断后,直接return回到完成判断后的场景return# 存在 联系人,点击第一个elelist[1].click()self.driver.find_element(MobileBy.ID, "com.tencent.wework:id/gq0").click()# 点击 编辑成员self.driver.find_element(MobileBy.XPATH, "//*[@text='编辑成员']").click()# 滚动查找  删除成员 并点击self.driver.find_element(MobileBy.ANDROID_UIAUTOMATOR,'new UiScrollable(new UiSelector()''.scrollable(true).instance(0))''.scrollIntoView(new UiSelector()''.text("删除成员").instance(0));').click()# 确定删除self.driver.find_element(MobileBy.XPATH, "//*[@text='确定']").click()sleep(2)# 验证删除成功elelist_after = self.driver.find_elements(MobileBy.XPATH, f"//*[@text='{name}']")assert len(elelist) - len(elelist_after) == 1

Menu:企业微信移动app测试实战(4)、(5)
PO改造

PO 封装
PO的方法和字段的意义方法意义用公共方法代表UI所提供的服务方法应该返回其他的PageObject或者返回用于断言的数据同样的行为不同的结果可以建模为不同的方法不要在方法内加断言字段意义不要暴露页面内部的元素给外部不需要建模UI内的所有元素
改造流程改造 1、搭建PO 模式的架构, 将业务逻辑写出来,后面就不需要改动,除非业务逻辑发生变化      #企业微信移动app测试实战4 1h05min改造 2、填充业务逻辑的实现代码,前端业务流程不变,只变后台逻辑改造 3、BasePage封装,初始化driver改造 4、app.py页面, 复用driver ,判断driver是否为None,如果为None 则创建一个Driver, 否则 复用原来的driver self.driver.launch_app()改造 5、封装 find(), find_and_click(), find_and_sendkeys(), webdriver_wait(), find_byscroll()改造 6、添加日志便于定位问题
BasePage页面的封装:初始化方法find方法find_and_click方法handle_exception方法   #异常处理加入良好日志方便定位: #日志添加使用标准log取代printlogging.baseConfig(level=logging.DEBUG)在具体的action中加入log方法方便跟踪App页面的封装:启动应用关闭应用重启应用进入首页数据驱动的应用:测试数据的数据驱动PO定义的数据驱动测试步骤的数据驱动断言数据驱动#不要全部框架数据驱动,会丢失代码比较重要的重构、建模、开放的生态能力
多个页面之间相互导入时产生循环导入的问题(most likely due to a circular import),解决方案:使用局部导入,注释掉循环导入的名称,然后通过alt+enter键选择'import xxx locally'局部导入的方式

案例:实现PO改造
改造流程
改造 1、搭建PO 模式的架构, 将业务逻辑写出来,后面就不需要改动,除非业务逻辑发生变化 #企业微信移动app测试实战4 1h05min
改造 2、填充业务逻辑的实现代码,前端业务流程不变,只变后台逻辑
改造 3、BasePage封装,初始化driver
改造 4、app.py页面, 复用driver ,判断driver是否为None,如果为None 则创建一个Driver, 否则 复用原来的driver self.driver.launch_app()
改造 5、封装 find(), find_and_click(), find_and_sendkeys(), webdriver_wait(), find_byscroll()
改造 6、添加日志便于定位问题
Pycharm项目说明:
1.datas文件夹存放数据驱动文件
2.page文件夹完成page页面元素定位及页面业务操作
3.testcases文件夹 测试用例的业务链调用

#datas/addcontact.yml文件内容
-- "霍格name2"- "男"- "13700000002"- - "霍格name3"- "女"- "13700000003"
#page/basepage.py文件内容
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import loggingfrom appium.webdriver.common.mobileby import MobileBy
from appium.webdriver.webdriver import WebDriver
from selenium.webdriver.support.wait import WebDriverWait"""
#封装滚动查找元素可以return 元素 ,封装click()和send_keys()就不可以啦,他们是方法不是元素,所以不能returnBasePage : 存放一些基本的方法,比如:初始化 driver , find查找元素,
find_and_click方法
handle_exception方法  #异常处理1.查找元素弹框、点击元素出现弹框、输入元素出现弹框(弹框指的是异常弹框,例如升级、消息弹框等),那么就需要对这些异常弹框进行处理2.使用try except 或者是 加装饰器处理这些异常情况3.然后继续操作我们的元素find_by_scroll方法   #滚动查找
加入良好日志方便定位:  #日志添加       #【企业微信移动app测试实战5】 增加日志,日志一般是在基类里面添加 30min使用python自带logging记录日志 方案一:1.使用标准log取代print           使用python自带logging记录日志                                2.logging.baseConfig(level=logging.DEBUG)     #logging.info   小写info 传入日志的相关message  #logging.INFO   大写INFO设置日志级别3.在具体的action中加入log方法方便跟踪logging.info(f'find: {locator}')#logging.basicConfig(level=logging.INFO)配置失效,日志不打印的情况处理#参考链接: https://blog.csdn.net/weixin_39773337/article/details/109138035#在调用logging.basicConfig前打印了root处理器的handles是有两个的,但是进一步去追踪是哪个地方先行调用了basicConfigroot_logger = logging.getLogger()for h in root_logger.handlers[:]:root_logger.removeHandler(h)logging.basicConfig(level=logging.INFO)方案二:  #企业微信移动app测试实战5 36min40s 使用pytest.ini收集日志1.创建一个pytest.ini,在执行的过程中收集测试结果,注意需要在pycharm里面安装allure-pytest,不然会报错#pytest.ini文件内容[pytest]addopts = --alluredir ../result     #存放结果路径2.因为你pytest.ini里面的那一行中文注释导致的,写英文注释的话是不会报错的 ini文件是不能随便修改的
"""
class BasePage:#python自带手机日志logging   level=logging.INFO 大写INFO设置日志级别,配置完成后会自动打印logging中的日志# logging.basicConfig(level=logging.INFO)#logging.basicConfig(level=logging.INFO)配置失效#在调用logging.basicConfig前打印了root处理器的handles是有两个的,但是进一步去追踪是哪个地方先行调用了basicConfigroot_logger = logging.getLogger()for h in root_logger.handlers[:]:root_logger.removeHandler(h)logging.basicConfig(level=logging.INFO)#None值在BasePage页面进行定义def __init__(self, driver: WebDriver = None):self.driver = driverdef find(self, locator):#小写info 传入日志的相关messagelogging.info(f'find: {locator}')return self.driver.find_element(*locator)def find_and_click(self, locator):logging.info('click')self.find(locator).click()def find_and_sendkeys(self, locator, text):logging.info(f'sendkeys : {text}')self.find(locator).send_keys(text)def find_by_scroll(self, text):logging.info('find_by_scroll')#封装滚动查找元素可以return 元素 ,封装click()和send_keys()就不可以啦return self.driver.find_element(MobileBy.ANDROID_UIAUTOMATOR,'new UiScrollable(new UiSelector()''.scrollable(true).instance(0))''.scrollIntoView(new UiSelector()'f'.text("{text}").instance(0));')def webdriver_wait(self, locator, timeout=10):logging.info(f'webdriver_wait: {locator}, timeout: {timeout}')element = WebDriverWait(self.driver, timeout).until(lambda x: x.find_element(*locator))return element#返回上一页方法封装def back(self, num=1):logging.info(f'back: {num}')for i in range(num):self.driver.back()
#page/app.py文件内容
#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
存放 app 应用常用的一些方法:比如启动app, 关闭app, 停止 app, 进入首页
'''
from appium import webdriver
from app.page.basepage import BasePage
from app.page.mainpage import MainPage
'''
App页面的封装:启动应用关闭应用重启应用进入首页app.py页面, 复用driver ,判断driver是否为None,如果为None 则创建一个Driver, 否则 复用原来的driver self.driver.launch_app()
app.py页面既然需要判断driver是否为None,就需要有一个None值,None值在BasePage页面进行定义。
driver的复用,改动app页面的同时还需要改动basepage,需要在basepage页面默认指定一个driver类型,给driver一个默认None值'''class App(BasePage):def start(self):'''启动app'''if self.driver == None:                 #既然需要判断driver是否为None,就需要有一个None值,None值在BasePage页面进行定义# 第一次调用start()方法的时候driver 为Nonecaps = {}caps["platformName"] = "android"caps["deviceName"] = "emulator-5554"caps["appPackage"] = "com.tencent.wework"# caps["appActivity"] = ".launch.LaunchSplashActivity"caps["appActivity"] = ".launch.WwMainActivity"caps["noReset"] = "true"caps['skipServerInstallation'] = 'true'  # 跳过 uiautomator2 server的安装caps['skipDeviceInitialization'] = 'true'  # 跳过设备初始化# caps['dontStopAppOnReset'] = 'true'    # 启动之前不停止appcaps['settings[waitForIdleTimeout]'] = 0# 与server 建立连接,初始化一个driver 创建session,返回一个sessionidself.driver = webdriver.Remote("http://localhost:4723/wd/hub", caps)else:# launch_app() 这个方法不需要传入任何参数, 会自动启动起来DesireCapa里面定义的activity# start_activity(packagename, activityname) 可以启动其它的应用的页面#如果driver存在,直接启动DesireCapa里面定义的activityself.driver.launch_app()self.driver.implicitly_wait(20)return selfdef restart(self):'''重启app'''self.driver.close()self.driver.launch_app()#return self 使用return self后表示的意思是调用完当前页面的该方法后仍然可以接着调用当前页面的其他方法return selfdef stop(self):'''停止 app'''self.driver.quit()def goto_main(self):'''进入首页'''#当 当前页面方法的目的是进入到其他页面,需要传递一个driver过去。 上一个页面传递给下一个页面driverreturn MainPage(self.driver)
#page/mainpage.py文件内容
#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
主页面
'''
from appium.webdriver.common.mobileby import MobileByfrom app.page.basepage import BasePage
from app.page.contactlistpage import ContactListPageclass MainPage(BasePage):# def __init__(self, driver):#     self.driver = drivercontactlist = (MobileBy.XPATH,"//android.widget.TextView[@text='通讯录']")def goto_contactlist(self):'''进入到通讯录'''# self.driver.find_element(MobileBy.XPATH,#                          "//android.widget.TextView[@text='通讯录']").click()self.find_and_click(self.contactlist)return ContactListPage(self.driver)def goto_workbench(self):pass
#page/contactlistpage.py文件内容
#!/usr/bin/env python
# -*- coding: utf-8 -*-'''
通讯录列表页
'''
from appium.webdriver.common.mobileby import MobileBy
from app.page.addmemberpage import AddMemberPage
from app.page.basepage import BasePageclass ContactListPage(BasePage):# def __init__(self, driver):#     self.driver = driveraddmember_text = "添加成员"def add_contact(self):# self.driver.find_element(MobileBy.ANDROID_UIAUTOMATOR,#                          'new UiScrollable(new UiSelector()'#                          '.scrollable(true).instance(0))'#                          '.scrollIntoView(new UiSelector()'#                          '.text("添加成员").instance(0));').click()self.find_by_scroll(self.addmember_text).click()return AddMemberPage(self.driver)def search_contact(self):pass
#page/addmemberpage.py文件内容
#!/usr/bin/env python
# -*- coding: utf-8 -*-'''
添加成员页
'''
# from app.page.contactaddpage import ContactAddPage
from appium.webdriver.common.mobileby import MobileBy
from selenium.webdriver.support.wait import WebDriverWait
from app.page.basepage import BasePageclass AddMemberPage(BasePage):# def __init__(self, driver):#     self.driver = driveradd_manual_element = (MobileBy.XPATH,"//android.widget.TextView[@text='手动输入添加']")toast_ele = (MobileBy.XPATH, "//*[@class='android.widget.Toast']")def add_menual(self):'''手动输入添加'''from app.page.contactaddpage import ContactAddPage# self.driver.find_element(MobileBy.XPATH,#                          "//android.widget.TextView[@text='手动输入添加']").click()self.find_and_click(self.add_manual_element)return ContactAddPage(self.driver)def get_toast(self):# text = '成功'# element = WebDriverWait(self.driver, 10).until(#     lambda x: x.find_element(MobileBy.XPATH, "//*[@class='android.widget.Toast']"))element = self.webdriver_wait(self.toast_ele)result = element.textreturn result
#page/contactaddpage.py文件内容
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# from app.page.addmemberpage import AddMemberPage
from appium.webdriver.common.mobileby import MobileBy
from app.page.basepage import BasePageclass ContactAddPage(BasePage):# def __init__(self, driver):#     self.driver = drivername_element = (MobileBy.XPATH,"//*[contains(@text, '姓名')]/../*[@class='android.widget.EditText']")gender_element = (MobileBy.XPATH,"//*[contains(@text, '性别')]/..//*[@text='男']")male_ele = (MobileBy.XPATH, "//*[@text='男']")female_ele = (MobileBy.XPATH, "//*[@text='女']")phonenum_ele = (MobileBy.XPATH, "//*[@text='手机号']")save_ele = (MobileBy.ID, "com.tencent.wework:id/ad2")save_txt = "保存"def set_name(self, name):# 设置姓名# self.driver.find_element(MobileBy.XPATH,#                          "//*[contains(@text, '姓名')]/../*[@class='android.widget.EditText']").send_keys(name)self.find_and_sendkeys(self.name_element, name)return selfdef set_gender(self, gender):# 设置性别# self.driver.find_element(MobileBy.XPATH,#                          "//*[contains(@text, '性别')]/..//*[@text='男']").click()self.find_and_click(self.gender_element)if gender == '男':# self.driver.find_element(MobileBy.XPATH, "//*[@text='男']").click()self.find_and_click(self.male_ele)else:self.find_and_click(self.female_ele)# self.driver.find_element(MobileBy.XPATH, "//*[@text='女']").click()return selfdef set_phonnum(self, phonenum):# 设置手机号# self.driver.find_element(MobileBy.XPATH, "//*[@text='手机号']").send_keys(phonenum)self.find_and_sendkeys(self.phonenum_ele, phonenum)return selfdef click_save(self):from app.page.addmemberpage import AddMemberPage# 点击保存# self.driver.find_element(MobileBy.ID, "com.tencent.wework:id/gq7").click()#可以通过滑动到元素的位置,点击文本self.find_by_scroll(self.save_txt).click()#self.find_by_scroll(self.save_txt)     #也可以通过滑动到文件的位置,通过xpath定位文本元素来实现点击文本# self.find_and_click(self.save_ele)#循环调用问题使用局部导入的解决方案#保存完成员验证保存成功后又一次跳入 添加成员页AddMemberPage 页面return AddMemberPage(self.driver)
#调用
#testcases/test_contact.py文件内容
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import pytest
import yaml'''
测试策略:上线前使用自动化遍历工具过一遍,后续会介绍
【企业微信移动app测试实战5】 41min 自动化遍历工具简单介绍:在上线之前,让你的所有的自动化用例全都去跑一遍,不要让页面出现空白、崩溃Monkey、AppCrawler
'''from app.page.app import App#解决中文编码问题  encoding='utf-8'
#路径 ../datas/addcontact.yml  ../表示上一层  ./datas/addcontact.yml  ./表示同一级文件路径
with open('../datas/addcontact.yml',encoding='utf-8') as f:addcontactdatas = yaml.safe_load(f)with open('../datas/delcontact.yml',encoding='utf-8') as f:delcontactdatas = yaml.safe_load(f)class TestContact:def setup_class(self):self.app = App()def teardown_class(self):self.app.stop()#如果一条用例执行失败,为了后面不受影响可以使用setup和teardown方法,setup进入主页,teardown方法多回退几次到首页def setup(self):self.main = self.app.start().goto_main()def teardown(self):self.app.back(5)@pytest.mark.parametrize('name,gender,phonenum',addcontactdatas)def test_addcontact(self, name, gender, phonenum):'''添加联系人'''# name = "霍格name2"# gender = "女"# phonenum = "13700000002"mypage = self.main.goto_contactlist(). \add_contact().add_menual(). \set_name(name).set_gender(gender).set_phonnum(phonenum).click_save()text = mypage.get_toast()# mypage.add_menual()assert '成功' in textself.app.back()#比较文件路径区别  ./test.yml  表示当前同一级文件路径   ../test.yml表示上一级文件路径# def test_file(self):#     with open('./test.yml', encoding='utf-8') as f:#         testdatas = yaml.safe_load(f)#         print(testdatas)
#testcases/pytest.ini文件内容    为配置文件 #后续会继续解读这块内容
#1.创建一个pytest.ini,在执行的过程中收集测试结果,注意需要在pycharm里面安装allure-pytest,不然会报错
#2.因为你pytest.ini里面的那一行中文注释导致的,写英文注释的话是不会报错的 ini文件是不能随便修改的   pytest.ini文件里面都不能写中文注释哦
[pytest]
addopts = --alluredir ../result     #存放结果路径

【移动端】企业微信移动app测试实战(2)、(3)相关推荐

  1. 判断当前入口是PC端企业微信还是PC端浏览器。或者是APP端企业微信

    function isQyweixin(){//判断当前入口是PC端还是APP端let flag = navigator.userAgent.match(/(phone|pad|pod|iPhone| ...

  2. 精通移动App测试实战:技术、工具和案例

    本文是根据书籍<精通移动App测试实战:技术.工具和案例>进行学习记录,方便后期查阅,感谢书籍作者提供的学习机会. 目录 第1章 Android系统基础内容介绍 1.6创建模拟器 第2章J ...

  3. vue实现网页端企业微信扫码登录功能(前端部分)

     时至今日,企业微信在企业日常工作中的使用越来越频繁也越来越重要,不少企业已使用企业微信进行着日常的工作安排管理.在这种背景下,各类系统和企业微信对接的需求也不断增加,今天要说的就是一个比较常见的需求 ...

  4. 《移动App测试实战》读书笔记

    最近看完了<移动App测试实战>,这里做一点笔记,后面可以重温. 功能测试自动化 轻量接口自动化测试(JMeter):JMeter是一款开源测试工具,多用于接口测试 用例的分层: CGI: ...

  5. 《移动App测试实战》——2.2 App UI层面的自动化

    本节书摘来自华章出版社<移动App测试实战>一 书中的第2章,第2.2节,作者:邱鹏 陈吉 潘晓明,更多章节内容可以访问云栖社区"华章计算机"公众号查看. 2.2 Ap ...

  6. 企业微信手机端可以退出吗?会影响电脑端企业微信吗?

    现在很多企业选择企业微信作为移动办公软件,企业微信打破了传统办公软件的地域限制,可以在手机端和电脑端进行登录,随时可以进行工作事宜. 前言 现在很多企业选择企业微信作为移动办公软件,企业微信打破了传统 ...

  7. 《移动App测试实战》——1.2 测试用例设计和评审

    本节书摘来自华章出版社<移动App测试实战>一 书中的第1章,第1.2节,作者:邱鹏 陈吉 潘晓明,更多章节内容可以访问云栖社区"华章计算机"公众号查看. 1.2 测试 ...

  8. 《精通引动APP测试实战:技术、工具和案例》---Android 开发环境搭建

    文章目录 一,环境搭建 1)Java环境(JDK) 2)Eclipse 开发工具 3)Android SDK 二,SDK和AVD的配置 1)SDK和AVD 2)Android SDK Manager下 ...

  9. 新书出版了 - 移动App测试实战

    好久没有更新blog了,算是憋了一个大招 :) 下面是这本书的前言. 前 言 现在已经是一个移动互联网的时代,借助手机等移动设备,人们可以完成资讯的获取.社交.游戏,以及日常生活的各种应用,甚至很多工 ...

  10. Zabbix 3.0 配置企业微信报警(注册---测试)

    一.申请企业微信 1.登录企业微信官网,点击企业注册 二.配置企业微信 1.邀请管理员使用企业微信,如果有多个人直接添加新成员 2.管理员收到邀请,下载手机版企业微信,使用微信号登陆即可 3.创建应用 ...

最新文章

  1. SAP系统如何快速上手?
  2. 最长回文子串--马拉车(?)
  3. python学习三:列表,元组
  4. 佳能g3800故障灯说明书_汽车仪表灯的使用方法以及注意事项
  5. Short-Session的推荐如何做?
  6. java不支持发行版本12_主要发行版本后Java开发人员应使用的15种工具
  7. 小朋友排队|2014年蓝桥杯B组题解析第十题-fishers
  8. 用linux集成电路版图设计,集成电路版图设计报告.doc
  9. markdown 中的一些 html 使用属性
  10. Linux struct itimerval用法
  11. 2021年危险化学品经营单位安全管理人员考试内容及危险化学品经营单位安全管理人员考试报名
  12. android 波斯文排序,android 阿拉伯,波斯字符串从右到左显示问题
  13. DNS是什么意思?什么是DNS服务器?(中科三方)
  14. 【Ubuntu 20.04 安装中文输入法 谷歌拼音】
  15. 3d max材质贴图
  16. 爬取微博视频页并批量下载python+requests+ffmpeg(连接视频和音频)
  17. 网站地图是什么,怎么制作和查看网站的地图呢?
  18. Kotlin第二章:kotlin基础
  19. 关于FlashDB的应用-GD32F450上
  20. 一份超详细的IBM公司JAVA基础面试题附答案以及解析(题库)

热门文章

  1. STM32 外部晶振电路设计和匹配
  2. 免费报表软件有哪些?5款热门工具
  3. 老电脑适合用linux,老旧电脑适于装什么操作系统
  4. Codeforces 592 A. PawnChess 【Codeforces Round #328 (Div. 2)】
  5. Linux 读书笔记 一
  6. PDF 文件格式 基本结构
  7. ios13文件管理器 连接服务器,支持鼠标、文件管理—IOS13和iPad OS深度体验报告
  8. 一维卷积神经网络在近红外光谱分析中的应用
  9. Centos8装Wine笔记
  10. echarts 画四川省地图 点击高亮并获取各市区参数