目录
活用 java 注解和反射(python 中应该也有相关的机制)
前言
测试开发是软件开发中的一个重要领域,它涉及编写和维护自动化测试脚本、执行测试、生成报告等任务。在测试开发的过程中,可读性、可维护性和可扩展性是非常重要的考虑因素。
可读性是指测试代码的清晰度和易读性。一个具有良好可读性的测试代码可以让其他开发人员轻松理解和维护,减少错误和改动的风险。
可维护性是指测试代码的易于维护和修改。一个具有良好可维护性的测试代码可以快速适应需求变化,并且容易修复和调试。
可扩展性是指测试代码的易于扩展和重用。一个具有良好可扩展性的测试代码可以方便地添加新的测试用
测试框架与测试脚本的目标(部分)
- Tests as Documentation(你能很容易通过测试脚本理解被测软件的功能) ----可读性
- Defect Localization(通过测试脚本能够快速定位 bug 的位置) ----可读性与隔离性
- Tests should be easy to write and maintain(测试脚本应该是容易编写和维护的) ----可维护性
- Tests should be easy to improve when product changes(当产品变化时,测试应该是很容易扩展自身以适应变化的)----可扩展性
分层
为了提高我们测试脚本的质量, 分层显然是最常用的方法. 想象一下如果我们把根测试所有相关的东西都放在脚本里那是怎样的一种灾难,每次你去看脚本的时候都会一个头两个大。其一你不知道脚本在干嘛,其二你根本不敢随便动这个脚本。深怕动了哪里就破坏了这条脚本。所以当我们作了分层后,将责任划分出去,分而治之,每一层负责特定的功能,其他层不用担心这些特定的功能。
原则:
测试脚本只关注被测的功能逻辑,其他一切责任分层出去,或交给框架作,或交给其他模块作。
常用的分层方式:
- 数据驱动,把测试参数的构建分离出去,减少脚本复杂度
- 注册式数据管理,我们把测试数据的构建与销毁分离出去,减少脚本复杂度
- page object,UI 自动化常用的模式. 我看到的大家常用的方式就是把页面元素的定义分到单独的类中.下面来看看我曾经怎么做这个分层的.
脚本是这么写的:
driver.page("登陆页面").sendKeys("用户名输入框", "Admin").sendKeys("密码输入框", "1234567").click("登录按钮");
可以看到我定义了一个 page 的概念.一个页面所有的元素都在这个 page 里. 只要脚本中选定了某个 page,那么他就能随意控制页面操作. 那么 page object 在哪呢?脚本中我们看不到调用 page object 的操作,我们看不到你到底用 xpath 查找的元素还是用 id 还是用 name。请看下一段 xml 定义
<page name="登陆页面">
<Element eleName="用户名输入框" xpath="//input[@type='text']" id="userNameInput" ifBaseElement="true"></Element>
<Element eleName="密码输入框" xpath="//input[@type='password']" ifBaseElement="true"></Element>
<Element eleName="登录按钮" xpath="//button" ifBaseElement="true"></Element>
</page>
page object 在这里,这里面用中文定义了元素名称,以及控件元素到底是用什么方式去查找等信息。当脚本引用任何页面的时候,框架都会去缓存中读取此页面信息,并执行页面元素的控制操作。可以看到我们不仅把页面元素的定义分层出去,还把页面元素的查找过程也都分层出去了。 而且我们可以用自然语言定义控件的名字(英语还是汉语都可以),所以就像上面的代码一样,脚本在做什么一目了然。这就是可读性,我们做的事情跟之前没什么分别,但是我们把责任划分的更详细,脚本中只剩下业务逻辑。我们有一个原则就是脚本中只有业务逻辑。其他一切不相关的要不交给框架,要不交给其他层的模块。
使用类似 xml 这种可扩展性强的语义存储数据
我们看到上面的 xml 里还有一个 ifBaseElement 属性。 这个是什么呢? 它就是给这些页面元素打个标签,这些控件是属于页面基本元素,这样我们可以通过下面一段代码把所有带有这个标签的页面元素全找出来。
List<String> eleNames = driver.getBaseElementsNameOfPage("登陆页面");
for(String eleName:eleNames){
WebElement element = driver.findElement("登陆页面."+eleName+"");
Assert.assertNotNull(element);
}
看到效果了么? 这样我可以验证所有这些页面基本元素在页面中是存在的,这就是我们 UI 自动化策略中的静态元素验证。我们不用再一行一行去写代码验证了。而是通过 xml 这种方便扩展的定义遍历出所有的静态元素。这是一种方式,你也可以通过定义 xml 文件的属性扩展出很多功能。这是可扩展性。记得我的那篇数据驱动及其变种么?之后的关键字驱动框架就使用 xml 在数据驱动的基础上扩展而来的。同时 xml 是一种很清晰很结构化的定义方式。实际上 xml 本身的可读性就不低。可扩展性和可读性上去了,可维护性也就差不到哪去
代码复用:抽象一切可抽象的,减少一切可能的代码相似与重复
记住一点:代码越少越简单,维护起来就越方便。简单即是美
还是用 UI 自动化这个例子吧,我们看到上面讲 xml 可扩展性的时候。我们可以通过定义一个标签 ifBaseElement 来帮助做静态元素验证。但是 java 里普遍也就是用 dom4j 等工具遍历 xml 文件,你为 ifBaseElement 需要写一套遍历,你加另一个属性可能还要一套遍历。或者 xml 树结构改了,我们在已有的标签下又加了一套新的标签等情况。都需要重写遍历。而且一层又一层的 for 循环也挺让人崩溃的。外人不知道你这段代码在干吗。可维护性,可读性,可扩展性都差的要死。那我们一般怎么做呢。看下面一个例子。
注:有个方案是写迭代器(请 Google 迭代器模式),for 循环过多,而且复杂的时候一般使用此模式增加可维护性和可读性。不过在 xml 遍历的场景中,应变能力不强。xml 变化,迭代器也必须变化。所以我一般使用解释器模式遍历 xml 和 json
XMLParser.parser(pageObject, "page/Element$(ifBaseElement=true).eleName");
OK,大家看到了吧,一个解释器接收一个 string 和 xml 对象为参数。String 就是我们自定义的语法,上面的意思就是取出 page 节点下的 Element 节点中所有 ifBaseElement 属性值为 true 的 eleName 属性的值