如何用Appium将RecyclerView所有项存入ArrayList并验证安卓排序筛选功能
解决Appium中RecyclerView懒加载下的Sort&Filter自动化验证问题
我之前在电商App的自动化测试项目里,完全碰到过和你一模一样的痛点——RecyclerView懒加载导致只能获取可见项,滚动后索引乱跳,根本没法直接拿到所有结果来验证排序和筛选。下面是我实际项目里用过的几个靠谱方案,附示例代码,你可以参考:
方案一:分段滚动+局部验证+趋势检查(无需全量数据)
如果不需要严格验证每一项,这个方案最省时间:
- 验证筛选有效性:每次滚动后,检查当前可见的所有项是否都符合筛选条件(比如分类、价格区间)
- 验证排序趋势:比如按价格升序,就检查当前批次的最后一项价格 ≤ 下一批次的第一项价格,保证整体趋势正确
- 辅助验证:同时检查页面上的排序/筛选标签是否正确,以及结果总数是否和接口返回一致(如果能调接口的话)
示例代码片段(Python):
def verify_sort_trend_and_filter(): # ... 初始化driver、搜索、排序筛选操作省略 ... last_price = -1.0 while True: current_items = get_visible_items(driver) # 验证当前可见项符合筛选条件 for item in current_items: assert item["category"] == "智能", f"商品{item['name']}不符合筛选" # 验证排序趋势 if last_price != -1.0: assert item["price"] >= last_price, f"排序异常:{last_price} > {item['price']}" last_price = item["price"] # 尝试滚动,判断是否到底部 screen_size = driver.get_window_size() start_y = screen_size['height'] * 3//4 end_y = screen_size['height']//4 driver.swipe(0, start_y, 0, end_y, 500) time.sleep(1) # 检查是否还有新项(比如判断当前可见项的第一个是否和上一次的最后一个重复) new_first_item = get_visible_items(driver)[0] if new_first_item["name"] == current_items[-1]["name"]: break
方案二:滚动收集+去重缓存(获取全量数据验证)
如果必须验证每一项,那就通过滚动分批获取可见项,用唯一标识去重后缓存,直到滚动到底部:
- 核心是不依赖元素索引,而是用商品ID、唯一名称等标识判断是否已经收集过
- 滚动时要处理加载状态,避免漏项
完整示例代码(Python):
from appium import webdriver import time def get_visible_items(driver): """获取当前可见商品的详细信息(用商品ID作为唯一标识)""" items = driver.find_elements_by_id("com.yourapp:id/layout_product_item") item_list = [] for item in items: prod_id = item.get_attribute("data-prod-id") # 假设元素有唯一ID属性 price = float(item.find_element_by_id("com.yourapp:id/tv_price").text.replace("¥", "")) category = item.find_element_by_id("com.yourapp:id/tv_category").text item_list.append({ "id": prod_id, "price": price, "category": category }) return item_list def scroll_up(driver): """模拟向上滚动,触发RecyclerView加载更多""" screen = driver.get_window_size() driver.swipe(screen['width']//2, screen['height']*3//4, screen['width']//2, screen['height']//4, 600) time.sleep(1.5) # 等待加载完成 def verify_full_sort_filter(): # 初始化driver desired_caps = { "platformName": "Android", "deviceName": "Pixel_5", "appPackage": "com.yourshop.app", "appActivity": ".search.SearchResultActivity", "automationName": "UiAutomator2" } driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps) # 1. 执行搜索、排序筛选 driver.find_element_by_id("com.yourshop.app:id/et_search").send_keys("无线耳机") driver.find_element_by_id("com.yourshop.app:id/btn_search").click() time.sleep(2) # 选择价格升序 driver.find_element_by_id("com.yourshop.app:id/tv_sort").click() driver.find_element_by_xpath("//android.widget.TextView[@text='价格从低到高']").click() # 筛选"降噪"分类 driver.find_element_by_id("com.yourshop.app:id/tv_filter").click() driver.find_element_by_xpath("//android.widget.CheckBox[@text='降噪']").click() driver.find_element_by_id("com.yourshop.app:id/btn_confirm").click() time.sleep(2) # 2. 滚动收集所有项(去重) all_prods = {} # 用ID做key,避免重复 prev_item_count = 0 while True: current_items = get_visible_items(driver) for item in current_items: if item["id"] not in all_prods: all_prods[item["id"]] = item # 如果没有新增项,说明已滚动到底部 if len(all_prods) == prev_item_count: break prev_item_count = len(all_prods) scroll_up(driver) # 3. 验证筛选条件 for prod in all_prods.values(): assert prod["category"] == "降噪", f"商品ID {prod['id']}不符合筛选条件" # 4. 验证排序(价格升序) sorted_prices = sorted([p["price"] for p in all_prods.values()]) actual_prices = [p["price"] for p in all_prods.values()] assert actual_prices == sorted_prices, "排序结果不符合预期" driver.quit() if __name__ == "__main__": verify_full_sort_filter()
方案三:接口+UI结合验证(高效且稳定)
这是我最喜欢的方案,兼顾效率和准确性:
- 先调用后端的搜索、排序筛选接口,拿到全量的预期数据
- UI层只需要验证前几项、后几项和接口返回一致,同时验证页面上的排序/筛选标签正确
- 完全避开RecyclerView懒加载的问题,测试速度快很多
示例代码片段:
import requests def get_expected_data(): """调用后端接口获取预期的排序筛选结果""" api_url = "https://api.yourshop.com/v1/products/search" params = { "keyword": "无线耳机", "sort_type": "price_asc", "filter_category": "降噪" } resp = requests.get(api_url, params=params) return resp.json()["products"] # UI验证部分 expected_data = get_expected_data() # 验证前3项 first_three_ui = get_visible_items(driver)[:3] for i in range(3): assert first_three_ui[i]["id"] == expected_data[i]["id"] assert first_three_ui[i]["price"] == expected_data[i]["price"] # 滚动到底部验证最后3项 scroll_up(driver) # 多次滚动直到底部 last_three_ui = get_visible_items(driver)[-3:] for i in range(3): assert last_three_ui[i]["id"] == expected_data[-3+i]["id"] assert last_three_ui[i]["price"] == expected_data[-3+i]["price"] # 验证页面筛选标签 assert driver.find_element_by_id("com.yourshop.app:id/tv_selected_filter").text == "降噪" assert driver.find_element_by_id("com.yourshop.app:id/tv_selected_sort").text == "价格从低到高"
一些注意事项
- 滚动时一定要加足够的等待时间,避免元素未加载完成就获取,导致漏项或错误
- 去重尽量用商品ID这种唯一标识,不要用名称,避免重名商品导致的错误
- 如果遇到加载更多按钮,要先点击按钮再滚动,否则可能无法加载后续数据
- 结合接口的方案要保证接口参数和UI操作的参数完全一致(比如排序类型、筛选条件)
内容的提问来源于stack exchange,提问作者shiva oleti
相关产品推荐
相关产品推荐

