Hướng dẫn nâng cao về Appium và công cụ kiểm thử tự động trên thiết bị di động

Sử dụng Monkey để kiểm thử ngẫu nhiên

Monkey là công cụ dòng lệnh giúp tạo hành vi người dùng ngẫu nhiên trên ứng dụng Android. Dưới đây là một số ví dụ cơ bản:

adb shell monkey -p com.lqr.wechat -v 500 > log1.txt
adb shell monkey -p com.lqr.wechat -vvv 5000 > log2.txt
adb shell monkey -p com.lqr.wechat -vvv 5000 --seed=1556166765229 --throttle 500 > log3.txt
adb shell monkey -p com.lqr.wechat -vvv 5000 --seed=1556166765229 --ignore-crashes > log4.txt

Các tham số phổ biến

  • -p: Chỉ định package ứng dụng
  • -v: Mức độ ghi log (có thể lặp lại để tăng chi tiết)
  • --seed: Thiết lập hạt giống để tái hiện kịch bản
  • --throttle: Thời gian chờ giữa các sự kiện (ms)
  • --ignore-crashes: Tiếp tục khi ứng dụng crash
  • --ignore-timeouts: Bỏ qua ANR (Application Not Responding)
  • --kill-process-after-error: Kết thúc tiến trình sau lỗi
  • --monitor-native-crashes: Theo dõi lỗi native

Tùy chỉnh tỷ lệ sự kiện

--pct-touch          # Sự kiện chạm (click)
--pct-motion         # Sự kiện vuốt kéo
--pct-trackball      # Di chuyển trackball
--pct-nav            # Điều hướng cơ bản (lên/xuống/trái/phải)
--pct-majornav       # Nút điều hướng chính (quay lại, menu, OK)
--pct-syskeys        # Phím hệ thống (Home, Back, Volume...)
--pct-appswitch      # Chuyển đổi ứng dụng
--pct-anyevent       # Sự kiện bất kỳ

Dừng Monkey thủ công

adb shell
ps | grep monkey
kill [PID]

Sử dụng Appium Inspector

Công cụ này hỗ trợ xác định phần tử giao diện và ghi lại thao tác tương tác.

  • Khởi động và cấu hình phiên làm việc
  • Xác định vị trí phần tử bằng nhiều phương pháp
  • Ghi lại chuỗi hành động để tái sử dụng

Định vị phần tử trong Appium

Có nhiều cách để truy vấn phần tử giao diện:

# Theo ID tài nguyên
element = driver.find_element(By.ID, "resource_id")

# Theo mô tả truy cập
element = driver.find_element(By.ACCESSIBILITY_ID, "description")

# Theo XPath
element = driver.find_element(By.XPATH, "//android.widget.TextView[@text='Login']")

# Tìm nhiều phần tử + chỉ mục
elements = driver.find_elements(By.CLASS_NAME, "android.widget.Button")
elements[1].click()

# Định vị lồng ghép
parent = driver.find_element(By.ID, "container")
child = parent.find_element(By.CLASS_NAME, "item")

Sử dụng UiSelector (Android UI Automator)

# Theo ID
driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,
    'new UiSelector().resourceId("com.example:id/button")')

# Theo nội dung văn bản
driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,
    'new UiSelector().text("Đăng nhập")')

# Văn bản chứa chuỗi con
driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,
    'new UiSelector().textContains("Nhập")')

# Nhiều điều kiện kết hợp
driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,
    'new UiSelector().className("android.widget.TextView").text("Tùy chỉnh")')

Thao tác với phần tử

element.click()                          # Nhấn vào phần tử
element.send_keys("nội dung")            # Nhập liệu
element.clear()                          # Xóa nội dung
element.is_displayed()                   # Kiểm tra hiển thị
element.get_attribute("text")            # Lấy thuộc tính
element.text                             # Lấy nội dung văn bản
element.location                         # Tọa độ phần tử
element.size                             # Kích thước phần tử

Ví dụ thực hành: Kiểm tra trạng thái đăng nhập WeChat

try:
    login_btn = driver.find_element(By.ID, "login_button")
    print("Tìm thấy nút đăng nhập")
    print(f"Text: {login_btn.text}")
    print(f"Enabled: {login_btn.is_enabled()}")
    login_btn.click()
    
    # Nhập thông tin
    username_field = driver.find_element(By.ID, "username")
    password_field = driver.find_element(By.ID, "password")
    username_field.send_keys("user123")
    password_field.send_keys("pass456")
    
    submit_btn = driver.find_element(By.ID, "submit_login")
    if submit_btn.is_enabled():
        submit_btn.click()
        try:
            contacts_tab = driver.find_element(By.XPATH, "//*[contains(@text, 'Liên hệ')]")
            print("Đăng nhập thành công!")
        except:
            print("Đăng nhập thất bại!")
    else:
        print("Nút đăng nhập không khả dụng")

except NoSuchElementException:
    print("Đã đăng nhập - Truy cập tab Liên hệ")
    contacts = driver.find_element(By.XPATH, "//*[contains(@text, 'Liên hệ')]")
    print(f"Resource ID: {contacts.get_attribute('resource-id')}")
    print(f"Class: {contacts.get_attribute('class-name')}")
    print(f"Có thể nhấn: {contacts.is_enabled()}")

finally:
    driver.quit()

Thao tác bàn phím và thiết bị

driver.press_keycode(4)   # Nút Back
driver.press_keycode(3)   # Nút Home
driver.press_keycode(24)  # Tăng âm lượng
driver.press_keycode(25)  # Giảm âm lượng

Bài tập: Đăng nhập → Quét mã QR → Chụp ảnh → Quay lại màn hình chính

# Đăng nhập
driver.find_element(By.ID, "username").send_keys("18010181267")
driver.find_element(By.ID, "password").send_keys("123456")
driver.find_element(By.ID, "login_btn").click()

# Vào trang cá nhân → Mã QR
driver.find_element(By.XPATH, "//*[contains(@text, 'Tôi')]").click()
driver.find_element(By.XPATH, "//*[contains(@text, 'Mã QR')]").click()

# Chụp màn hình
driver.save_screenshot("d:/screenshot/VCODE.png")

# Đẩy file vào thiết bị
driver.push_file("/sdcard/images/VCODE.png", "d:/screenshot/VCODE.png")

# Quay lại + tăng âm lượng + về home
driver.press_keycode(4)
driver.press_keycode(24)
driver.press_keycode(24)
driver.press_keycode(3)

Thao tác màn hình cảm ứng

# Lấy kích thước màn hình
screen = driver.get_window_size()
width = screen['width']
height = screen['height']

# Vuốt màn hình
def swipe_up():
    driver.swipe(width*0.5, height*0.8, width*0.5, height*0.2, 500)

def swipe_down():
    driver.swipe(width*0.5, height*0.2, width*0.5, height*0.8, 500)

def swipe_left():
    driver.swipe(width*0.8, height*0.5, width*0.2, height*0.5, 500)

def swipe_right():
    driver.swipe(width*0.2, height*0.5, width*0.8, height*0.5, 500)

Bài tập: Tìm mục "Giới thiệu" trong Cài đặt

driver.start_activity("com.android.settings", ".Settings")

found = False
while not found:
    try:
        about = driver.find_element(By.XPATH, "//*[contains(@text, 'Giới thiệu')]")
        about.click()
        found = True
    except:
        swipe_up()
        time.sleep(1)

# Lấy thông tin phiên bản
version = driver.find_element(By.XPATH, "//*[contains(@text, 'Phiên bản')]")
print(f"Phiên bản hệ thống: {version.text}")

driver.quit()

Quản lý ứng dụng và tệp tin

# Kiểm tra ứng dụng đã cài
if driver.is_app_installed("com.tencent.mm"):
    driver.remove_app("com.tencent.mm")
else:
    driver.install_app("/path/to/wechat.apk")

# Khởi chạy ứng dụng
driver.launch_app()
driver.start_activity("com.android.browser", ".BrowserActivity")

# Chuyển nền
driver.background_app(5)

# Lấy activity hiện tại
current = driver.current_activity
print(f"Activity hiện tại: {current}")

# Thao tác tệp tin
driver.pull_file("/sdcard/test.txt", "local_test.txt")
driver.push_file("/sdcard/upload.txt", "local_content.txt")

Trạng thái mạng

network = driver.network_connection
print(f"Trạng thái mạng: {network}")

# Các giá trị có thể:
# 0: Không có kết nối
# 1: Chỉ dữ liệu di động
# 2: Chỉ WiFi
# 4: Airplane mode
# 6: WiFi + Airplane

Tìm Activity chính của ứng dụng

adb shell
logcat | grep cmp=
# Sau đó mở ứng dụng trên thiết bị để xem log

Thẻ: appium Android-Automation UIAutomator Mobile-Testing ADB

Đăng vào ngày 2 tháng 6 lúc 04:05