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,里面大牛已经为咱们整理好了许多的学习资料,有自动化,接口,性能等等的学习资料!人生是一个逆水行舟的过程,不进则退,我们一块儿加油吧!

相关文章
相关标签/搜索