UI 自動化測試框架---TestCafe

什麼是TestCafe

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.JSTypeScript 3. Node.js Tool。
簡單說就是Node.JS編寫的Web端UI自動化測試框架。
官網:http://devexpress.github.io/t...css

TestCafe VS Selenium

這時我想你跟我都有個疑問,跟Selenium 有啥區別?這裏我的簡單閱讀了下官方文檔,寫幾個粗淺的對比。html

clipboard.png

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過程當中的總結,也不曉得真正用到項目中會有哪些坑得踩。node

跟你們推薦一個學習資料分享羣:903217991,裏面大牛已經爲咱們整理好了許多的學習資料,有自動化,接口,性能等等的學習資料!人生是一個逆水行舟的過程,不進則退,我們一塊兒加油吧!react

TestCafe 快速入門

安裝

由於是Node.js 項目,能夠直接經過npm安裝,全局安裝以下git

npm install -g testcafe

快速Demo一個

baidu.jsgithub

fixture `baidu demo`
    .page `https://www.baidu.com`;

test('baidu search', async t=>{
   await t.typeText('#kw',"hao123")
       .click('#su')
});

經過Chrome運行web

testcafe chrome baidu.js

上面代碼看不懂不要緊,感覺下TestCafe就行。chrome

Cases管理

自動化測試,終歸仍是測試,是測試就離不開測試用例,那TestCafe如何組織管理測試用例?express

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");
});

運行結果:
clipboard.png
其中你會發現每一個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")
});

運行結果:
clipboard.png

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")
});

運行結果:
clipboard.png
注意: 從控制檯輸出看,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

1.建立Selectors

import { Selector } from 'testcafe';

2.使用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/t...

操做

元素操做其實上面例子咱們已經用過點擊,文本輸入等方法了,官方也給了很全的api文檔和demo:http://devexpress.github.io/t... ,這裏就講下一些比較特殊的元素或瀏覽器的操做。

- resizeWindow():設置窗口大小
- t.maximizeWindow( ):最大化窗口

fixture`demo`.page('https://www.baidu.com');

test('設置win窗口大小', async I => {
    await I.resizeWindow(300, 500)
              ..maximizeWindow( );
});

- getBrowserConsoleMessages():獲取頁面控制檯消息

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/t...

特性

在介紹幾個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提供的模擬器。

clipboard.png

testcafe "chrome:emulation:device=iphone x" xxx.js

設備模擬器更多參數查看:http://devexpress.github.io/t...

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);
    });

結語:

跟你們推薦一個學習資料分享羣:903217991,裏面大牛已經爲咱們整理好了許多的學習資料,有自動化,接口,性能等等的學習資料!人生是一個逆水行舟的過程,不進則退,我們一塊兒加油吧!

相關文章
相關標籤/搜索