【实战练习】用户端App自动化测试实战
【实战练习】用户端 App 自动化测试实战
实战需求
实现思路
优化点
返回的实现
在自动化测试过程中,由于真实执行的过程,一定是有多条用例一起执行的。所以在用例执行完成之后,需要将 App 的状态设置为初始页面,以免影响后续的用例的执行。
Python 实现
def back_to_main_page(self, num):
# 返回主界面
for i in range(num):
self.driver.back()
def setup(self):
print("setup")
# 点击通讯录按钮
self.driver.activate_app("com.tencent.wework")
Java 实现
通过显示等待的机制,实现回到首页的操作。
@AfterEach
public void tearDown() {
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(15), Duration.ofMillis(500));
// 条件检查函数
ExpectedCondition<WebElement> condition = input -> {
driver.navigate().back();
return driver.findElement(AppiumBy.xpath("//*[@text='消息']"));
};
// 使用 until 方法并传递条件检查函数
WebElement element = wait.until(condition);
element.click();
}
成员添加失败自动化测试用例场景
Python 实现
root_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
class TestAddDuplicate:
def setup_class(self):
# 设置启动参数
print("setup_class")
# 定义一个字典
caps = {}
# 设置 app 安装平台
caps["appium:platformName"] = "Android"
# 设置 app 安装平台的版本
caps["appium:platformVersion"] = "6"
# 设置driver
caps["appium:automationName"] = "UiAutomator2"
# 设备的名字
caps["appium:deviceName"] = "MuMu"
# 设置 app 的包名
caps["appium:appPackage"] = "com.tencent.wework"
# 设置 app 启动页
caps["appium:appActivity"] = ".launch.LaunchSplashActivity"
# 不清空缓存
caps["appium:noReset"] = True
# 实例化 appiumOptions 对象
options = AppiumOptions().load_capabilities(caps)
# 连接 appium server
self.driver = webdriver.Remote("http://localhost:4723/wd/hub", options=options)
# 设置全局的隐式等待
self.driver.implicitly_wait(10)
# 实例化 faker 对象
faker = Faker("zh_CN")
# 生成姓名
self.name = faker.name()
# 生成手机号
self.phonenum = faker.phone_number()
def back_to_main_page(self, num):
# 返回主界面
for i in range(num):
self.driver.back()
def setup(self):
print("setup")
# 点击通讯录按钮
self.driver.activate_app("com.tencent.wework")
def teardown(self):
print("teardown")
# 返回上一个界面
self.back_to_main_page(2)
def teardown_class(self):
print("teardown_class")
# 退出 app
self.driver.quit()
def swipe_window(self):
'''
滑动界面
'''
# 滑动操作
# 获取设备的尺寸
size = self.driver.get_window_size()
# {"width": xx, "height": xx}
print(f"设备尺寸为 {size}")
width = size.get("width")
height = size.get('height')
# # 获取滑动操作的坐标值
start_x = width / 2
start_y = height * 0.8
end_x = start_x
end_y = height * 0.2
# swipe(起始x坐标,起始y坐标,结束x坐标,结束y坐标,滑动时间(单位毫秒))
self.driver.swipe(start_x, start_y, end_x, end_y, 2000)
def swipe_find(self, text, max_num=5):
'''
滑动查找
通过文本来查找元素,如果没有找到元素,就滑动,
如果找到了,就返回元素
'''
# 为了滑动操作更快速,不用等待隐式等待设置的时间
self.driver.implicitly_wait(1)
for num in range(max_num):
try:
# 正常通过文本查找元素
ele = self.driver.find_element(AppiumBy.XPATH, f"//*[@text='{text}']")
print("找到元素")
# 能找到则把隐式等待恢复原来的时间
self.driver.implicitly_wait(10)
# 返回找到的元素对象
return ele
except Exception:
# 当查找元素发生异常时
print(f"没有找到元素,开始滑动")
print(f"滑动第{num + 1}次")
# 滑动操作
self.swipe_window()
# 把隐式等待恢复原来的时间
self.driver.implicitly_wait(10)
# 抛出找不到元素的异常
raise NoSuchElementException(f"滑动之后,未找到 {text} 元素")
def enter_contact_page(self):
"""
进入通讯录页面
:return:
"""
# 点击通讯录按钮
self.driver.find_element(AppiumBy.XPATH, "//*[@text='通讯录']").click()
# 点击添加成员按钮
self.swipe_find("添加成员").click()
# 点击手动输入添加按钮
self.driver.find_element(AppiumBy.XPATH, "//*[@text='手动输入添加']").click()
def screenshot(self):
'''
截图
:param path: 截图保存路径
'''
# 获取当前时间
cur_time = time.strftime("%Y-%m-%d-%H-%M-%S")
# 截图文件名
file_path = cur_time + ".png"
# 截图保存目录
dir_path = os.sep.join([root_path, "screenshot"])
# 资源目录如果不存在则新创建一个
if not os.path.isdir(dir_path):
os.mkdir(dir_path)
# 截图保存路径
source_path = os.sep.join([dir_path, file_path])
# 截图
self.driver.save_screenshot(source_path)
# 返回保存图片的路径
return source_path
# 填写通讯录信息
def input_contact_info(self, name, phonenum):
"""
填写通讯录信息
:param name:
:param phonenum:
:return:
"""
# 输入姓名
self.driver.find_element(AppiumBy.XPATH, '//*[contains(@text,"姓名")]/../*[@text="必填"]').send_keys(name)
# 输入手机号
self.driver.find_element(AppiumBy.XPATH, '//*[contains(@text,"手机")]/..//*[@text="必填"]').send_keys(phonenum)
def test_add_contact_duplicate(self):
"""
1.打开企业微信
2.点击通讯录
3.点击添加成员
4.点击手动输入添加
5.输入姓名、手机号
6.点击保存
7.断言保存成功
8.返回通讯录页面
9.点击搜索按钮
10.输入搜索内容
11.断言搜索结果
12.返回通讯录页面
13.再次进行添加操作
14.断言保存失败
:return:
"""
with allure.step("步骤1:进入企业微信"):
# 点击通讯录按钮
self.enter_contact_page()
with allure.step("步骤2:输入姓名、手机号"):
# 输入姓名
self.input_contact_info(self.name, self.phonenum)
with allure.step("步骤3:点击保存"):
# 点击保存按钮
self.driver.find_element(AppiumBy.XPATH, "//*[@text='保存']").click()
# 断言保存成功
# 获取toast提示信息
toast_text = self.driver.find_element(AppiumBy.XPATH, "//*[@class='android.widget.Toast']").text
image_file = self.screenshot()
allure.attach.file(image_file, name="toast提示信息", attachment_type=allure.attachment_type.PNG)
assert toast_text == "添加成功"
with allure.step("步骤4:返回通讯录页面"):
self.back_to_main_page(1)
with allure.step("步骤5:搜索联系人"):
# 点击搜索按钮
self.driver.find_element(AppiumBy.XPATH, '//*[@text="悠然科技"]/../../../following-sibling::*/*[1]').click()
# 输入搜索内容
self.driver.find_element(AppiumBy.XPATH, "//*[@text='搜索']").send_keys(self.name)
# 断言搜索结果
eles = self.driver.find_elements(AppiumBy.XPATH,
'//*[@class="android.widget.ListView"]//*[@class="android.widget.ImageView"]/../../following-sibling::*/*[1]/*[1]')
image_file = self.screenshot()
allure.attach.file(image_file, name="列表数据", attachment_type=allure.attachment_type.PNG)
assert self.name in [ele.text for ele in eles]
with allure.step("步骤6:返回通讯录页面"):
self.back_to_main_page(2)
with allure.step("步骤7:再次进行添加操作"):
# 点击通讯录按钮
self.enter_contact_page()
# 输入信息
self.input_contact_info(self.name, self.phonenum)
# 点击保存按钮
self.driver.find_element(AppiumBy.XPATH, "//*[@text='保存']").click()
with allure.step("步骤7:断言保存失败"):
# 获取提示信息
notice_text = self.driver.find_element(AppiumBy.XPATH, '//*[@text="确定"]/../preceding-sibling::*/*/*').text
image_file = self.screenshot()
allure.attach.file(image_file, name="失败提示", attachment_type=allure.attachment_type.PNG)
assert "手机已存在于通讯录,无法添加" in notice_text
Java 实现
// 成员模块
public class TestMemberFail {
// AppiumServer版本 2.3.0
// 前置条件: 登录成功
public static AndroidDriver driver;
@BeforeAll
public static void setUpClass() {
//配置信息
UiAutomator2Options uiAutomator2Options = new UiAutomator2Options()
.setPlatformName("Android")
// 2.x 版本呢需要添加, 1.x 版本则不需要
.setAutomationName("uiautomator2")
// 不会重置app信息,解决登录问题
.setNoReset(true)
// 配置 app的package
.amend("appium:appPackage", "com.tencent.wework")
// 配置 app的appActivity 注意 企业微信必须用 .launch.LaunchSplashActivity
.amend("appium:appActivity", ".launch.LaunchSplashActivity")
// 强制app启动,解决server 掉不起来 应用的问题
.amend("appium:forceAppLaunch", true)
// 可以关闭app
.amend("appium:shouldTerminateApp", true);
//初始化
try {
// appium 1.x 版本 url 换成这个 http://127.0.0.1:4723/wd/hub
// 配置 server 的地址, 添加应用的配置信息
driver = new AndroidDriver(new URL("http://127.0.0.1:4723"), uiAutomator2Options);
// 显示等待
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(5));
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
// 退回到首页操作
@AfterEach
public void tearDown() {
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(15), Duration.ofMillis(500));
// 条件检查函数
ExpectedCondition<WebElement> condition = input -> {
driver.navigate().back();
return driver.findElement(AppiumBy.xpath("//*[@text='消息']"));
};
// 使用 until 方法并传递条件检查函数
WebElement element = wait.until(condition);
element.click();
}
@Test
public void addMemberFail() throws IOException {
driver.findElement(AppiumBy.xpath("//*[@text='通讯录']")).click();
for (int i = 1; i < 10; i++) {
// 如果没找到,则会出现找不到元素的异常,
try {
driver.findElement(AppiumBy.xpath("//*[@text='添加成员']")).click();
break;
} catch (Exception e) {
// 通过try 捕获这个异常,如果出现异常,则说明位置还没到,则做滑动操作
scrollDown();
}
}
driver.findElement(AppiumBy.xpath("//*[@text='手动输入添加']")).click();
driver.findElement(AppiumBy.xpath("//*[@text='姓名']/../*[@text='必填']")).sendKeys("东东");
driver.findElement(AppiumBy.xpath("//*[@text='手机']/..//*[@text='必填']")).sendKeys("15818591232");
driver.findElement(AppiumBy.xpath("//*[@text='保存']")).click();
// 获取toast 提示信息,为后续做断言
String toastMessage = driver.findElement(AppiumBy.xpath("//*[@text='手机已存在于通讯录,无法添加']")).getText();
ElementScreenBase("添加成员失败");
assertEquals("手机已存在于通讯录,无法添加", toastMessage);
}
// 截图方法
private void ElementScreenBase(String message) throws IOException {
// 调用截图方法
File screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
// 将截图的结果贴到allure报告中
Allure.addAttachment(message, "image/png", new FileInputStream(screenshot), ".png");
}
// 通过 touchAction实现滑动操作
public static void scrollDown() {
// 获取屏幕高度和宽度
int height = driver.manage().window().getSize().getHeight();
int width = driver.manage().window().getSize().getWidth();
// 设置滑动起始和结束坐标
int startY = (int) (height * 0.8);
int startX = width / 2;
int endY = (int) (height * 0.2);
TouchAction touchAction = new TouchAction(driver);
// longPress -> 长按 moveTo -》移动到 某个位置
touchAction.longPress(ElementOption.point(startX, startY))
.moveTo(ElementOption.point(startX, endY))
.release()
.perform();
}
}