第4章:元素定位基础
元素定位,说白了就是告诉自动化框架:「嘿,去点那个按钮!」。但小程序的环境比较特殊——它跑在微信的WebView里,DOM结构和普通网页不太一样。我刚开始做小程序自动化时,就吃过这个亏。
4.1 使用CSS选择器定位元素
CSS选择器是我个人最常用的定位方式。为什么?因为它快,而且写法简洁。小程序里大部分元素都能用CSS搞定。
核心原则:能用CSS就别用XPath。CSS选择器在性能上通常优于XPath,尤其在复杂页面中。
来看几个实战例子。假设我们有这样一个页面结构:
<view class="container">
<text class="title">商品列表</text>
<view class="item" data-id="1001">
<image class="thumb" src="..." />
<text class="name">iPhone 15</text>
<text class="price">¥6999</text>
</view>
</view>
常用的CSS选择器写法:
- 类选择器:
.title→ 定位到第一个class为title的元素 - 标签+类:
text.title→ 更精确,只匹配text标签下的title类 - 后代选择器:
.container .price→ 定位container下的price元素 - 属性选择器:
[data-id="1001"]→ 根据自定义属性定位 - 伪类选择器:
.item:first-child→ 第一个item
我在项目中遇到过一个问题:同一个class名出现在多个页面,导致定位到错误元素。后来我养成了一个习惯——优先用「父元素 + 子元素」的组合选择器,比如 .product-list .item .name,这样能大幅降低误命中率。
小技巧:在微信开发者工具的Console里输入 document.querySelectorAll('.your-selector'),可以快速验证选择器是否生效。我每次写定位前都会先跑一遍。
4.2 使用XPath定位元素
XPath虽然慢一点,但有些场景非它不可。比如:
- 需要根据文本内容定位(「点那个写着'提交订单'的按钮」)
- 需要定位父元素或兄弟元素
- CSS选择器搞不定的复杂层级
常用的XPath写法:
// 绝对路径(不推荐,太脆弱)
//view/view[2]/text[3]
// 相对路径 + 属性
//text[@class='price']
// 包含文本
//text[contains(text(), 'iPhone')]
// 根据索引
(//view[@class='item'])[1]
// 找父元素
//text[text()='¥6999']/..
嗯,这里要注意:XPath的索引从1开始,不是0。我第一次写XPath时踩过这个坑,死活定位不到第二个元素,后来才发现索引写成了0。
避坑指南:我曾经在某个项目中用XPath定位一个动态生成的弹窗,结果每次运行都报错。查了半天发现是弹窗的class名里带了随机数。后来改用 contains(@class, 'modal') 才搞定。记住——优先用部分匹配,别写死完整属性值。
什么时候用CSS,什么时候用XPath?我个人的经验法则:
| 场景 | 推荐方式 | 原因 |
|---|---|---|
| 通过class/id定位 | CSS | 简洁、性能好 |
| 通过文本内容定位 | XPath | CSS不支持文本匹配 |
| 定位父/兄弟元素 | XPath | CSS只能向下查找 |
| 简单层级(2-3层) | CSS | 可读性强 |
| 复杂层级(4层以上) | XPath | CSS写起来太啰嗦 |
4.3 元素属性的获取与断言
定位到元素只是第一步。你想想看,自动化测试的核心是什么?是验证。验证这个按钮是不是可点的,验证这个文本是不是预期的,验证这个图片有没有加载出来。
小程序里常用的属性获取方法:
// 获取文本内容
const text = await page.getText('.price')
// 返回: "¥6999"
// 获取属性值
const dataId = await page.getAttribute('.item', 'data-id')
// 返回: "1001"
// 获取样式
const color = await page.getStyle('.title', 'color')
// 返回: "rgba(0, 0, 0, 0.9)"
// 判断是否可见
const isVisible = await page.isVisible('.submit-btn')
// 返回: true/false
断言怎么写?我一般这样组织:
// 断言文本内容
const price = await page.getText('.price')
expect(price).toBe('¥6999')
// 断言元素存在
await expect(page.locator('.item')).toBeVisible()
// 断言属性值
const id = await page.getAttribute('.item', 'data-id')
expect(id).toBe('1001')
// 断言元素数量
const items = await page.locator('.item').count()
expect(items).toBeGreaterThan(0)
重要提醒:断言一定要有明确的预期值。我见过有人写 expect(element).toBeTruthy() 就完事了——这等于没测。你要验证的是「这个按钮显示'提交订单'」,而不是「这个按钮存在」。
说到属性获取,我记得有一次排查线上bug,发现某个按钮在某些机型上不显示。后来用 getStyle 获取了 display 属性,发现是条件渲染没处理好,导致按钮被设成了 display: none。你看,属性获取不仅能做断言,还能帮我们定位问题。
我的习惯:每个页面元素至少做3个断言——存在性断言、文本/属性断言、状态断言(可见/可点)。这样覆盖全面,漏测概率低。
最后说一句:定位和断言是自动化测试的基石。你想想看,如果连元素都定位不准,后面的操作全是白搭。所以,花时间把这块练扎实了,后面写用例会顺手很多。