Puppeteer之如何提升脚本稳定性

UI层自动化测试脚本中,脚本不稳定性一直是困扰测试人员很大的一个点,此次课程将学习如果选择puppeteer作为测试框架,有哪些策略可以提高脚本稳定性。

试想手动完成一个测试案例的执行流程步骤是准备测试数据、执行测试、验证结果。UI层自动化测试只是把手动执行的过程转换为脚本自动运行而已,所以自动化脚本总的来说也是准备数据、模拟人在web应用上点击页面元素完成业务流程操作、验证执行结果三个步骤。其中数据准备和模拟人执行测试是影响脚本稳定性的两个方面,下面就从这两个方面列举了一些常见的引起脚本不稳定的因素,并给出了解决方法,具体内容如下所示

上面提到的等待处理、利用接口和数据库准备测试数据、分环境管理测试数据等在前面的课程中已经讲解过,这里不再重复说明,本次课程只给大家演示如何实现retry机制,retry主要分为案例级别retry和步骤级别retry,接下来,我们先看看如何实现案例级别retry。

实现案例级别retry

实际,案例级别的retry很简单,jest框架自身就提供了失败案例retry的机制。要使用jest框架提供的失败案例retry机制,首选执行“npm install jest-circus”安装jest-circus。接着在“jest.config.js”中配置testRunner,配置内容如下所示:

module.exports= {
    preset: 'jest-puppeteer',
    setupFilesAfterEnv: ['expect-puppeteer','./jest.setup.js'],
    testRunner: 'jest-circus/runner',  
    // 将testRunner设置为jest-circus
};

编写脚本,并在脚本上设置retry次数即可,如下所示,第一个脚本是错误的脚本,运行会失败,第二个脚本是正确的脚本,能成功运行。同样,执行“npm run retry-failed-case”即可运行下面的案例。

jest.retryTimes(1);
// 设置失败后retry的次数

describe("retry case demo", () => {
    it("retry failed case", async () => {
        await page.goto("https://angular.realworld.io/");
        await console.log("open app");
        await page.waitForSelector('app-layout-header li a[href="/login"]');
        await page.click('app-layout-header li a[href="/login"]');
        await console.log("click sign in");
        await page.click('app-layout-header li a[href="/registers"]');
        await console.log("click register");
    });

    it("does not retry succes case",async() => {
        await page.goto("http://juliemr.github.io/protractor-demo/");
        await page.type('input[ng-model="first"]', '5');
        await page.select('select[ng-model="operator"]','SUBTRACTION');
        await page.type('input[ng-model="second"]',  '3');
        await page.click('#gobutton');
        await console.log("run success case")
    })
});

运行上面的案例结果如下所示,可以看到打印的日志信息,打印了两次第一个案例中的console.log()中的信息,说明当案例运行失败后,确实重新运行了案例。如果案例运行成功,不会进行retry。

上面只是演示了retry机制的demo,实际场景中不能简单这样处理,大家可以想象一下,所有的案例运行前都会进行登陆操作,即所有案例运行前都会执行登陆步骤,按上面retry的机制,第一次失败后,程序在浏览器上再次输入应用的baseUrl进行登陆操作,但此时用户可能已处于登陆状态,因为上一次脚本失败的地方很可能是登陆后的某个操作。那么,如何让retry机制正常运行呢?这里就需要添加afterEach语句,让用户退出登陆,恢复到未登陆状态。下面是优化后的案例脚本,同样执行“npm run retry-failed-case-refactor”即可运行下面的案例

jest.retryTimes(1);
describe("retry case demo", () => {
    afterEach(async() => {
        await expect(page).toClick('app-layout-header li a[href="/settings"]');
        await expect(page).toClick('app-settings-page button.btn.btn-outline-danger');
    });
    //调用afterEach,如果用户已经成功登陆,那么进行登出操作,让用户恢复到未登陆状态

    it("retry failed case", async () => {
        await page.goto("https://angular.realworld.io/");
        await expect(page).toClick('app-layout-header li a[href="/login"]');
        await expect(page).toFill('app-auth-page form input[formcontrolname="email"]',"e2etest@163.com");
        await expect(page).toFill('app-auth-page form input[formcontrolname="password"]',"12345678");
        await expect(page).toClick('app-auth-page button[type="submit"]');
        await expect(page).toClick('app-auth-page button123');
        //这里输入错误的脚本,模拟失败案例
    });

    it("do not retry success case",async() => {
        await page.goto("https://angular.realworld.io/");
        await expect(page).toClick('app-layout-header li a[href="/login"]');
        await expect(page).toFill('app-auth-page form input[formcontrolname="email"]',"e2etest@163.com");
        await expect(page).toFill('app-auth-page form input[formcontrolname="password"]',"12345678");
        await expect(page).toClick('app-auth-page button[type="submit"]');
        await console.log("second success case just do login")
        //这里模拟测试该应用的另外场景,所有场景都会进行登陆操作,每个案例运行后都进行登出操作,还可以隔离案例间的影响,因为不同案例使用的用户可能不同
    })
});

 这里,再次强调下,为了案例间不相互影响或者retry机制能正常运行,同一个describe()下有多个it()测试场景时,可以在beforeEach()中完成每个测试场景的登陆操作,afterEach()中完成登出操作。

上面演示了案例级别的retry,在编写脚本过程中还可以根据业务情况编写步骤级别的retry,接下来就看看如何实现步骤级别的retry。

实现步骤级别retry

步骤级别的retry思路是包一个for或者while循环,设置循环次数,在循环内添加if()判断,当if()为true时进行补救操作,当if()为false时继续执行脚本中后续step。能否实现步骤级别的retry有2个关键点,第一:能否找到if()中的条件判断,第二:能否有补救操作。即当判断到页面不符合预期时,能否进行一些补救措施,例如强刷页面进行补救等等。这两个点都需要结合实际的业务系统才能找到,如果找不到这两个点,那么基于步骤级别的retry就无法实施。

因为没有实际的应用演示基于步骤级别的retry,下面的代码更像是伪代码,目的是给大家演示写脚本的思路。同样,执行“npm run retry-step”即可运行下面的案例

//这个被测应用是前面练习过的应用,当输入5-3后点击go button后,会等1-2秒才会显示结果,我们把这里假设称一个页面不稳定的情况,通过步骤级别的retry让脚本更稳定
describe("retry step demo", () => {
    it("should retry step successfully", async () => {
        await page.goto('http://juliemr.github.io/protractor-demo/');
        await page.type('input[ng-model="first"]', '5');
        await page.select('select[ng-model="operator"]', 'SUBTRACTION');
        await page.type('input[ng-model="second"]', '3');
        await page.click('#gobutton');
        let i = 0;
        while (i < 3) {
            //通过whild循环达到retry的效果

            if (page.$eval('h2', el => {return el.innerText}) == 2) { 
                //if里面进行判断,当if里面能获取到h2这个页面元素内容且等于2时,才进行下面的校验操作

                await expect(page.$eval('h2', el => {return el.innerText})).toEqual('2')
               //if为true后才进行下一步操作
            }
            i = i + 1;
            console.log("retry times" + i - 1)
        }
    });
});

上面就是如何实现步骤级别和案例级别的retry,实际大家可以想一下,前面提到的等待处理,实际也是一种retry机制。例如page.waitForSelector(),程序不断去尝试获取页面元素,如果未获取到继续retry获取,直到达到超时时间。

retry机制实际是自动化脚本维护中比较通用的一种提高稳定性的思路,不仅是UI层自动化测试,接口层自动化测试同样适用。如果出现脚本稳定性不高,都可以考虑是否可以通过retry解决不稳定性因素。当然,retry也要适度,retry次数越多,用例运行时间越长,不利于快速反馈项目质量。如果所有案例都启动retry机制,那么可能会遗漏一些非必显的issue,比如某个场景总是retry后才成功等,所以,retry也要根据具体情况分析,不可滥用。

上面的策略都是优化自动化测试脚本自身,如果因为第三方接口不稳定导致自动化测试难以维护,那么可以创建mock服务,模拟不稳定的第三方接口。例如使用wiremock模拟依赖接口,详细信息可查看wiremock官网介绍。

至此,此篇博客内容就结束了,最后,再强调一点,上面只是列举了一些常见的不稳定因素,自动化脚本也需要持续优化,要优化,首先就要找到不完善的点,那如何找到不完善的点,CICD平台上频繁并发运行是最好的方式,根据结果不断提出新的目标,自动化测试的覆盖率、成功率、反馈时间是我们需要持续关注的三个度量指标。下次课程将学习如何提高脚本可读性、获取度量指标内容。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

taoli-qiao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值