什么是TestCafe
轉自: https://www.jianshu.com/p/94181abe5e9a
官方:
A node.js tool to automate end-to-end web testing Write tests in JS or TypeScript, run them and view results
抓幾個重點詞語:1. E2E Web Testing 2.JS\TypeScript 3. Node.js Tool。
簡單說就是Node.JS編寫的Web端UI自動化測試框架。
官網:http://devexpress.github.io/testcafe/
TestCafe VS Selenium
這時我想你跟我都有個疑問,跟Selenium 有啥區別?這里個人簡單閱讀了下官方文檔,寫幾個粗淺的對比。
對比項 | Selenium 3.x | TestCafe | 誰更優 |
---|---|---|---|
社區 | Web自動化測試一哥 ,學習資料非常多 | 較新的工具,官方提供了詳細的學習資料,Github上Start人數也超過5K | Selenium |
支持語言 | java,python,ruby,Node.js...... | JavaScript,TypeScript | Selenium |
支持的瀏覽器 | Chrome,IE,Firefox,Safari,Edge等有Driver的瀏覽器都支持 | 支持所有能支持JS的瀏覽器,也就意味着支持多端 | TestCafe |
完善性 | 需要跟其它框架結合使用,如TestNG等 | 自身就是一個完整的自動化測試框架 | TestCafe |
易學性 | 簡單易學 | 簡單易學 | 不分上下 |
Selenium畢竟已經是Web自動化測試的W3C標准了,它有非常多的優勢,但TestCafe 作為后起之秀我這還想誇誇Demo使用過程的幾點優於Selenium的感受。
- TestCafe 不再像Selenium 通過各個瀏覽器提供的Driver來驅動瀏覽器,而是有點類似Selenium RC直接往頁面注入JS來操作頁面,所以使用過程中再也不用擔心因為瀏覽器版本和Driver版本以及Selenium版本不匹配而照成的Case執行失敗。
- TestCafe 是一整套完整的自動化測試框架,不僅提供了Cases的管理,運行,失敗自動重跑,錯誤自動截圖,並發等,對頁面和頁面元素的等待也封裝完善而且使用簡單,不像Selenium需要借助其他框架或者二次封裝智能等待或者使用隱示/顯示等待而有點復雜。
- TestCafe 可以控制整體的執行速度,甚至可以細到某個操作的執行速度(這個有點類似慢放,用起來大家可以感受下,非常魔性)
- TestCafe 因為只要你的瀏覽器支持JS,所以支持桌面,移動端平台。
- TestCafe debug模式,通過代碼配置或運行時設置,可以控制執行過程中失敗時進入調試模式。
PS:當然以上的感受並沒有經過項目的積累,純粹Demo過程中的總結,也不曉得真正用到項目中會有哪些坑得踩。
TestCafe 快速入門
安裝
因為是Node.js 項目,可以直接通過npm安裝,全局安裝如下
npm install -g testcafe
快速Demo一個
baidu.js
fixture `baidu demo`
.page `https://www.baidu.com`; test('baidu search', async t=>{ await t.typeText('#kw',"hao123") .click('#su') });
通過Chrome運行
testcafe chrome baidu.js
上面代碼看不懂沒關系,感受下TestCafe就行。
Cases管理
自動化測試,終歸還是測試,是測試就離不開測試用例,那TestCafe如何組織管理測試用例?
fixture 和 test
一個js文件可以包含多個fixture,一個fixture可以包含多個test。 我們可以理解為fixture是個集合,test標注的每個函數模塊是一個case。
語法
fixture("測試集描述")
fixture `測試集合描述`
test('用例描述',fn(t))
Demo
fixture("cases manage").page("https://www.baidu.com"); test('this case 1', async I => { console.log("this is case 1"); }); test('this case 2', async I => { console.log("this is case 2"); }); test('this case 3', async I => { console.log("this is case 3"); }); fixture(`cases manage 2`).page(`https://testerhome.com/#gsc.tab=0`); test('this case 1-1', async I => { console.log("this is case 1-1"); }); test('this case 2-1', async I => { console.log("this is case 2-1"); }); test('this case 3-1', async I => { console.log("this is case 3-1"); });
運行結果:

其中你會發現每個test 執行之前都會執行fixture打開頁面的操作,但只會啟動一次瀏覽器。那這時又會一個新的問題,除了打開頁面的前提條件,是否框架自帶了更多的前提/后置條件的處理了,也就是各種beforexxx。
當然!
fixture 的前置條件
fixture.beforeEach( fn(t) ):每個test執行之前都會被運行
fixture.afterEach( fn(t) ):每個test執行之后都會被運行
fixture.before(fn(t)):比beforeEach更早運行,且每個fixture只運行一次
fixture.after(fn(t)):比afterEach更晚運行,且每個fixture只運行一次
Demoj
fixture(`beforeeach test1`) .page(`https://www.baidu.com`) .beforeEach(async I => { console.log('this is beforeEach') }) .before(async I => { console.log('this is before') }) .after(async I => { console.log('this is after') }) .afterEach(async I=>{ console.log("this is afterEach") }); test("test beforeAndafter",I=>{ console.log("1111") }); test("test beforeAndafter",I=>{ console.log("2222") });
運行結果:

test的前置條件
test.before(fun(t)):該test運行之前運行
test.after(fun(t)):該test運行之后運行
Demo
fixture(`beforeeach test1`) .page(`https://www.baidu.com`) .beforeEach(async I => { console.log('this is beforeEach') }) .before(async I => { console.log('this is before') }) .after(async I => { console.log('this is after') }) .afterEach(async I => { console.log("this is afterEach") }); test .before(async t => { console.log(`this is test's before`) }) ("test beforeAndafter", I => { console.log("1111") }) .after(async t => { console.log(`this is test's after`) }); test("test beforeAndafter", I => { console.log("2222") });
運行結果:

注意: 從控制台輸出看,test的before/after 會覆蓋fixture中的beforeEach/afterEach。也就是說如果一個test里面包含了before/after 那么fixture中的beforeEach/afterEach對該test無效。
跳過測試
fixture.skip :跳過該fixture下的所有test
test.skip : 跳過該test
fixture.only :只執行該fixture下的所有test,其余的fixture下的test全部跳過
test.only : 只運行該test,其余全部跳過
元素定位
Demo
- 創建Selectors
import { Selector } from 'testcafe';
- 使用Selectors
// 通過css定位 const osCount = Selector('.column.col-2 label').count; // 通過id定位 const submitButtonExists = Selector('#submit-button').exists;
同時因為是JS注入方式,所以定位方式非常靈活,幾乎JS中定位元素的方式都支持。 例如
import { Selector } from 'testcafe'; fixture `My fixture` .page `http://devexpress.github.io/testcafe/example/`; const label = Selector('#tried-section').child('label'); test('My Test', async t => { const labelSnapshot = await label(); await t.click(labelSnapshot); }); test('My test', async t => { const secondCheckBox = Selector('input') .withAttribute('type', 'checkbox') .nth(1); const checkedInputs = Selector('input') .withAttribute('type', 'checkbox') .filter(node => node.checked); const windowsLabel = Selector('label') .withText('Windows'); await t .click(secondCheckBox) .expect(checkedInputs.count).eql(1) .click(windowsLabel); });
同時還支持自定義擴展選擇器,而且針對當前流行的React,Vue,Angular,Aurelia前端框架,還有特點的定位選擇器,這里內容很多,有興趣直接看官方文檔:
http://devexpress.github.io/testcafe/documentation/test-api/selecting-page-elements/
操作
元素操作其實上面例子我們已經用過點擊,文本輸入等方法了,官方也給了很全的api文檔和demo:http://devexpress.github.io/testcafe/documentation/test-api/actions/ ,這里就講下一些比較特殊的元素或瀏覽器的操作。
- resizeWindow():設置窗口大小
- t.maximizeWindow( ):最大化窗口
fixture`demo`.page('https://www.baidu.com'); test('設置win窗口大小', async I => { await I.resizeWindow(300, 500) ..maximizeWindow( ); });
- getBrowserConsoleMessages():獲取頁面控制台消息
test('獲取控制台輸出', async I => { console.log(await I.getBrowserConsoleMessages()) });
- wait():暫停
test('暫停', async I => { await I.wait(3000); });
- switchToIframe():切換到iframe
- switchToMainWindow():返回到主窗體
fixture`iframe 處理 `
.page`http://www.w3school.com.cn/tiy/t.asp?f=jseg_alert`; test('iframe ', async t => { await t .click('#button-in-main-window') // 切換ifrme .switchToIframe('#iframe-1') // 返回主窗體 .switchToMainWindow(); });
- 下拉框選取:其實就是定位下拉框,再定位到下拉框下的選項,然后點擊兩次。
fixture`下拉框選取 `
.page`file:///C:/Note/selenium_html/index.html`; test.only('下拉框選取 ', async t => { const phone = Selector('#moreSelect'); const phoneOption = phone.find('option'); await t .click(phone) .click(phoneOption.withText('oppe')); });
- 三種警告框的處理setNativeDialogHandler(fn(type, text, url) [, options]):fu返回true 點擊確定,返回false點擊取消,返回文本則在prompt輸入文本,這個執行過程中就不會看到警告框彈出,直接處理掉。
fixture`警告框處理 ` .page`http://www.w3school.com.cn/tiy/t.asp?f=jseg_alert`; test('處理alert ', async t => { await t .switchToIframe("iframe[name='i']") // return true 表示點擊確定 .setNativeDialogHandler(() => true) .click('input[value="顯示警告框"]') .wait(10000); }); fixture`警告框處理 ` .page`http://www.w3school.com.cn/tiy/t.asp?f=jseg_prompt`; test.only('處理 提示框 ', async t => { await t .switchToIframe("iframe[name='i']") .setNativeDialogHandler((type, text, url) => { switch (type) { case 'confirm': switch (text) { //false 點擊 取消 case 'Press a button!': return false; // 返回 true 點擊確定 case 'You pressed Cancel!': return true; default: throw 'Unexpected confirm dialog!'; } case 'prompt': // 警告框填入值 hi vidor return 'Hi vidor'; case 'alert': throw '我是警告框!!'; } }) .click('input[value="顯示提示框"]') .wait(10000); });
- 上傳文件setFilesToUpload(),清空上傳:clearUpload():
fixture`My fixture`
.page`http://www.example.com/`; test('上傳圖片', async t => { await t .setFilesToUpload('#upload-input', [ './uploads/1.jpg', './uploads/2.jpg', './uploads/3.jpg' ]) // 清除上傳 .clearUpload('#upload-input') .click('#upload-button'); });
斷言
TestCafe自帶了較為齊全的斷言方法。斷言都是通過expect()開始;
import { Selector } from 'testcafe'; fixture `My fixture`; test('My test', async t => { // 斷言 通過CSS定位到的有3個元素,eql()表示相等,count表示定位元素個數 await t.expect(Selector('.className').count).eql(3); }); test('My test', async t => { // 斷言ok()表示為true,exists表示元素是否存在 await t.expect(Selector('#element').exists).ok(); });
更多APIdemo查看官方文檔:http://devexpress.github.io/testcafe/documentation/test-api/assertions/
特性
在介紹幾個TestCafe比較有意思的幾個地方。
執行速度
testcafe 支持測試執行的速度控制。 speed(x),x支持0.01到1之間,1則表示正常速度執行。
全局速度控制
可以通過控制台執行命令控制:
testcafe chrome xxxx.js --speed 0.1
控制某個test的執行速度
test("test setTestSpeed", I => { I.setTestSpeed(0.1); ...... });
控制某個步驟的執行速度
test("test setTestSpeed", I => { I.click("#kw").setTestSpeed(0.5); });
Chrome設備模擬
在啟動Chrome瀏覽器時,可以設定Chrome提供的模擬器。

testcafe "chrome:emulation:device=iphone x" xxx.js
PageObject demo
大家都知道,做UI自動化測試,肯定得使用PO或者PF模式,下面簡單Demo個例子看看TestCafe 可以如何組織PO模式。
baiduPage.js
import {Selector, t as I} from 'testcafe' class baiduPage { baiduInput = Selector('#kw'); baiduButton = Selector('#su').withAttribute('value', '百度一下'); async searchBaidu(text) { await I .typeText(this.baiduInput, text, { // 清空 replace: true, }) .click(this.baiduButton) }; } export default baiduPage = new baiduPage();
baiduCases.js
import baiduPage from './baidu_page' fixture`baidu search`.page`https://www.baidu.com/`; test('po demo', async I => { await I.typeText(baiduPage.baiduInput, "test"); baiduPage.searchBaidu("testCafe"); await I.typeText(baiduPage.baiduInput,"居於之前的字符串空兩個字符中插入",{ caretPos:2 }) });
參數化/數據驅動
其實就是創建一個對象,用for ... of ... 循環遍歷
fixture`todoPage test cases`.page`http://todomvc.com/examples/react/#/`; const testCases = [ { todo: '123', }, { todo: '!@#$', } // 等等可能性的cases,這里隨便造兩個作為data driver ]; for (const todoText of testCases) { test('create todo list ' + todoText.todo, async t => { await todoPage.createTodoList(todoText.todo); await t.expect(todoPage.firstTodo.innerText).eql(todoText.todo); }); }
運行方式Runner
TestCafe 可以通過命令行的方式來執行測試腳本,但是感覺實際過程中肯定不是很方便,特別如果運行時需要跟一堆參數的情況下,那么TestCafe 提供了Runner,更方便配置和運行。
如下配置,我需要被運行的Cases,錯誤自動截圖,並發,生成report,智能等待,執行速度,執行的瀏覽器等全部配到Runner里面,這樣我就不需要通過命令行運行,而且在項目中使用非常方便。
const createTestCase = require('testcafe'); const fs = require('fs'); let testcafe = null; createTestCase('localhost', 1337, 1338) .then(tc => { testcafe = tc; const runner = testcafe.createRunner(); const stream = fs.createWriteStream('report.json'); return runner // 需要運行的cases .src( [ '../demo/podemo/*.js', '../demo/setWindowsSize.js' ] ) // 設置需要執行的瀏覽器 .browsers([ 'chrome', 'firefox' ]) // 錯誤自動截圖 .screenshots( // 保存路徑 '../error/', true, // 保存路勁格式 '${DATE}_${TIME}/test-${TEST_INDEX}/${USERAGENT}/${FILE_INDEX}.png' ) // 生成report格式,根據需要安裝對應report模塊, // 詳細看:http://devexpress.github.io/testcafe/documentation/using-testcafe/common-concepts/reporters.html .reporter('json', stream) // 並發 .concurrency(3) .run({ skipJsErrors: true, // 頁面js錯誤是否忽略,建議為true quarantineMode: true, // 隔離模式,可以理解為失敗重跑 selectorTimeout: 15000, // 設置頁面元素查找超時時間,智能等待 assertionTimeout: 7000, // 設置斷言超時時間 pageLoadTimeout: 30000, // 設置頁面加載超時時間 debugOnFail: true, // 失敗開啟調試模式 腳本編寫建議開啟 speed: 1 // 執行速度0.01 - 1 }); }).then(failedCount => { console.error('Failed Count:' + failedCount); testcafe.close(); }) .catch(err => { console.error(err); });
寫在最后
TestCafe 還有非常多有意思的東西可以去發掘,例如跟Jenkins等集成一類的。 個人demo了一些例子覺得是個非常值得推薦的 UI 自動化測試框架,特別是用JS編寫的在codecept,WebdriverIO我推薦TestCafe。 也許國內現在用的人不多,但相信是金子總會發光的。
作者:博客已遷移I米陽
鏈接:https://www.jianshu.com/p/94181abe5e9a
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。