使用 CasperJS 進行簡單的 UI 測試

Winterto1990發表於2015-10-16

看到了一篇有關casperjs不錯的文章,轉載到自己部落格學習一下:
摘要
無論你開發大或小的應用, 從安全來講測試是一個很重要的組成部分.
但是如何可靠持續的測試, 尤其是當你的人員不能有效的測試應用的每一次改變? 所以呢,把這個工作交給軟體是最合適的了.
這麼多年來, 開發者開發了很多應用和技術來滿足這個需求. 當然, 它們執行的非常好. 但是我們總是尋找更新的, 更簡單的 (有時候是更’酷’的) 方式來做我們的工作. 所以最近我開始學習一種更有前途的新技術: PhantomJS.
—————————————————————————————
PhantomJS是什麼?
PhantomJS 是一個無介面的,包含了WebKit瀏覽器引擎JavaScript API的指令碼直譯器. 速度快並且支援各種web標準: DOM 操作, CSS 選擇器, JSON, Canvas 和SVG.
PhantomJS 的建立者是 Ariya Hidayat. 它是一個非常棒的技術,但是通過網站上的瞭解和所有的APIs文件, 我遇到了 CasperJS.
CasperJS是什麼?
Casper 是一個用JavaScript編寫的基於PhantomJS的導航指令碼和測試工具.它充許我們像PHPUnit或 JUnit一樣測試我們的網站,測試我們的程式碼.
它是一個非常棒的工具 – 一是它是非常簡單的,用JavaScript編寫,滿足各種各樣的測試需求,一是它可以方便的和我們的開發環境整合. 完美的組合前端和後端.
—————————————————————————————
在本篇文章中,我將瀏覽一下CasperJS的基礎用法。我將扮演一個典型的使用者訪問New Relic(新的遺址)網站的一頁,特別是‘真實使用者監控’部分。
我之所以選擇這一頁是因為它有大量方面對一個標準網站或應用極為常見,如影像,表單,按鈕,連結和文字。它內容豐富並且有許多東西可供我們處理與測試。現在,我確信New Relic的勤快的夥計已經做過這些,但我也非常相信他們不會介意我為這個網站建立一個簡單的測試集和斷言。
特徵
CasperJS具有一系列特徵。 本文中,我會聚焦於 測試人員 API。它具有一些列功能與斷言,都是你期望一個好的測試API所具有的,包括:
* assertTextExists (文字存在斷言)
* assertTitle (標題斷言)
* assertHttpStatus (HTTP狀態斷言)
* assertDoesntExist (不存在斷言)
* assertUrlMatch (Url匹配斷言)

我將使用這些斷言來顯示CasperJS是如何工作的。它也包含一組其他特性,但我不會去深入探討——僅僅足夠做一個工作示例而已。不過,我鼓勵你研究一下精彩的線上API文件。它閱讀起來令人愉悅,因為它相當清晰和簡潔。
準備
如果你沒有準備好,在我們繼續以前,先安裝 PhantomJS 或 Casper 。 現在,讓我們馬上開始吧。
開始
當我第一次載入New Relic網站的’真實使用者監控’頁面,看起來像下面圖片這樣(自撰寫本文以後它已經變化了)。我突出了登錄檔格的起點。
這裡寫圖片描述
如果你點選了這個表單裡的任何元素,它其餘的部分就會顯露出來,看起來就是下圖這樣。
這裡寫圖片描述
測試將著眼於表單,但我們也要看看title標籤,表單左邊的圖片,及其上的文字。
程式碼
首先,為節省時間,我初始化了兩個變數:一個用來儲存URL,一個用來儲存網站名稱,因為我要在測試中的‘success’資訊裡顯示它們。

var url = 'http://newrelic.com/product/real-user-monitoring';
var siteName = 'NewRelic';

Casper帶有4個內建記錄級別:
* debug
* info
* warning
* error

預設情況下,CasperJS會在‘error’級別過濾日誌。所以如果你開始記錄日誌後沒有看到任何東西,可能就是這個原因。為確保顯示日誌輸出,我把它設定為‘debug’。而且我關閉了‘verbose’選項,如果它啟用,我們會看到關於所有東西的資訊,這會相當干擾。

var casper = require('casper').create({
    verbose: false,
    logLevel: 'debug'
});

你可以在Casper API頁面上發現其它配置選項。comment函式,如下面演示的,使我們能記錄輸出——在這種情況下是輸出到控制檯。我在這裡使用一個簡單的訊息說明測試開始了:

casper.test.comment('Starting Testing');

函式‘casper.start’開始執行測試:

casper.start(url, function() {
    this.test.assert(
        this.getCurrentUrl() === url, 'url is the one expected'
    );

    this.test.assertHttpStatus(200, siteName + ' is up');

這是我所做的:我載入在‘url’中指定的URL,並假定我們可以載入想要的網址。在成功載入頁面之後,開始呼叫一些斷言來確認所載入的頁面上的內容是我們所期望的。
在這種情況下,我對返回的狀態碼進行了一個快速檢查,它應該是200,我得到的也是200。讓我們進行另一個斷言:

this.test.assertTitle(
    'Real User Monitoring, End User Experience Monitoring : New Relic',
    siteName + ' has the correct title'
);

上面這個測試非常簡單。它斷言載入的頁面的標題應該是什麼,並輸出一個含有該資訊的訊息。
在下面的程式碼裡,我首先檢查確認存在一個ID為‘nr-signup-form’的表單。如果這個表單不存在,那對錶單內的元素進行檢查就沒有意義了,對吧?

this.test.assertExists(
    'form[id="nr-signup-form"]',
    siteName + ' has a form with name "nr-signup-form"'
);

現在,在我繼續之前,我要提到的是,當我斷言元素存在或不存在於載入的頁面上時,我可以用兩種不同的方法:
*XPath
*CSS選擇器

現在讓我們用斷言來搜尋輸入域控制元件:

this.test.assertExists(
    {type: 'xpath', path: '//input[@id="FullName"]' },
    'the element exists'
);

this.test.assertExists(
    {type: 'xpath', path: '//input[@id="Company"]' },
    'the element exists'
);

this.test.assertExists(
    {type: 'xpath', path: '//input[@id="Email"]' },
                'the element exists'
);

this.test.assertExists(
    {type: 'xpath', path: '//input[@id="Password"]' },
    'the element exists'
);

this.test.assertExists(
    {type: 'xpath', path: '//input[@id="PromoCode"]' },
    'the element exists'
);

在上面的五個斷言中,我查詢了五個輸入域控制元件,它們的ID分別是‘FullName’, ‘Company’, ‘Email’, ‘Password’ 和‘PromoCode’。用XPath查詢選擇列表控制元件元素同樣容易。

現在,這些測試還非常簡單。那麼,讓我們來把它們變得複雜點。

this.test.assertExists(
    {
        type: 'xpath',
        path: '//button[@id="sign-up-button"
                and @class="small-button"
                and @type="submit"]'
    }, 'the element exists'
);

在上面的測試中,我搜尋‘Create Free Account’按鈕,它在頁面中的位置如下圖所示。
這裡寫圖片描述
我在查詢中使用了一些條件運算子。我們要尋找一個具有下列條件的按鈕:
* ID 為 ‘sign-up-button’
* class 為‘small-button’
* type 為‘submit’
雖然看起來似乎我關注XPath比CasperJS更多,但為了更多的功能和配置,請耐心忍受我多說一點吧。

var x = require('casper').selectXPath;
var checkboxXpath = "//input[@type='checkbox' and
@id='checkbox1']/parent::*/a/label[
    contains(., 'Java') or
    contains(., 'Python') or
    contains(., 'Ruby') or
    contains(., 'PHP') or
    contains(., '.NET') or
    contains(., 'Node.js')
]";
this.test.assertExists(x(checkboxXpath), 'the element exists');

我在上面的測試中所做的,是通過把’x’初始化為‘selectXPath’助手來使XPath測試變得簡化一點。我要查詢那一頁上所有選擇應用程式型別的核取方塊,所以XPath查詢查詢核取方塊時用一個伴隨的label標籤來匹配所提供的應用程式語言的名稱。
為專門顯示我並非只會使用XPath(雖然它極其強大而且簡單),下面的兩個測試也使用CSS選擇器來檢查一個輸入域控制元件和列表專案也存在。

this.test.assertVisible('input#Company');

this.test.assertExists('li.nav-enterprise', 'the element exists');

搜尋表單元素和列表專案很簡單,但若你的頁面有很多資源,對於圖片或Flash的載入又怎麼檢測呢?這有一個例子:

this.test.assertResourceExists(
    '/images/tshirt-dude-form.png', 'T-shirt Image exists'
);

對於上面的測試,我斷言穿著所展示T恤的男人的照片已載入。 最後一個測試斷言某些文字存在,在本情況中就是在穿著T恤的男人鏡頭上方的‘Sign up for free!’文字:

this.test.assertTextExists(
        'Sign up for free!', 'page body contains "Sign up for free!"'
    );

然後我們這樣結束‘start’函式:

casper.test.comment('Ending Testing');

測試基本完成了,所以我輸出另一個日誌記錄來讓使用者知道這一點:

casper.run(function() {
    this.test.done(16);
    this.echo('So the whole suite ended.');
    require('utils').dump(casper.test.getFailures());
    require('utils').dump(casper.test.getPasses());
    this.exit();
});

我可以寫出我想要的所有測試,但若不呼叫“run”則什麼都不會發生。所以“run”是揭開上述工作的序幕。我所做的是檢查指令碼是否已經執行了16個測試,並且在最後用‘this.echo()’輸出最後一行文字表示整個測試已結束。
當我們執行測試時,有時得到最後結果的總結會挺好。用‘getFailures()’ 和 ‘getPasses()’函式,我們可以看到測試通過和失敗的總結。跟在這一切最後,我呼叫“this.exit()”來清盤。這個可以退出CasperJS和PhantomJS,如果需要,可以指定一個退出編號。
把這個更進一步,這一次我呼叫‘casperjs’略有不同:我傳遞‘test’,如下所示,伴隨著“xunit”開關。 這個將測試結果輸出到一個 XUnit XML檔案.

$ casperjs test mytest.js --xunit=log.xml

在CasperJS的下一個主要版本中,如果‘test’沒被傳遞到命令中,你將無法執行和呈現測試。提前知道這個很重要,可以避免你的指令碼被意外打斷。

轉自:http://www.oschina.net/translate/simpler-ui-testing-with-casperjs

相關文章