腾讯微信团队打造的企业通讯与办公工具,具有与微信一致的沟通体验,丰富的 OA 应用,和连接微信生态的能力。
可帮助企业连接内部、连接生态伙伴、连接消费者。专业协作、安全管理、人即服务。
前提条件:
手机端安装好企业微信 App。
企业微信注册用户。
企业微信 App 搜索联系人功能自动化。
企业微信 App 添加成员功能自动化测试。
实战 1:搜索联系人测试场景
Python 实现
from appium import webdriver
from appium.webdriver.common.appiumby import AppiumBy
from appium.options.common import AppiumOptions
class TestWework:
def setup(self):
# Capability 设置定义为字典
caps = {}
# 设置 app 安装的平台(Android、iOS)
caps["platformName"] = "Android"
# 设置 app 安装平台的版本
caps["appium:platformVersion"] = "6"
# 设备的名字
caps["appium:deviceName"] = "MuMu"
# 设置 app 的包名
caps["appium:appPackage"] = "com.tencent.wework"
# 设置 app 启动页
caps["appium:appActivity"] = ".launch.LaunchSplashActivity"
# 不清空缓存
caps["appium:noReset"] = True
# app 不重启
# caps["appium:dontStopAppOnReset"] = True
options = AppiumOptions().load_capabilities(caps)
# 初始化 driver
self.driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", options=options)
# 设置全局的隐式等待
self.driver.implicitly_wait(10)
def teardown(self):
# 关闭 driver
self.driver.quit()
def test_search_contact(self):
冒烟用例,搜索存在的联系人
search_key = "蚊子"
# 点击通讯录按钮
self.driver.find_element(
AppiumBy.XPATH,
"//*[@text='通讯录']").click()
# 点击搜索按钮
self.driver.find_element(
# ID 定位,id在不同手机上可能会变化,所以不推荐使用
# AppiumBy.ID,
# "com.tencent.wework:id/lyp").click()
AppiumBy.XPATH,
# xpath 轴定位
'//*[@text="悠然科技"]/../../../following-sibling::*/*[1]').click()
# xpath 父子定位
# '//*[@text="悠然科技"]/../../../../*[3]/*[1]').click()
# 查找搜索框,输入搜索关键词
self.driver.find_element(
AppiumBy.XPATH,
"//*[@text='搜索']").send_keys(search_key)
# 获取搜索结果元素的文本,完成断言
eles = self.driver.find_elements(
AppiumBy.XPATH,
'//*[@class="android.widget.ListView"]//*[@class="android.widget.ImageView"]/../../following-sibling::*/*[1]/*[1]')
assert search_key in [ele.text for ele in eles]
Java 实现
import io.appium.java_client.AppiumBy;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.options.UiAutomator2Options;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.WebElement;
import java.net.MalformedURLException;
import java.net.URL;
import java.time.Duration;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class TestMember {
public static AndroidDriver driver;
@BeforeAll
public static void setUpClass() {
//配置信息
UiAutomator2Options uiAutomator2Options = new UiAutomator2Options()
.setPlatformName("Android")
.setAutomationName("uiautomator2")
.setNoReset(true)
.amend("appium:appPackage", "com.tencent.wework")
.amend("appium:appActivity", ".launch.LaunchSplashActivity")
.amend("appium:forceAppLaunch", true)
.amend("appium:shouldTerminateApp", true);
//初始化
try {
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);
@AfterAll
public static void tearDownClass(){
driver.quit();
* 前置条件: 登录成功
* 1. 进入通讯录页面
* 2. 点击搜索按钮,输入一个已经存在的成员名称。
@Test
public void searchMember() {
driver.findElement(AppiumBy.xpath("//*[@text='通讯录']")).click();
driver.findElement(AppiumBy.xpath("//*[@text='Hufflepuff']/../../../../*[3]/*[1]")).click();
driver.findElement(AppiumBy.xpath("//*[@text='搜索']")).sendKeys("张三");
List<WebElement> webElements = driver.findElements(AppiumBy.xpath("//*[@class='androidx.recyclerview.widget.RecyclerView']//*[@class='android.widget.LinearLayout']//*[@class='android.widget.TextView']"));
// 1. 遍历所有元素 2. 获取所有元素对应的文本 3. 判断是否包含张三 4. 返回判断结果,如果所有元素都包含,则返回true
assertTrue(webElements.stream().allMatch(item -> item.getText().contains("张三")));
实战 2:添加联系人测试场景
Python 实现
from appium import webdriver
from appium.webdriver.common.appiumby import AppiumBy
from appium.options.common import AppiumOptions
from faker import Faker
class TestWework:
def setup_class(self):
faker = Faker("zh_CN")
self.name = faker.name()
self.phonenum = faker.phone_number()
def setup(self):
# Capability 设置定义为字典
caps = {}
# 设置 app 安装的平台(Android、iOS)
caps["platformName"] = "Android"
# 设置 app 安装平台的版本
caps["appium:platformVersion"] = "6"
# 设备的名字
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)
# 初始化 driver
self.driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", options=options)
# 设置全局的隐式等待
self.driver.implicitly_wait(10)
def teardown(self):
# 关闭 driver
self.driver.quit()
def test_add_contact(self):
添加联系人
1。 点击通讯录,进入通讯录页面
2。 点击添加成员按钮,进入添加成员页面
3。 点击手动输入添加按钮,输入成员信息页面
5。 输入姓名,手机号,点击保存按钮
6。 返回添加成员页面
# 点击通讯录按钮
self.driver.find_element(
AppiumBy.XPATH,
"//*[@text='通讯录']").click()
# 滑动点击添加成员按钮
self.swipe_find("添加成员").click()
# 点击手动输入添加按钮
self.driver.find_element(
AppiumBy.XPATH,
"//*[@text='手动输入添加']").click()
# 定位姓名输入框
self.driver.find_element(
AppiumBy.XPATH,
"//*[contains(@text, '姓名')]/../*[@text='必填']").send_keys(self.name)
# 定位手机号输入框
self.driver.find_element(
AppiumBy.XPATH,
"//*[contains(@text, '手机')]/..//*[@text='必填']").send_keys(self.phonenum)
# 点击保存按钮
self.driver.find_element(
AppiumBy.XPATH,
"//*[@text='保存']").click()
tips = self.driver.find_element(
AppiumBy.XPATH,
"//*[@class='android.widget.Toast']").text
assert tips == "添加成功"
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):
# 正常通过文本查找元素
ele = self.driver.find_element(AppiumBy.XPATH, f"//*[@text='{text}']")
print("找到元素")
# 能找到则把隐式等待恢复原来的时间
self.driver.implicitly_wait(15)
# 返回找到的元素对象
return ele
except Exception:
# 当查找元素发生异常时
print(f"没有找到元素,开始滑动")
print(f"滑动第{num + 1}次")
# 滑动操作
self.swipe_window()
# 把隐式等待恢复原来的时间
self.driver.implicitly_wait(15)
# 抛出找不到元素的异常
raise NoSuchElementException(f"滑动之后,未找到 {text} 元素")
Java 实现
public class TestMember {
public static AndroidDriver driver;
@BeforeAll
public static void setUpClass() {
//配置信息
UiAutomator2Options uiAutomator2Options = new UiAutomator2Options()
.setPlatformName("Android")
.setAutomationName("uiautomator2")
.setNoReset(true)
.amend("appium:appPackage", "com.tencent.wework")
.amend("appium:appActivity", ".launch.LaunchSplashActivity")
.amend("appium:forceAppLaunch", true)
.amend("appium:shouldTerminateApp", true);
//初始化
try {
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);
@AfterAll
public static void tearDownClass(){
driver.quit();
@ParameterizedTest
@CsvSource({"李四, 13188890000"})
public void addMember(String name, String phone) throws InterruptedException {
driver.findElement(AppiumBy.xpath("//*[@text='通讯录']")).click();
for(int i =1; i<10; i++) {
try {
driver.findElement(AppiumBy.xpath("//*[@text='添加成员']")).click();
break;
}catch (Exception e) {
scrollDown();
driver.findElement(AppiumBy.xpath("//*[@text='手动输入添加']")).click();
driver.findElement(AppiumBy.xpath("//*[@text='姓名']//../*[@class='android.widget.EditText']")).sendKeys(name);
driver.findElement(AppiumBy.xpath("//*[@text='手机']/..//*[@class='android.widget.EditText']")).sendKeys(phone);
driver.findElement(AppiumBy.xpath("//*[@text='保存']")).click();
String toastMessage = driver.findElement(AppiumBy.xpath("//*[@class='android.widget.Toast']")).getText();
driver.navigate().back();
driver.findElement(AppiumBy.xpath("//*[@text='Hufflepuff']/../../../../*[3]/*[1]")).click();
driver.findElement(AppiumBy.xpath("//*[@text='搜索']")).sendKeys(name);
List<WebElement> webElements = driver.findElements(AppiumBy.xpath("//*[@class='androidx.recyclerview.widget.RecyclerView']//*[@class='android.widget.LinearLayout']//*[@class='android.widget.TextView']"));
// 1. 遍历所有元素 2. 获取所有元素对应的文本 3. 判断是否包含张三 4. 返回判断结果,如果所有元素都包含,则返回true
assertTrue(webElements.stream().allMatch(item -> item.getText().contains("李四")));
assertEquals("添加成功", toastMessage);
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);
touchAction.longPress(ElementOption.point(startX, startY))
.moveTo(ElementOption.point(startX, endY))
.release()
.perform();
实战 3:用例优化
用例执行过程中添加截图。
将截图保存到 allure 报告中。
Python 实现
import os
import time
import allure
from appium import webdriver
from appium.webdriver.common.appiumby import AppiumBy
from appium.options.common import AppiumOptions
root_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@allure.feature("企业微信联系人操作")
class TestWithShot:
def setup(self):
# Capability 设置定义为字典
caps = {}
# 设置 app 安装的平台(Android、iOS)
caps["platformName"] = "Android"
# 设置 app 安装平台的版本
caps["appium:platformVersion"] = "6"
# 设备的名字
caps["appium:deviceName"] = "MuMu"
# 设置 app 的包名
caps["appium:appPackage"] = "com.tencent.wework"
# 设置 app 启动页
caps["appium:appActivity"] = ".launch.LaunchSplashActivity"
# 不清空缓存
caps["appium:noReset"] = True
# app 不重启
# caps["appium:dontStopAppOnReset"] = True
# 实例化 AppiumOptions 对象
options = AppiumOptions().load_capabilities(caps)
# 初始化 driver
self.driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", options=options)
# 设置全局的隐式等待
self.driver.implicitly_wait(10)
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
@allure.story("搜索联系人")
def test_search_contact(self):
冒烟用例,搜索存在的联系人
search_key = "蚊子"
# 点击通讯录按钮
with allure.step("点击通讯录按钮"):
image_path = self.screenshot()
self.driver.find_element(
AppiumBy.XPATH,
"//*[@text='通讯录']").click()
allure.attach.file(image_path, name="点击通讯录按钮", attachment_type=allure.attachment_type.PNG)
# 点击搜索按钮
with allure.step("点击搜索按钮"):
image_path = self.screenshot()
self.driver.find_element(
AppiumBy.XPATH,
'//*[@text="悠然科技"]/../../../'
'following-sibling::*/*[1]').click()
allure.attach.file(image_path, name="点击搜索按钮", attachment_type=allure.attachment_type.PNG)
# 查找搜索框,输入搜索关键词
with allure.step("输入搜索关键词"):
self.driver.find_element(
AppiumBy.XPATH,
"//*[@text='搜索']").send_keys(search_key)
image_path = self.screenshot()
allure.attach.file(image_path, name="输入搜索关键词", attachment_type=allure.attachment_type.PNG)
# 获取搜索结果元素的文本,完成断言
with allure.step("获取搜索结果元素的文本,完成断言"):
eles = self.driver.find_elements(
AppiumBy.XPATH,
'//*[@class="android.widget.ListView"]//*[@class="android.widget.ImageView"]/../../following-sibling::*/*[1]/*[1]')
image_path = self.screenshot()
allure.attach.file(image_path, name="获取搜索结果元素的文本,完成断言",
attachment_type=allure.attachment_type.PNG)
assert search_key in [ele.text for ele in eles]
Java 实现
import io.appium.java_client.AppiumBy;
import io.appium.java_client.TouchAction;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.options.UiAutomator2Options;
import io.appium.java_client.touch.offset.ElementOption;
import io.qameta.allure.Allure;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebElement;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.time.Duration;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class TestMember {
public static AndroidDriver driver;
@BeforeAll
public static void setUpClass() {
//配置信息
UiAutomator2Options uiAutomator2Options = new UiAutomator2Options()
.setPlatformName("Android")
.setAutomationName("uiautomator2")
.setNoReset(true)
.amend("appium:appPackage", "com.tencent.wework")
.amend("appium:appActivity", ".launch.LaunchSplashActivity")
.amend("appium:forceAppLaunch", true)
.amend("appium:shouldTerminateApp", true);
//初始化
try {
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);
@AfterAll
public static void tearDownClass(){
driver.quit();
private void ElementScreenBase(String message) throws IOException {
File screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
Allure.addAttachment(message, "image/png", new FileInputStream(screenshot), ".png");
* 前置条件: 登录成功
* 1. 进入通讯录页面
* 2. 点击搜索按钮,输入一个已经存在的成员名称。
@Test
public void searchMember() throws IOException {
driver.findElement(AppiumBy.xpath("//*[@text='通讯录']")).click();
driver.findElement(AppiumBy.xpath("//*[@text='Hufflepuff']/../../../../*[3]/*[1]")).click();
driver.findElement(AppiumBy.xpath("//*[@text='搜索']")).sendKeys("张三");
List<WebElement> webElements = driver.findElements(AppiumBy.xpath("//*[@class='androidx.recyclerview.widget.RecyclerView']//*[@class='android.widget.LinearLayout']//*[@class='android.widget.TextView']"));
// 1. 遍历所有元素 2. 获取所有元素对应的文本 3. 判断是否包含张三 4. 返回判断结果,如果所有元素都包含,则返回true
// 到最后一个页面进行截图
ElementScreenBase("查找成员");
assertTrue(webElements.stream().allMatch(item -> item.getText().contains("张三")));
编写添加成员成功和失败的测试用例,具体参考测试用例-添加联系人测试场景。
测试结果需添加截图到报告中。
观察 Appium Server/Desktop 的日志信息,了解 Appium 相关的通信原理。
环境安装与配置
Appium 环境安装
emulator 安装应用方法
模拟器控制