e2e或者端到端(end-to-end)或者UI测试是一种测试方法,它用来测试一个应用从头到尾的流程是否和设计时候所想的一样。简而言之,它从一个用户的角度出发,认为整个系统都是一个黑箱,只有UI会暴露给用户。
想象一下,如果把这些自动化的测试工作换成人工来做,那会多么的令人头疼,尤其是对于那些具有大量交互的应用,手工测试将会以编程不可能的任务。
在本文中,我们将会介绍如何使用webdriverJS和Jasmine来进行自动e2e测试。
什么是WebDriverJS?
和WebDriver相关联的东西很多,以至于很多初学者会感到比较困惑。
因此,在本文中我们从头讲起。
Selenium
正如Selenium网站中提到的:
Selenium将会自动操作你的浏览器。
仅此而已。
Selenium已经支持了大部分主流的浏览器,它是一个用于自动化测试的绝佳工具。因此无论你在测试你的应用时做了些什么,比如导航到一个页面,点击了一个按钮,在一个输入框中写了一些文字,提交了一个表单等等,Selenium都可以替你自动完成这些事。
WebDriver
WebDriver(或者Selenium 2)基本上指的都是特定浏览器控制代码的语言绑定和实现。
WebDriver引入了一个JSON wire protocol用于将不同的语言和浏览器控制器进行交流。
例如,在浏览器中点击了一个元素,绑定将会发送一个POST请求到session/:sessionId/element/:id/click
。
因此,WebDriver的一端是一个语言绑定,而另一端,是叫做Selenium server的服务器。二者之间的交流使用的就是JSON wire protocol。
WebDriverJS
正如前面所提到的,WebDriver拥有很多不同的语言绑定,像是Ruby,Python等等。javascript是web世界的主要语言,也载入了WebDriver的列表中。于是我们迎来了WebDriverJS!
你可能已经猜到了,WebDriverJS就是一个简单的对于JSON wire protocol的包装,它同时暴露除了高等级的函数接口来使我们的生活更加轻松。
现在如果你搜搜webdriverJS,你可能会看到两个不同的绑定,它们的名字分别是Selenium-webdriver和webdriverjs(确实有很多driver),两个绑定都是node模块。你可以使用任何一个你喜欢的,但是我们依然会坚持使用官方的版本 – selenium-webdriver。
假设你现在有一个javascript项目需要进行自动化的e2e测试。首先你需要安装这个模块:
代码laycode - v1.1npm install selenium-webdriver
- 1
好了!你现在可以require这个包,然后进行一些小小的配置以便你能够在浏览器中打开任何网页:
代码laycode - v1.1var webdriver = require('selenuim-webdriver'); var driver = new webdriver.Builder(). withCapabilities(webdriver.Capabilities.chrome()). build(); driver.get('http://www.baidu.com');
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
为了运行你的测试代码,你可以这样做:
代码laycode - v1.1node testfile.js
- 1
注意:除了npm包之外,你还需要下载你想要使用的WebDriver实现。对于2.34.0版本,Selenium-WebDriver原生的支持ChromeDriver。你可以简单的下载ChromeDriver的副本并确保它能够在你的PATH中找到。其他的driver(比如. Firefox,Internet Explorer,以及Safari),仍然需要标准的Selenium server。
和其他语言绑定的不容之处
WebDriverJS和其他的语言绑定之间有一个重要的区别 – 它是异步的。
因此如果你在python下写下面的代码:
代码laycode - v1.1//伪代码 driver.get(page1); driver.click(E1);
- 1
- 2
- 3
由于Python(以及其他语言)的API都是同步的,因此所有的带啊吗语句都是同步执行的。但是在javascript中却不是这样的情形。为了确保一些行为的前后顺序,WebDriverJS使用了Promise。简单来说,一个promise就是一个能够在它完成之后执行一些代码的对象。
但是到这里还没有结束。即使是有了Promise,上面的代码也会变成下面的样子:
代码laycode - v1.1//伪代码 driver.get(page1).then(function(){ driver.click(E1); });
- 1
- 2
- 3
- 4
- 5
- 6
你在这里有没有闻到一丝丝回调地狱的味道?为了让代码更加整洁,WebDriverJS对Promise进行了一些包装,于是便有了ControlFlow。
简单来说,ControlFlow从下面几个方面阻止了回调地狱:
- 它保留了一个行动的列表
- WebDriverJS暴露的函数并没有做实际的事情,它们只是把需求的任务push到上面的列表中
- ControlFlow将每一个then的新入口放到了列表的最后,因此确保了它们之间的顺序
总而言之,它允许我们编写下面的代码:
//伪代码driver.get(page1);//隐式的添加到上面行为的then()中
driver.click(E1);
是不是碉堡了!
ControlFlow也提供了一个excute函数来将你的自定义函数push到执行列表中人后返回这个函数在特定执行环境下解析/拒绝之后的返回值。因此你可以使用promise并且在你的自定义代码中做任何异步的事情:
代码laycode - v1.1var flow = webdriver.promise.controlFlow(); flow.execute(function () { var d = webdriver.promise.defer(); do_anything_async().then(function (val) { d.fulfill(val); }) return d.promise; });
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
将WebDriverJS和Jasmine结合起来
我们可以使用Selenium进行浏览器自动化测试。现在我们需要一个测试框架来处理我们的测试。这时我们需要Jasmine。
你可以通过npm来安装Jasmine:
代码laycode - v1.1npm install jasmine-node
- 1
如果我们要让前面的testfile.js代码来检查页面的标题是否正确,代码将会如下所示:
代码laycode - v1.1var webdriver = require('selenium-webdriver'); var driver = new webdriver.Builder(). withCapabilities(webdriver.Capabilities.chrome()). build(); describe('basic test',function(){ it('should be on correct page',function(){ driver.get('http://www.baidu.com'); driver.getTitle().then(function(title){ expect(title).toBe('Baidu'); }); }); });
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
现在上面的文件需要使用Jasmine-node命令来运行,如下所示:
代码laycode - v1.1jasmine-node testfile.js
- 1
上面的操作会让浏览器做上面所提到的事情,但是你会注意到Jasmine什么结果都没给出。为什么?
这是因为Jasmine完成了执行步骤同时没有任何的expect语句被执行,因为期望被放在了getTitle函数的异步毁掉中。
为了解决异步的问题,Jasmine-node提供了一种方法来告诉特定的it块:它是异步的。只需要在it的回调中添加一个done,并在最后执行即可。于是我们可以把上面的嗲吗改成下面的形式:
代码laycode - v1.1var webdriver = require('selenium-webdriver'); var driver = new webdriver.Builder(). withCapabilities(webdriver.Capabilities.chrome()). build(); describe('basic test',function(done){ it('should be on correct page',function(){ driver.get('http://www.baidu.com'); driver.getTitle().then(function(title){ expect(title).toBe('Baidu'); //Jasmine-node会一直等到这个异步函数执行完毕再调用done()函数来进入下一个测试单元 done(); }); }); });
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
小提示:你可能需要改变Jasmine用于测试的时间:
代码laycode - v1.1jasmine.getEnv().defaultTimeoutInterval = 10000; //以微秒为单位
- 1
Angular应用的额外福利
AngularJS框架从一开始就是非常倾向于测试的。不用说,这些应用汇应该花费很多时间在e2e之上。
Protractor是一个又AngularJS团队编写的库,它是一个队WebDriverJS的包装,同时Jasmine在这里被指定用来测试,真是太爽了。
我们来看看Protractor都有什么额外的福利:
- 不需要基于id,css选择器,xpath等查询元素,你可以基于绑定,模型,迭代器等等进行测试。太贴心了!
-
其中的Jasmine可以接受promises。因此,我们可以用下面的代码进行上面的测试来检查标题:
driver.getTitle().then(function(title){ expect(title).toBe('Baidu'); });
可以重构为:
代码laycode - v1.1expect(driver.getTitle()).then('Baidu');
- 1
除此之外,AngularJS中海油很多非常酷的特性来进行end-to-end测试。
总结
e2e对于现在的应用来说非常重要,因此它对于自动化测试来说也变得很重要。你可以选择很多工具来帮助你进行自动化测试。
希望本文能够帮助你开启你的e2e之路。还在等什么呢?赶快做一些end-to-end测试吧!
本文译自e2e-testing-with-webdriverjs-jasmine/,原文地址http://engineering.wingify.com/posts/e2e-testing-with-webdriverjs-jasmine/
如果你觉得本文对你有帮助,请为我提供赞助 https://me.alipay.co,/jabez128