Selenium Selenide 阶段性总结介绍

孙高飞 · 2016年08月14日 · 最后由 冬冬 回复于 2019年04月27日 · 3680 次阅读
本帖已被设为精华帖!

前言

最近工作上忙了点,回家也都在陪老婆再加上犯懒所以有几个星期没写帖子了。大家请原谅我哈~ 今天给大家介绍一个比较新的 UI 自动化测试工具-- Selenide。确实是比较新的,国内应该还没有多少人用它。在百度和 google 上你只能搜到一个中文帖子简单介绍了一下。如果你想用这个工具,不可避免的你要去阅读英文文档了。不过这年头写代码的有几个看不懂英文的。所以这都不是问题。引一个 git hub 上的链接和图片。
selenide

可以看到,使用的人不是很多但是有很多人在做贡献。而且有了固定的使用群体,相信以后会慢慢发展起来的。现在主要还是国外的同行们在使用它。

简单介绍

Selenide 的团队自诩它是一个测试工具而不是一个测试框架。因为它只是 webdriver 的一个封装,只是他们封装了更好用的 API,更稳定的控件搜索机制,更好的异常处理机制等等。底层的实现还是 webdriver。所以他们认为并没有伟大到开发了一个测试框架,而仅仅是个测试工具(很谦虚的说~)。所以一切 webdriver 能做的 selenide 都能做。webdriver 做不到的。。 嘿嘿。。你也别指望它能做到。 就像团队人员说的:selenide is just a wrapper. 如果你不想使用 selenide 了,或者 selenide 满足不了你的需要。你大可以通过以下方式获取一个 webdriver,直接操作 webdriver 的 api。 顺便一说,现在只有 java 版本。

driver = WebDriverRunner.getWebDriver();

所以如果有小伙伴担心这个新兴的工具没有强大到满足你的项目需求怎么办。你们可以放心了,最坏的情况也就是你直接用 webdriver 去做就好了。

Quick start

废话不多说直接搞。使用 maven 管理的话直接引入下面的依赖

<dependency>
    <groupId>com.codeborne</groupId>
    <artifactId>selenide</artifactId>
    <version>3.0</version>
    <scope>test</scope>
</dependency>

然后我们直接写一个脚本,就拿官方文档的例子吧

@Test
public void userCanLoginByUsername() {
   open("/login");
   $(By.name("username")).setValue("johny");
   $("#submit").click();
   $(".success-message").shouldHave(text("Hello, Johny!"));
}

Ok,我们的 demo 结束了。你问启动和关闭浏览器的代码哪去了?答案是木有啊。前面说的 selenide 致力于封装更简单好用的 API,所以这些东西它都帮你做好了。它默认使用 Firefox 浏览器。如果你要使用其他浏览器可以手动设置一下。如下:

Configuration.browser="chrome";
// 在环境变量中设置chrome 浏览器的路径
System.setProperty("webdriver.chrome.driver", Constant.getChromeDriverPath());

然后我们就可以不用关心浏览器的启动和销毁的问题了。是不是简化了一些了

webdriver 和 selenide 对比

我们一定很关心为什么我们不用 webdriver 而用 selenide 呢,我们下面就来一步一步对比一下。

创建 webdriver 的方式:

webdriver:

DesiredCapabilities desiredCapabilities = DesiredCapabilities.htmlUnit();
desiredCapabilities.setCapability(HtmlUnitDriver.INVALIDSELECTIONERROR, true);
desiredCapabilities.setCapability(HtmlUnitDriver.INVALIDXPATHERROR, false);
desiredCapabilities.setJavascriptEnabled(true);
WebDriver driver = new HtmlUnitDriver(desiredCapabilities);

selenide:

open("/my-application/login");

就像刚才的 demo 里一样。selenide 不需要这些代码。直接在 open 这个 api 里就启动浏览器了,当测试结束的时候,自然就会关闭浏览器

查询页面元素

webdriver:

WebElement customer = driver.findElement(By.id("customerContainer"));

selenide:

WebElement customer = $(By.id("customerContainer"));

这方面倒没有什么特别简化的,但是 selenide 有其他更灵活的方式搜寻控件,例如 byText,byValue 等等等等。这些在 webdriver 中除非用 xpath,否则你是做不到的。下面看看例子。
selenide:

WebElement customer = $(byText("Customer profile")); 
WebElement temp = $(byValue("不使用")).click();
WebElement temp1 = $(byAttribute("data-name",test name)).click();

再看看下面一个,如果返回多个元素,取其中一个的例子
webdriver:

driver.findElements(By.tagName("li")).get(5);

selenide:

$("li", 5);
$$("#multirowTable tr").filterBy(text("Norris"))

上面第一行代码是所有标签为li的元素中的第 5 个
第二行代码是取特定的集合后再去搜寻 text 为期望值的元素。
注:
$开头的是取一个元素
$$开头的是取一个集合
selenide 默认支持 css selector,所以代码看上去简洁了很多。

截图

关于失败截图我们都知道 webdriver 有个 api 可以做。但是 selenide 可以自动截图,默认保存在 build/report 里面

断言控件

webdriver:

assertEquals("Customer profile", driver.findElement(By.id("customerContainer")).getText());

selenide:

$("#customerContainer").shouldHave(text("Customer profile"));

selenide 提供一系列should标签帮我们做断言的工作,而且有一批 text() 的这种选择器来帮助我们断言各种类型。上面的例子就是断言控件是否有期望的 text。selenide 专门有一个 condition 包,里面有各种各样的 condition,这些 condition 就是 should 标签的参数。上面的例子 text 就是一个 condition。其他的还有 id,value,attribute,readonly 等等。

等待控件

有些时候为了增加稳定性,我们需要增加等待一个控件出现的机制。因为控件可能不是立刻出现的。或者说等待一个控件的某个属性变成一个特定值
webdriver:

FluentWait<By> fluentWait = new FluentWait<By>(By.tagName("TEXTAREA"));
fluentWait.pollingEvery(100, TimeUnit.MILLISECONDS);
fluentWait.withTimeout(1000, TimeUnit.MILLISECONDS);
fluentWait.until(new Predicate<By>() {
    public boolean apply(By by) {
        try {
            return browser.findElement(by).isDisplayed();
        } catch (NoSuchElementException ex) {
            return false;
        }
    }
});
assertEquals("John", browser.findElement(By.tagName("TEXTAREA")).getAttribute("value"));

selenide:

$("TEXTAREA").shouldHave(value("John"));

可以看到 selenide 还是一个should 的 api 搞定了。 它默认 4s 超时。4s 内会循环 check 控件的 value 是否变成了期望值。同样的还有 text,attribute 等选择器。

正则表达式

webdriver:

WebElement element = driver.findElement(By.id("customerContainer"));
assertTrue(Pattern.compile(".*profile.*", DOTALL).matcher(element.getText()).matches());

selenide:

$("#customerContainer").should(matchText("profile"));
关于 alert

webdriver:

try {
     Alert alert = checkAlertMessage(expectedConfirmationText);
     alert.accept();
   } catch (UnsupportedOperationException alertIsNotSupportedInHtmlUnit) {
     return;
   }
   Thread.sleep(200); // sometimes it will fail

selenide:

confirm("Are you sure to delete your profile?");

或者

dismiss("Are you sure to delete your profile?");
关于 log

webdriver:

WebElement element = driver.findElement(By.id("customerContainer"));
    System.out.println("tag: " + element.getTag());
    System.out.println("text: " + element.getText());
    System.out.println("id: " + element.getAttribute("id"));
    System.out.println("name: " + element.getAttribute("name"));
    System.out.println("class: " + element.getAttribute("class"));
    System.out.println("value: " + element.getAttribute("value"));
    System.out.println("visible: " + element.isDisplayed());

selenide:

System.out.println($("#customerContainer"));
// 输出的信息有点像这样: "<option value=livemail.ru checked=true selected:true>@livemail.ru</option>". 把元素的所有信息显示出来

还有很多不同,我就不一一列举了。 大家自己感受一下

selenide 的 API

其实从上面的一节我们已经认识了不少的 api 了,但是其实还有很多很好用的 api。更详细的内容,大家看一下官方文档:selenideAPI

题外话

UI 测试框架的选型

为什么我没用 robot framework, macaca 这些大家常用的测试框架?很多同学一定会比较这些框架。例如 robot framework 是关键字驱动,接口,UI 都可以。使用起来很方便,学习成本低。 macaca 支持各种平台封装的非常好等等。为啥你要用这么个冷门货。我告诉大家其实一开始我就想用原生的 webdriver 了。为啥呢?因为灵活,我用这么个原始的玩意我可以自己写好多自己的东西。那我为啥要写那么多自己的东西呢?
答案是我们这个蛋疼的 UI。来来来,我给大家 show 一下我们高大上的 UI。

我们的主要功能就是这个 DAG(有向无环图)管理的一系列任务。所有的任务都是异步执行并都在这个 DAG 中管理,我们前端的框架是 facebook 的 react 框架,我们前端的开发自己开发了特定组件。也就是说我们的 UI 上充斥了很多自定义控件,强大的 xpath 都识别不了的控件。而且很多控件无 ID,无 name,无 text,除了一个 tag name 基本啥也没有。看到图里的连接各个 node 的那小圆点了么?那就是这么个 3 无控件。你想自动化它就只能这样

public PlanPage linkSlot(int from, int to){
        actions().dragAndDrop($(By.tagName("circle"),from), $(By.tagName("circle"),to)).perform();
        return this;
    }

对,为了能自动化连接这俩小圆点,我只能数数了。还有任务是异步执行的对吧。你怎么等待任务成功或者失败呢?因为执行成功或者失败在 UI 上反应的就只有个对号或者错号。而这俩图片也特么的是那 3 无控件。除了 image 的 name 也几乎啥也没有,但是 image 的 name 还特么的是随机生成的。。。 而且一个 DAG 里这么多任务,要验证多少个对号错号?所以没办法逼的我只能写个函数等待数据库的状态变化和 yarn(hadoop 资源管理服务)上面的 job 的变化。例如:

public PlanPage waitForjob(AppFinalStatus status){
        YarnMonitor monitor = new YarnMonitor();
        App app = monitor.monitorAPPOnYarn(jobBeginTime);
        assertThat(app.getFinalStatus()).as("任务执行结果有误,请查看集群log,appID为:"+app.getId()).isEqualTo(AppFinalStatus.SUCCEEDED.getValue());
        return this;
    }


public PlanPage waitRunStatus(String projectName, RunStatus expectedStatus, int timeout){
        RunStatus status = WaitDBStatus.waitRun(projectName,timeout);
        assertThat(status).as("任务的状态错误,当前任务的状态为:"+status).isEqualTo(expectedStatus);
        return this;
    }

大家知道我的心酸了吧~~ 就连我们前端开发的 leader 都跟我说:实在太委屈你们了~。 这么蛋疼的 UI 一开始我都不想 UI 自动化了。但是后来觉得还是不行,因为我们是一个很重 UI 的产品,前端代码很多很复杂。你不自动化靠手动得要多少人。。。。好歹我得自动化回归个主流程吧。。由于配置复杂,传递给后台的 json 大的离谱,除了在 UI 上执行,根本没有一个好的办法生成那个 json。所以接口测试的方式去调用任务也是不靠谱的。因为你搞不出那个大 json 来。只能硬着头皮在 UI 上搞。所以碰到这种 UI。robot framework 和 macaca 都不行了。
也许有同学会问为啥控件里没 id 没 name 呢? 因为开发用的框架不需要那玩意,react 框架不需要 id 和 name 来帮开发定位,人家有自己的一套机制。所以人家当然也就不加了。。我也就是在有些控件实在没招的情况下才露胳膊挽袖子的跟开发说赶紧给我加个 id 或者 name 了。

关于测试框架

我总结的一条经验就是:切记不作死就不会死,装逼是作的表现会死的很惨很惨。有开源项目就用开源的千万别装逼自己写,你可以 2 次封装一下以适应你的项目但是千万别天真的以为你写的比人家的好。我学会 rest-assured 以后立马把以前封装的 http client 代码给扔了,学会 assertJ 以后我立马把以前封装的断言库扔了一大半就剩下那责任链了,学会 selenide 以后我立马把以前封装的 webdriver 组件给扔了,等到 testng 把自己的 dataloading 做完善了做牛逼了没准我写的数据驱动组件也得扔了,记住你自己写的代码就是用来仍的。对于我们这些非大神的人来说什么是框架?框架是就是把各种开源工具拼到一块然后自己写点代码封装一下,没开源框架的自己赶紧快速写一套对付用等找到成熟的框架就加进来把以前的代码仍进垃圾箱,这年头谁等你吭哧瘪肚的憋个把月的写个框架出来。等你把框架写完了没准项目都黄了。

总结

我这人就这么啰嗦大家见谅哈。

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 36 条回复 时间 点赞
ABEE ycwdaaaa (孙高飞) 在 TesterHome 的发帖整理 中提及了此贴 01月12日 13:47
38楼 已删除

selenide + jenkins 持续集成报错,
org.openqa.selenium.WebDriverException: Timed out waiting for driver server to start.
Build info: version: '3.141.59', revision: 'e82be7d358', time: '2018-11-14T08:17:03'
System info: host: 'cloudqa-ci-20170602', ip: '127.0.1.1', os.name: 'Linux', os.arch: 'amd64', os.version: '3.18.20-nce-amd64', java.version: '1.8.0_201'
Driver info: driver.version: SelenideDriver
selenide.url: https://www.163yun.com/price/calculation#CDN
selenide.baseUrl: http://localhost:8080

我只是参照文章设置了 chrome 启动
本地运行是 OK 的,放到 jeckins 上就失败了, 楼主有类似的经验吗

我又来挖坟了,selenide 官方网站进不去了,也没文档

selenide 支持 h5 页面吗?就是手机浏览器

学习了,Selenide 的接口确实比原生的 selenium 要好用,而且第一次学会了 import static 用法!

程明远 回复

这就是 selenide 的 python 版本~~~

老马 回复

这个我还真没用过~ 之后可以看看

在路上 回复

是吧~ 哈哈, 所以我一直安利这个框架

刚才试了一下,确实非常简单易用

发现了 selene 这个框架,感觉还不错https://github.com/yashaka/selene

在路上 回复

@ycwdaaaa 高大侠 这个怎么样 Taurus
http://gettaurus.org/
是 BlazeMeter 组织下的,支持 selenium appium 和各种性能扩展

孙高飞 回复

好嘞,谢谢

在路上 回复

推荐 selenide+docker gird 架构, 最好跑在 k8s 里。 自动化运维和执行效率杠杠的。 我再我们这 40 浏览器并发,速度飞起

孙高飞 回复

好的,我试试

在路上 回复

移动端的我没研究过了~~ 反正 web 端我觉得这玩意最好用了。听说 python 也支持 selenide 了

飞哥,你的覆盖面太广了,我找个好用的 web UI 自动化框架,也能找到你这里来。

还有其他的推荐吗?关于 web UI 自动化的框架

回复

欢迎~ selenide 是个非常好用的 UI 自动化框架,据说现在已经出了 python 版本的了。

线上班同学前来报到

Nisir 回复

额,一年半前的帖子有人来挖坟了哈哈哈。 我们一直用着呢。 还不错的感觉。

额,原来是 16 年的帖子。。

selenide,我们 2017 年初就开始用了,并进行了封装,一年下来确实还不错。我跟楼主测的项目有点类似,也是大数据相关的。另外我现在负责的就是后端组件 spark 的测试

韩将 回复

python 圈的福音啊

现在有 python 版的 selenide 了
https://github.com/yashaka/selene

看了你的文章之后,我也下了一个 selnide 试试。现在进行到 4.5.1 版本了,不知道这玩意为什么在驱动默认 firefox 的时候会出现问题,在 open(url)的时候报错,打开了浏览器但是没有输入 url,看报错应该是没有生成一个 server 所以导致报错,不知道这和 selenium3 要用 geckodriver.exe 有没有关系。
试了一下 chromedriver 还是可以用的,对了,里面 bytext 的方法目前变了哦,

$(Selectors.byText("LOG IN")).click();
总之感谢你的文章,告诉了我一个很好用的工具

新鸟膜拜中

挺有意思的语言,之前就遇到一个项目里面整个 web 架构,采用的属性元素全是自定义的,当时只能采用 xpath 定位,让我蛋疼了很久

—— 来自 TesterHome 官方 安卓客户端

#11 楼 @seveniruby 额。。。原谅我的懒惰。 一忙起来就有惰性了,一有时间就想歇着。。

#10 楼 @ycwdaaaa 我前天还在想, 你怎么最近没发帖了...

#9 楼 @seveniruby 嗯,以后多交流。说不定以后就有好的方案了

#8 楼 @ycwdaaaa 用在质量分析上. 做一些基于 ELK 的数据计算或者日志的计算. 从线上挖掘潜在 Bug. 我目前也还没用到 spark 的计算. ELK 能解决大部分的分析问题了. 目前我也没精力深入研究应用 spark

#6 楼 @seveniruby 请教一下,研究了 spark 之后怎么应用到测试中呢? 因为我们的产品也用到了 spark,我一直没有什么好的方案。虽然知道了原理但是感觉测试上还是得从上层调用 API。现在除了调用 yarn 的 API 以外我没想到什么好的方式辅助测试了

#6 楼 @seveniruby 嗯~ 暂时不搞

#5 楼 @ycwdaaaa 暂时不推荐 scala. 会导致受众偏小. 我是因为有特殊需要. 为了深入研究 gatling zipkin 还有 spark 云计算等相关内容.

#4 楼 @seveniruby 其实如果 scala 普及起来了的话,我觉得我会用 scala 的。现在换成 scala 的话实在是担心写出来的东西只能自己用。

java 的这几个项目不错, 终于把 java 老土的测试生态提升了下.

有点类似 https://testerhome.com/topics/3480
scalatest 直接集成了你提的这些优点, 比如更好的 webdriver 封装, 更好的断言, 以及更好的数据驱动.
官方的这个小例子很有代表性

class BlogSpec extends FlatSpec with ShouldMatchers with WebBrowser {

  implicit val webDriver: WebDriver = new HtmlUnitDriver
  val host = "http://localhost:9000/"
  "The blog app home page" should "have the correct title" in {
    go to (host + "index.html")
    pageTitle should be ("Awesome Blog")
    click on id("q")   // to lookup by id "q" 
    click on name("q") // to lookup by name "q" 
    click on linkText("click here!")
    textArea("body").value = "I saw something cool today!"
    textArea("body").value should be ("I saw something cool today!")
  }
}

当然不是为了吹嘘 scala, scala 跟 java 是同源的都基于 jvm. 只是测试生态碰巧很丰富.
不过 scala 偏难, 会始终是个小众语言. 未来 5 年还是 java 的天下

@zht 这个东西在我看来相当有用啊,虽然我不是很喜欢 Java

同类工具多如牛毛。不过我欣赏这种学习精神,虽然做出来的东西没什么用,但继续努力下去总能做得更好。

陈恒捷 将本帖设为了精华贴 08月14日 22:16
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册