自動化測試工具小記:node + SpookyJS + CasperJS + PhantomJS

概述

這是一個跨度很廣的小記哦,使用node作爲腳本,涵蓋了三個工具:PhantomJS、CasperJS、SpookyJS。目前網上相關的資料比較少,請關注亂燉,我會斷斷續續更新。

那這三個工具有什麼用呢,網上比較專業的說法是:“前端自動化測試工具”,通俗點來說就是一個“沒有UI界面的終端瀏覽器”;如果完整的來看,他應該是NSCP(我個人寫的簡稱,即:node + SpookyJS + CasperJS + PhantomJS)

注意哦,NSCP和傳統的CURL不一樣的地方在於:

  • CURL是發起一個URL請求,單項的;而NSCP則是一個真正的網頁請求,請求整個網頁及網頁包含的所有資源
  • 效率來說,CURL遠遠比NSCP要快的多
  • CURL更適合爬數據,NSCP更多是對於“無界的瀏覽網頁”

那這個工具有什麼用呢?舉幾個簡單例子:

  • 網頁快照(截圖)
  • 前端自動化測試
  • 其他…

PhantomJS

OK,說了這麼多,那怎麼了解NSCP呢?在分享之前必須先了解PhantomJS。CasperJS和SpookyJS都是在PhantomJS基礎上拓展的工具。PhantomJS是一個無界的終端瀏覽器,他能夠訪問並測試指定網頁。

安裝方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Mac brew 安裝
brew install phantomjs
 
# Npm 安裝
npm install phantomjs
 
# CentOS 編譯安裝
sudo yum install gcc gcc-c++ make git openssl-devel freetype-devel fontconfig-devel
git clone git://github.com/ariya/phantomjs.git
 
cd phantomjs
git checkout 1.9
 
./build.sh

簡單的例子

將下面保存爲simple.js文件

1
2
3
4
5
6
7
8
9
console.log('Loading a web page');
 
var page = require('webpage').create();
var url = 'http://levi.cg.am/';
 
page.open(url, function (status) {
    // Page is loaded!
    phantom.exit();
});

終端執行命令

1
phantomjs simple.js

更多示例見:http://phantomjs.org/examples/

有人說PhantomJS是node,這樣理解不是很正確,PhantomJS是一個運行JS調用API接口的工具,允許通過npm方式下載安裝並以模塊方式存在node中。但是PhantomJS中是不支持node方法的哦,具體差異見官方文檔!

同類的軟件

挺多的,這裏我只列舉一個SlimerJS。SlimerJS與PhantomJS不同之處在於,PhantomJS是基於webkit內核的,而SlimerJS是基於Gecko(firefox),SlimerJS和PhantomJS語法很接近,不重複說明,具體見官網

http://www.slimerjs.org/index.html

在PHP中使用PhantomJS

方法一:通過exec

1
2
<?php
exec('phantomjs simple.js');

方法二:PHP PhantomJS

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
require '../vendor/autoload.php';
use JonnyW\PhantomJs\Client;
 
$client = Client::getInstance();
$request  = $client->getMessageFactory()->createRequest();
$response = $client->getMessageFactory()->createResponse();
 
$request->setMethod('GET');
$request->setUrl('http://google.com');
 
$client->send($request, $response);
var_dump($response);

安裝方法及使用見官方git:
https://github.com/jonnnnyw/php-phantomjs

CasperJS

通過上面提供的PhantomJS示例並瞭解後,你可能會發現一些問題,比如你要請求一系列網址,並且在一個請求之後,執行另一個請求。使用PhantomJS可能會像這樣:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var page = require('webpage').create();             //新建一個頁面
page.open(url1, function(status) {                  //導航到第一個URL
    if (status == "fail") phantom.exit();           //如果發生錯誤,退出程序
    page.open(url2, function(status) {              //否則在頁面加載完成的回調函數中繼續導航到第二個URL,依次類推
        if (status == "fail") phantom.exit();
        page.open(url3, function(status) {
            if (status == "fail") phantom.exit();
            page.open(url4, function(status) {
                if (status == "fail") phantom.exit();
                // 我可以停下來了嗎?
            });
        });
    });
});

是不是很恐怖,而CasperJS的目的就在於更方便的操作PhantomJS。CasperJS是一個開源的,用JavaScript編寫的,基於PhantomJS的導航腳本和測試工具,它簡化了定義一個完成的導航操作所需的步驟,還提供了很有用的函數封裝、方法、和語法糖、它可以完成下面這些常見任務:

  • 定義 & 排序瀏覽器導航步驟
  • 填充 & 提交表單
  • 點擊 & 跟蹤鏈接
  • 捕獲網頁截圖 (還可以截取某一區域)
  • 在遠程DOM上進行斷言測試
  • 記錄事件
  • 下載資源,包括二進制文件
  • 編寫功能測試套件,結果保存爲JUnit XML文件
  • 抓取網頁內容

CasperJS使用更方便的API解決了這種異步操作的問題:

1
2
3
4
5
6
7
8
var casper = require('casper').create();           //新建一個頁面
 
casper.start(url1);                                //添加第一個URL
casper.thenOpen(url2);                             //添加第二個URL,依次類推
casper.thenOpen(url3);
casper.thenOpen(url4);
 
casper.run();                                      //開始導航

終端執行文件

1
casperjs simple.js

更多示例見官方文檔:

http://casperjs.readthedocs.org/en/latest/index.html

注意哦:CasperJS使用的是casper的方法,PhantonJS使用的是phantom的方法;他們有各自的使用方法,不可以用混淆執行

安裝方法

目前我只在本地開發環境安裝,安裝方法如下:

1
brew install casperjs --devel

注意哦:目前PhantomJS的版本是1.9.2,正式版是不支持的,請安裝開發版

SpookyJS

通過上面描述,應該瞭解到:

  • PhantomJS,一個無界的終端瀏覽器
  • CasperJS,一個改善PhantomJS操作的工具

而SpookyJS有什麼用途呢?前面說了PhantonJS和CasperJS是可以作爲模塊進行npm安裝在node中,但是執行的方法卻是通過各自的bin文件,諸如:

1
2
phantomjs simple.js
casperjs simple.js

而SpookyJS則是一個驅動器,在node下能夠正常使用PhantonJS和CasperJS,這就是我定義的NSCP (node + SpookyJS + CasperJS + PhantomJS)。你可以簡單將其理解爲:

  • node:腳本語言
  • Spooky:驅動器
  • CasperJS:工具
  • PhantomJS:無界瀏覽器

安裝方法

既然SpookyJS作爲node的驅動器,那麼也需要通過node進行安裝:

1
npm install spooky

SpookyJS需要依賴的環境

簡單示例

保存爲hello.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
try {
    var Spooky = require('spooky');
} catch (e) {
    var Spooky = require('../lib/spooky');
}
 
var spooky = new Spooky({
        child: {
            transport: 'http'
        },
        casper: {
            logLevel: 'debug',
            verbose: true
        }
    }, function (err) {
        if (err) {
            e = new Error('Failed to initialize SpookyJS');
            e.details = err;
            throw e;
        }
 
        spooky.start(
            'http://en.wikipedia.org/wiki/Spooky_the_Tuff_Little_Ghost');
        spooky.then(function () {
            this.emit('hello', 'Hello, from ' + this.evaluate(function () {
                return document.title;
            }));
        });
        spooky.run();
    });
 
spooky.on('error', function (e, stack) {
    console.error(e);
 
    if (stack) {
        console.log(stack);
    }
});
 
/*
// Uncomment this block to see all of the things Casper has to say.
// There are a lot.
// He has opinions.
spooky.on('console', function (line) {
    console.log(line);
});
*/
 
spooky.on('hello', function (greeting) {
    console.log(greeting);
});
 
spooky.on('log', function (log) {
    if (log.space === 'remote') {
        console.log(log.message.replace(/ \- .*/, ''));
    }
});

通過node執行腳本

1
node hello.js

更多內容請見官方git:

https://github.com/WaterfallEngineering/SpookyJS

需要注意的幾個地方

通過SpookyJS配置PhantomJS和CasperJS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var spooky = new Spooky({
    child: {
        transport: 'http'
    },
    casper: {
        logLevel: 'debug',
        verbose: true,
        viewportSize: {
            width: 1440, height: 768
        },
        pageSettings: {
            outputEncoding: 'gbk'
        }
    }
}, function(err) {});

phantom和casper不同的方法調用

1
2
3
4
5
6
7
8
9
10
11
12
spooky.start('http://example.com/the-page.html');
 
spooky.then(function () {
    // 這裏是CasperJS的方法
});
 
spooky.thenEvaluate(function () {
    // 這裏是PhantomJS的方法
})
 
// this function (and the three spooky calls above) runs in Spooky's environment
spooky.run();

變量作用域

SpookyJS的作用域和js不一樣哦,具體看下面幾個來自官方的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var x = 'spooky';
spooky.start('http://example.com/the-page.html');
 
spooky.then(function () {
    var y = 'casper';
    console.log('x:', x); // -> x: undefined
});
 
spooky.thenEvaluate(function () {
    console.log('x:', x); // -> x: undefined
    console.log('y:', y); // -> y: undefined
});
 
spooky.run();

而PhantomJS的方法中可以通過傳遞一個對象過去

1
2
3
4
5
6
7
8
var x = 'spooky';
 
// spooky.thenEvaluate accepts an options argument (just like Casper)
spooky.thenEvaluate(function (x) {
    console.log('x:', x); // -> x: spooky
}, {
    x: x
});

而CasperJS的方法中需要將變量作爲數組對象,以參數傳過去

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var x = 'spooky';
var y = 'kooky';
 
// spooky.then accepts a function tuple
spooky.then([{
    x: x
}, function () {
    console.log('x:', x); // -> x: spooky
}]);
 
// spooky.thenEvaluate accepts both a function tuple and an argument hash
spooky.thenEvaluate([{
    y: y
}, function (x) {
    console.log('x:', x); // -> x: spooky
    console.log('y:', y); // -> y: kooky
}], {
    x: x
});

全局和局部變量

1
2
3
4
5
6
7
8
9
10
var x = 'spooky';
 
spooky.then([{
    x: x
}, function () {
    x = 'casper';
    console.log('x:', x); // -> x: casper
}]);
 
console.log('x:', x); // -> x: spooky

一個混合的使用例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
spooky.start('http://example.com/the-page.html');
 
var x = 'spooky';
spooky.then([{
    x: x
}, function () {
    var y = 'casper';
    var z = this.evaluate(function (x, y) {
        console.log('x:', x); // -> x: spooky
        console.log('y:', y); // -> y: casper
 
        return [x, y, 'and the-page.html'].join(', ');
    }, {
        x: x,
        y: y
    });
 
    console.log('z:', z); // -> z: spooky, casper, and the-page.html
}]);
 
spooky.run();

那兩個不同的方法之間,如何傳遞變量呢?來一個非官方的例子,同樣來自github

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
spooky.then(function () {
    window.pageCount = 0;
});
 
spooky.waitFor(function () {
    var isLastPage = !this.exists('#next');
 
    if (!isLastPage) {
        window.pageCount++;
        this.click('#next');
    }
 
    return isLastPage;
});
 
spooky.then(function () {
    this.emit('console', 'Page count: ' + window.pageCount);
});

是不是有點不太好理解,如果你瞭解PHP的話,可以將其當作PHP閉包中的ues去看待。如果仍舊不懂,你可以給我留言哦

不允許使用中文

這個真的是官方、非官方都沒有說明的,無論你是不是utf-8,在SpookyJS構造方法中都不允許使用中文

1
2
3
4
5
6
7
8
9
10
11
12
var spooky = new Spooky({
    // 此處省略...
}, function() {
    spooky.then(function () {
        // 這樣是不可以的哦
        var name = '中文';
    }
});
 
spooky.on('test', function(log) {
    console.log('但是這裏是可以用中文的哦!');
});

好在JS中有一個原生的方法 decodeURIComponent,可以這樣

1
2
3
4
5
6
7
8
9
10
11
12
13
var spooky = new Spooky({
    // 此處省略...
}, function() {
    spooky.then(function () {
        var name = this.evaluate(function() {
            return window.decodeURIComponent('%E4%B8%AD%E6%96%87');
        });
    }
});
 
spooky.on('test', function(log) {
    console.log('但是這裏是可以用中文的哦!');
});

當然也可以使用base64,目前還沒測。原因,我找了很多資料,唯一有個相關的說法是CasperJS不支持大於8b的字符

轉載自:http://levi.cg.am/archives/3648

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章