• UI自动化测试框架 ---TestCafe(转)


    什么是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.JSTypeScript 3. Node.js Tool。
    简单说就是Node.JS编写的Web端UI自动化测试框架。
    官网:http://devexpress.github.io/testcafe/

    TestCafe VS Selenium

    这时我想你跟我都有个疑问,跟Selenium 有啥区别?这里个人简单阅读了下官方文档,写几个粗浅的对比。

    对比项Selenium 3.xTestCafe谁更优
    社区 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");
    });
    

    运行结果:


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

    运行结果:


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

    运行结果:


     
    image.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';
    
    1. 使用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提供的模拟器。


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

    设备模拟器更多参数查看:http://devexpress.github.io/testcafe/documentation/using-testcafe/common-concepts/browsers/using-chrome-device-emulation.html

    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
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 相关阅读:
    Android权限大全(链接地址整理)
    Android6.0运行时权限(基于RxPermission开源库)
    Android6.0机型上调用系统相机拍照返回的resultCode值始终等于0的问题
    使用AccessibilityService模拟点击事件失败的分析
    Android混淆打包配置总结
    okhttputils开源库的混淆配置(Eclipse)
    Android布局中的空格以及占一个汉字宽度的空格,实现不同汉字字数对齐
    java开发者大会:总结
    JAVA开发者大会:拍拍贷MQ系统原理与应用
    消息总线真的能保证幂等?
  • 原文地址:https://www.cnblogs.com/dzone/p/13323152.html
Copyright © 2020-2023  润新知