借鉴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/

目录
相关文章
|
26天前
|
开发框架 JavaScript 前端开发
震撼!破解 ASP.NET 服务器控件 Button 执行顺序之谜,颠覆你的开发认知!
【8月更文挑战第16天】在ASP.NET开发中,通过Button控件实现先执行JavaScript再触后台处理的需求十分常见。例如,在用户点击按钮前需前端验证或提示,确保操作无误后再传递数据至后台深度处理。此过程可通过设置Button的`OnClientClick`属性调用自定义JavaScript函数完成验证;若验证通过,则继续触发后台事件。此外,结合jQuery也能达到相同效果,利用`__doPostBack`手动触发服务器端事件。这种方式增强了应用的交互性和用户体验。
34 8
|
3天前
|
人工智能 测试技术 PyTorch
AI计算机视觉笔记二十四:YOLOP 训练+测试+模型评估
本文介绍了通过正点原子的ATK-3568了解并实现YOLOP(You Only Look Once for Panoptic Driving Perception)的过程,包括训练、测试、转换为ONNX格式及在ONNX Runtime上的部署。YOLOP由华中科技大学团队于2021年发布,可在Jetson TX2上达到23FPS,实现了目标检测、可行驶区域分割和车道线检测的多任务学习。文章详细记录了环境搭建、训练数据准备、模型转换和测试等步骤,并解决了ONNX转换过程中的问题。
|
9天前
|
开发框架 JavaScript 前端开发
|
20天前
|
前端开发 测试技术 UED
【测试效率对比】深入分析:为何UI自动化测试的投资回报率通常低于接口自动化测试?
这篇文章深入分析了UI自动化测试与接口自动化测试的投资回报率(ROI)问题,指出UI自动化测试在某些情况下的ROI并不低,反驳了没有实施过UI自动化就轻易下结论的观点,并强调了实践的重要性和自动化测试在项目迭代中的作用。
40 1
|
1月前
|
机器学习/深度学习 人工智能
高于临床测试3倍准确率!剑桥大学开发AI模型,提前6年预测阿尔茨海默症
【8月更文挑战第9天】剑桥大学研发的人工智能模型在预测阿尔茨海默症方面取得突破,准确率比传统临床测试高三倍,能提前六年预测疾病发生。该模型基于深度学习,利用大量临床及神经影像数据识别生物标志物,预测准确性达80%。这一成果有望促进早期干预,改善患者预后,但仍需更大规模研究验证,并解决隐私与公平性等问题。论文已发表于《The Lancet》子刊。
35 6
|
2月前
|
机器学习/深度学习 存储 数据可视化
谷歌的时间序列预测的基础模型TimesFM详解和对比测试
在本文中,我们将介绍模型架构、训练,并进行实际预测案例研究。将对TimesFM的预测能力进行分析,并将该模型与统计和机器学习模型进行对比。
82 2
|
10天前
|
测试技术 Swift iOS开发
探索iOS自动化测试:使用Swift编写UI测试
【8月更文挑战第31天】在软件开发的海洋中,自动化测试是保证船只不偏离航线的灯塔。本文将带领读者启航,深入探索iOS应用的自动化UI测试。我们将通过Swift语言,点亮代码的灯塔,照亮测试的道路。文章不仅会展示如何搭建测试环境,还会提供实用的代码示例,让理论知识在实践中生根发芽。无论你是新手还是有经验的开发者,这篇文章都将是你技能提升之旅的宝贵指南。
|
15天前
|
机器学习/深度学习 数据采集 测试技术
利用Python实现简单的机器学习模型软件测试的艺术与科学:探索自动化测试框架的奥秘
【8月更文挑战第27天】在本文中,我们将一起探索如何通过Python编程语言创建一个简单的机器学习模型。我们将使用scikit-learn库中的线性回归模型作为示例,并通过一个实际的数据集来训练我们的模型。文章将详细解释每一步的过程,包括数据预处理、模型训练和预测结果的评估。最后,我们会用代码块展示整个过程,确保读者能够跟随步骤实践并理解每个阶段的重要性。
|
4月前
|
机器学习/深度学习 人工智能 自然语言处理
2024年5月大语言模型论文推荐:模型优化、缩放到推理、基准测试和增强性能
本文汇总了2024年5月发布的七篇重要大语言模型论文,涉及模型优化、缩放、推理及性能增强。
147 2
|
3月前
|
Dart Serverless Android开发
Flutter 单线程模型保证UI运行流畅
Flutter 单线程模型保证UI运行流畅
36 0
下一篇
DDNS