在Web自动化测试教程中,如何定位元素是老生常谈了。大多初学者都能说出UI自动化定位有哪八大定位方式。然后一般教程也会告诉大家这八大定位方式的区别以及适用范围。这看似简单,但是在实际的自动化测试工作中,我们会发现,页面的HTML结构可能非常复杂,而且想要定位的元素经常不存在可以一下子就唯一确定的id或者属性,那就只能选择最灵活的Xpath或者CSS选择器了。这两者当中,我个人更加偏好于XPATH,因为XPATH天生就适合处理HTML文档DOM树的层次结构,写出来的路径表达式可读性很好,而且也提供了丰富的文本匹配功能。但是写好一个Xpath路径并不是看起来那么简单,写得好不好其实很大程度上影响了自动化测试代码的稳定性。
以下我以一个实战案例为例,浅谈一下我写XPATH元素定位路径的个人经验:
案例参考的页面是ElementUI的官方文档组件库的页面(https://element.eleme.cn/#/zh-CN/component/date-picker)。我们来看一下如何定位界面上的一个日期框:

鼠标右键菜单打开Chrome开发者工具,看一下这个HTML元素是什么样的:
可以看到,这个由ElementUI封装的日期框本质是一个HTML的<input>元素。可惜这个元素的没有ID,那就不能用ID便捷地定位这个元素了。那能不能用元素属性?不行,这个页面上有多个日期框,其HTML标签和属性都一模一样。那就来试试XPATH吧。我们用Chrome自带的自动生成XPATH的功能看看生成的XPATH是什么样的:
//*[@id="app"]/div[2]/div/div[1]/div/div/div[2]/section/div[1]/div[1]/div/div[1]/div/input
这个自动生成的XPATH我个人觉得写得很不好。这个路径很长很长,从根节点到末位节点跨越了很多的中间节点,而且中间节点之间是位置关系是通过单斜杠/来确定的绝对位置而不是相对位置。这样的缺点就是,一旦页面发生变化,这个XPATH很可能就失效了。那我们就自己写XPATH。问题来了,自己怎么写好一个既能唯一定位目标元素又不过多受到页面结构影响的XPATH呢?
我个人的经验是:要从理解需求和业务的角度出发,思考这个元素在页面上所处的相对位置,然后写出一个有意义的XPATH
什么是从需求和业务的角度出发?比如,在这个例子中,我会思考:我定位的这个日期控件是干什么用的?这个页面是ElementUI的官方文档组件库的页面,这个页面上提供了很多的日期控件,而我要定位的这个日期控件类型是“以「日」为基本单位,基础的日期选择控件”,而且是“默认”的日期控件。这一点想到了,那么我就能想到通过页面上对于控件的说明文本来辅助定位这个元素。
我们来看一下两个文案和我们的目标元素的相对位置:

如图,红框中是两个辅助定位的文案元素。我们可以先找到第一个红框元素,然后通过XPATH轴找到他的第一个兄弟节点(黄框圈出的),然后在这个节点的子节点中通过相对路径找到第二个红框元素,在这个元素的第一个兄弟节点<div>的子节点中找到我们需要的<input>元素。完整的XPATH写出来是这样的:
//*[text()="以「日」为基本单位,基础的日期选择控件"]/following-sibling::*[1]//*[text()="默认"]/following-sibling::*[1]//input
这个XPATH看起来也有点长,但可读性是很好的。它反映了这样一个意义: 在“以「日」为基本单位,基础的日期选择控件”这个栏目中,找到文案为“默认”的<input>
如果你觉得这样还不够好,想到:万一这个页面的文案变了,那这个XPATH不也失效了吗?比如,产品经理要求把"以「日」为基本单位,基础的日期选择控件"改成"按照「日」选择的基础日期选择控件"怎么办?
那我们就基于对页面的功能和业务的理解,提取提示文案中一般不会变的关键词,用XPATH支持的contains方法改写一下:
//*[contains(text(),"「日」") and contains(text(),"日期选择控件")]/following-sibling::*[1]//*[contains(text(),"默认")]/following-sibling::*[1]//input
改完后别忘了在浏览器console中验证一下这个XPATH写的正不正确,能不能唯一定位一个元素:

这样写出来的元素定位,对于页面复杂结构的依赖性较小,受到页面结构变动的影响较小,所以也就能提高自动化测试的稳定性了。
















