借鉴ASP.NET的控件模型辅助UI自动化测试

简介:

概述

  在敏捷测试中UI的自动化测试(一般我们也称这层测试为功能测试或验收测试,本文单指Web UI的自动化测试)虽然没有单元测试那么广为提及,但因为其与最终用户最近,所以基于用户场景的UI自动化测试还是有其重要的意义的。使用UI自动化测试对产品的关键功能路径进行验证及回归,比起传统的QA手工执行Test case可以更快地得到反馈,也让发布变得更有信心。

   理想状况下,我们应该将所有可以固化下来的Test case都自动化起来,而让我们的测试人员进行更有挑战性的探索性测试活动。让机器做已知领域的事儿,让人对未知领域进行探索。不过理想归理想,现实是残 酷的。虽然UI层的测试距离交付最近,但是成本也最高。编写和维护UI自动化测试需要付出比其他自动化测试更高昂的成本,这也是大多数团队放弃UI自动化 测试的主要原因。相比较系统的其他部分,UI是一个多变的层,如果UI自动化测试没有构建好,即使界面的一个微小改动,整个测试集可能就天崩地裂。这也就 是为什么我经常对team里其他人说:对于UI自动化测试,可维护性必须牢记心头。每当你写下一行测试代码时,你就必须记住你又给公司添加了一笔成本,而 且这个成本是持续增长的,如果review code的时候发现哪条测试代码维护性不好我会毫不犹豫的删掉。

  或许有人觉得这有点小题大作,不就UI测试么,有什么难的。定位元素,然后拿到页面元素的值与期望进行比较不就可以了。难就难在定位元素上。一般我们会使用SeleniumWebDriver, Watir, Sahi等工具驱动浏览器,进行元素定位(关于这些工具的详细使用可以参见官方文档,后文主要以Selenium WebDriver为示例)。这些工具在定位元素上基本上是大同小异:通过id, name, css, tagName, xpath等方式定位。这些定位方式,从前到后,一个比一个不靠谱。比如这个xpath,好不容易写出个xpath定位,然后突然有一天前端觉得某个地方 不美观,插入一个小东西,马上测试废掉。看着这种没有改变功能也把功能测试搞垮掉的现象是不是欲哭无泪。我有时天真的在想如果页面上每一个元素都有唯一的 id该多好啊。即使没有唯一的id,有name我也可以接受。不过这一切在遇到ExtJS之后都变了。

  遭遇ExtJS

  ExtJS是一个非常霸道的前端框架。使用ExtJS后,页面上几乎所有的一切都被ExtJS接管。尽管互联网提 供给用户的系统鲜有使用ExtJS,但是对于后台系统使用ExtJS确实带来了一些便利。使用ExtJS的基本组件就能组装出一个看起来还不错,功能强大 的应用。但是ExtJS非常霸道,被他接管后页面的生成基本上就是个黑盒子,而为了在各个浏览器的兼容它在各浏览器上生成的html还不一样。更可恨的是 默认情况下它给元素提供的id都是动态生成的。

  在刚选择这个ExtJS的系统作为我们自动化测试的第一个试点时,我还有点暗暗高兴。比 起那些提供给普通用户使用的丰富多彩的前端来说,这些后台系统大多中规中矩,使用ExtJS后更是层次分明。而且后台系统UI的变动也不会太过于频繁,我 想或许这个系统很容易测试吧。

  后来我看到同事代码里出现:

webDriver.findElement(By.id("ext-gen-1306"))

   我还在想,我们的前端同学真有“创意”,还用这么随机的名字啊。后来厄运来了,我check out代码在我这里死活通过不了。Selenium报告找不到指定元素。不是吧,我可是使用id进行定位的啊。通过翻阅ExtJS的文档发现,原来类似 ext-gen-xxx这类id都是ExtJS动态生成的。好吧,我使用name进行定位吧,后来发现很多元素居然没有name属性。再来看看ExtJS 生成的html,基本上把通过xpath进行定位的路给堵死了。要了解ExtJS生成的html,可以去ExtJS官方查看一些Demo。

  曙光

   阅读ExtJS文档我们发现,ExtJS极其强调它的组件模型。而用ExtJS写的前端代码也呈现出很好的结构。因为之前曾从事过ASP.NET的开 发,我想是不是可以使用ASP.NET类似的方式先编写一些小控件类,这些类对ExtJS的基本组件进行包装。然后利用这些小控件类组装出一个个页面。这 样不仅能把单个元素的定位分散到单个控件类里,而且可以做到极大程度的复用。在传统的UI自动化测试中我们使用Page Object模式来封装一个个页面,但是对于ExtJS来讲页面的粒度还显得过大。如是模仿ASP.NET的控件模型,我创建了Control, Button, TextBox等一系列基本的控件类。而原来Page Object中的Page不再使用WebDriver直接定位元素了,我们通过这些基本控件组装页面。

  实现

  在这里我用一个简单的用户登录作为例子:

  Control是我们的基本类型,所有的控件包括页面都从这个类派生。

  Control只提供了很少几个方法:

public abstract class Control {
    protected WebDriver webDriver;


    protected Control parent;


    public Control(WebDriver webDriver) {
        this.webDriver = webDriver;
    }


    public String getQuery() {
        return StringUtils.EMPTY;
    }


    public String getId() {
        JavascriptExecutor executor = (JavascriptExecutor) webDriver;
        return (String) executor.executeScript("return " + this.getQuery() + ".id");
    }
}
 在这里getQuery是一个非常重要的方法,这在后面会介绍。

public abstract class CompositeControl extends Control {
    protected List <Control> children;


    public CompositeControl(WebDriver webDriver) {
        super(webDriver);
        children = new ArrayList<Control> ();
    }


    public void addChild(Control control) {
        this.children.add(control);
        control.parent = this;
    }
}

  所有的可以包含其他控件的类型都从CompositeControl派生,包括Page。比如下面的Window就是这类元素:

public class Window extends CompositeControl {
    private String title;


    public Window(String title,WebDriver webDriver) {
        super(webDriver);
        this.title = title;
    }


    @Override
    public String getQuery(){
        return String.format("Ext.ComponentQuery.query(\"window[title='%s']\")[0]",title);
    }
}

  下面是一个基本控件Button的封装:

public class Button extends Control {
    private String text;


    public Button(String text, WebDriver webDriver) {
        super(webDriver);
        this.text = text;
    }


    @Override
    public String getQuery() {
        return this.parent.getQuery() + String.format(".query(\"button[text='%s']\")[0]", text);
    }


    public void click() {
        webDriver.findElement(By.id(getId())).click();
    }
}

  ExtJS提供了一个query接口,我们可以利用这个接口传入一些查询表达式查询到页面上的Ext控件,而这里的getQuery就是每个控件的查询表达式吧。因为页面上的ExtJS控件是层次的,所以我们可以利用这种嵌套关系进行精确的定位。

  好了,来看看我们的登陆页面如何封装吧:

public class LoginPage extends ExtJSPage{
     public LoginPage(WebDriver webDriver){
          super(webDriver);
     }


     private TextBox txtUserName;
     private TextBox txtPassword;
     private Button btnLogin;
     
     @Override
     protected void init(){
          txtUserName = new TextBox("userName", webDriver);
          txtPassword = new TextBox("password", webDriver);
          btnLogin = new Button("登录", webDriver);


          Window win = new Window("登陆", webDriver);
          win.addChild(txtUserName);
          win.addChild(txtPassword);
          win.addChild(btnLogin);


          this.addChild(win);
     }


     public void login(String userName, String password){
          txtUserName.setValue(userName);
          txtPassword.setValue(password);
          btnLogin.click();
     }
}

  上面的TextBox和ExtJSPage没有提供代码,都很简单可以自行进行封装一下(熟悉ASP.NET的同学可能对这里代码有点眼熟)。

  按照这种思路,只要我们封装好所有的基本ExtJS控件,对于所有的页面我们剩下的工作就是组装的工作了。在完成这些之后,我甚至发现使用 ExtJS的应用比那些没有使用ExtJS的应用更容易进行测试。在这里我们只需要完善我们的基本控件封装就可以让我们的测试更佳稳固,而对于编写测试的 人来说只需要集中精力关注Test case。








====================================分割线================================



最新内容请见作者的GitHub页:http://qaseven.github.io/

目录
相关文章
|
3月前
|
测试技术 持续交付 API
深入挖掘探索.NET单元测试
【10月更文挑战第11天】
48 2
|
10天前
|
数据采集 人工智能 自然语言处理
Midscene.js:AI 驱动的 UI 自动化测试框架,支持自然语言交互,生成可视化报告
Midscene.js 是一款基于 AI 技术的 UI 自动化测试框架,通过自然语言交互简化测试流程,支持动作执行、数据查询和页面断言,提供可视化报告,适用于多种应用场景。
103 1
Midscene.js:AI 驱动的 UI 自动化测试框架,支持自然语言交互,生成可视化报告
|
1天前
|
存储 测试技术 API
pytest接口自动化测试框架搭建
通过上述步骤,我们成功搭建了一个基于 `pytest`的接口自动化测试框架。这个框架具备良好的扩展性和可维护性,能够高效地管理和执行API测试。通过封装HTTP请求逻辑、使用 `conftest.py`定义共享资源和前置条件,并利用 `pytest.ini`进行配置管理,可以大幅提高测试的自动化程度和执行效率。希望本文能为您的测试工作提供实用的指导和帮助。
31 15
|
8天前
|
算法 Java 测试技术
使用 BenchmarkDotNet 对 .NET 代码进行性能基准测试
使用 BenchmarkDotNet 对 .NET 代码进行性能基准测试
40 13
|
29天前
|
算法 Java 测试技术
Benchmark.NET:让 C# 测试程序性能变得既酷又简单
Benchmark.NET是一款专为 .NET 平台设计的性能基准测试框架,它可以帮助你测量代码的执行时间、内存使用情况等性能指标。它就像是你代码的 "健身教练",帮助你找到瓶颈,优化性能,让你的应用跑得更快、更稳!希望这个小教程能让你在追求高性能的路上越走越远,享受编程带来的无限乐趣!
86 13
|
2月前
|
Java 测试技术 持续交付
【入门思路】基于Python+Unittest+Appium+Excel+BeautifulReport的App/移动端UI自动化测试框架搭建思路
本文重点讲解如何搭建App自动化测试框架的思路,而非完整源码。主要内容包括实现目的、框架设计、环境依赖和框架的主要组成部分。适用于初学者,旨在帮助其快速掌握App自动化测试的基本技能。文中详细介绍了从需求分析到技术栈选择,再到具体模块的封装与实现,包括登录、截图、日志、测试报告和邮件服务等。同时提供了运行效果的展示,便于理解和实践。
123 4
【入门思路】基于Python+Unittest+Appium+Excel+BeautifulReport的App/移动端UI自动化测试框架搭建思路
|
1月前
|
前端开发 JavaScript 测试技术
前端自动化测试
前端自动化测试是通过使用工具和脚本自动执行测试用例的过程,旨在提高测试效率、减少人为错误,并确保Web应用的功能在不同环境和设备上的一致性与稳定性。
|
1月前
|
敏捷开发 测试技术 持续交付
自动化测试之美:从零开始搭建你的Python测试框架
在软件开发的马拉松赛道上,自动化测试是那个能让你保持节奏、避免跌宕起伏的神奇小助手。本文将带你走进自动化测试的世界,用Python这把钥匙,解锁高效、可靠的测试框架之门。你将学会如何步步为营,构建属于自己的测试庇护所,让代码质量成为晨跑时清新的空气,而不是雾霾中的忧虑。让我们一起摆脱手动测试的繁琐枷锁,拥抱自动化带来的自由吧!
|
2月前
|
机器学习/深度学习 SQL 安全
如何确保自动化安全测试的全面性和准确性
确保自动化安全测试的全面性和准确性,需集成多种工具(如SAST、DAST、IAST、SCA),编写自动化测试脚本,融入CI/CD流程,定期更新测试用例和工具,使用真实数据,持续维护代码,模拟攻击场景,运用机器学习,采用智能测试平台,并结合手动测试。这些策略有助于及时发现并修复安全问题,提升软件安全性。
|
2月前
|
安全 前端开发 测试技术
如何选择合适的自动化安全测试工具
选择合适的自动化安全测试工具需考虑多个因素,包括项目需求、测试目标、系统类型和技术栈,工具的功能特性、市场评价、成本和许可,以及集成性、误报率、社区支持、易用性和安全性。综合评估这些因素,可确保所选工具满足项目需求和团队能力。