Cypress vs Playwright——哪个 JavaScript 测试框架更好?

2145 篇文章 2 订阅
2011 篇文章 14 订阅

10 年前,自动化测试人员如果要编写 E2E 测试,主要使用 Selenium。每个有机会使用该解决方案的人都应该会记得设置、编写和调试是多么不愉快。在此过程中还创建了一些更有趣的自动化工具,例如 Webdriver.io、TestCafe、Nightwatch.js 和 Puppeteer。但是我们今天不会关注那些,因为我想谈谈Cypress和Playwright中测试自动化的几个问题。Cypress 和 Playwright是目前为测试实现创建理想工具的顶级框架。

两个框架,相似……

  Cypress 是一种基于 JavaScript 的前端测试工具。设计为对开发人员友好,它直接在浏览器中运行。 Cypress 的第一个版本于 2015 年发布,并于 2018 年开始真正流行,并于 2020 年筹集了 4000 万美元的 B 轮融资。他们以开源为荣,他们相信这将帮助他们在竞争中取得领先。Playwright(微软),于2020年发布。它是一个用于端到端测试的跨浏览器自动化库,也是开源的。它最初是一个 JavaScript 库,但现在已经扩展到还支持 Python、NET 和 Java。它是专门为网络自动化而创建的。

尽管这两个框架都很年轻,但它们提供了许多很棒的工具,使测试实施变得有趣而不乏味。在本文中,我们将重点关注十几个选定的类别,在我看来,这些类别对开发人员和测试工程师至关重要。质量保证是我们认真对待的事情。
它们如下:
1.与元素交互
2.导航
3.处理警报
4.内嵌框架支持
5.等待
6.要求语言支持
所讨论的示例是针对前端部分的 TheInternet 应用程序和 API 部分的 Reqres.in 实现的。

与元素交互

  在第一类中,将比较 Cypress 和 Playwright 的元素交互示例及其断言。测试任务是转到复选框页面,验证项目的数量,验证第一个项目未被选中,选择一个项目,然后验证该项目是否被正确选择。

Cypress:

describe('Interacting with elements', () => {

  it('First example', () => {

    cy.visit('/checkboxes');


    cy.get('[type="checkbox"]')

      .should(($elm) => {

        expect($elm).to.have.length(2);

      })

      .eq(0)

      .should('not.be.checked')

      .check()

      .should('be.checked');

  });


  it('Second example', () => {

    cy.visit('/checkboxes');


    cy.get('[type="checkbox"]').then(($elm) => {

      expect($elm).to.have.length(2);

      expect($elm[0]).not.be.checked;

      cy.wrap($elm[0])

        .click()

        .then(($elm) => {

          expect($elm).to.be.checked;

        });

    });

  });

});

Playwright:

const { test, expect } = require('@playwright/test');


test('Interacting with elements', async ({ page }) => {

  await page.goto('/checkboxes');

  const checkboxes = page.locator('[type="checkbox"]');

  const firstCheckbox = checkboxes.nth(0);


  await expect(await checkboxes.count()).toEqual(2);

  await expect(firstCheckbox).not.toBeChecked();

  await firstCheckbox.check();

  await expect(firstCheckbox).toBeChecked();

});

在 Cypress 的实现中,代码乍一看可能比 Playwright 的可读性差一些,为了对给定元素执行操作,必须从 cy.get() 链接。在 Playwright 中,由于每个用户都能毫无问题地理解,可以轻松地将租户分配给变量。通常对元素执行复杂的操作与所谓的callback hell 相关联。在我看来,微软团队的实现肯定更令人赏心悦目。

导航

  下一个类别将验证框架如何处理在 Cypress 与 Playwright 中打开新页面的问题。在测试中,用户单击链接,他们将被重定向到新选项卡中的页面。

Cypress:

describe('Multiple windows', () => {

  it('Windows support - not supported', () => {

    cy.visit('/windows');


    cy.get('[href="/windows/new"]').click();


    cy.get('h3').should('have.text', 'New Window');

  });


  it('Windows support - removing "target" attribute', () => {

    cy.visit('/windows');


    cy.get('[href="/windows/new"]').invoke('removeAttr', 'target').click();


    cy.location('pathname').should('eq', '/windows/new');

    cy.get('h3').should('have.text', 'New Window');

  });

});

Playwright:

const { test, expect } = require('@playwright/test');


test('Windows support', async ({ page, context }) => {

  await page.goto('/windows');


  let [newPage] = await Promise.all([

    context.waitForEvent('page'),

    page.locator('[href="/windows/new"]').click(),

  ]);


  await newPage.waitForLoadState();

  const newWindowElement = newPage.locator('h3');


  expect(newPage.url()).toContain('/windows/new');

  await expect(newWindowElement).toHaveText('New Window');

});

不幸的是,Cypress 不支持在一次测试会话期间打开新窗口,如果被测试的应用程序需要它,这对很多人来说都是一个问题。但是有一个技巧可以绕过这个限制。但是,如果需要留在原始页面上,将无法访问它,因此双方之间的任何交互仍然很困难。使用 Playwright,可以打开的窗口数量没有限制。用户可以完全控制他想要验证的内容。它可以随时与任何上下文相关。

处理警报

警报通常是“旧”JavaScript 的残余,如今它们在现代应用程序中越来越不常见。不幸的是,在自动化过程中,并不总是能接触到现代框架。需要能够处理警报。在这个例子中,将分析Alert、Confirm和Prompt的例子。

Cypress:

describe('Handling Alerts in browser', () => {

  beforeEach(() => {

    cy.visit('/javascript_alerts');

  });


  it('Click "OK" on JS Alert', () => {

    cy.contains('Click for JS Alert').click();


    cy.on('window:alert', (alert) => {

      expect(alert).to.eq('I am a JS Alert');

    });

    cy.on('window:confirm', () => true);

    cy.get('#result').should('have.text', 'You successfully clicked an alert');

  });


  it('Click "OK" on JS Confirm', () => {

    cy.contains('Click for JS Confirm').click();


    cy.on('window:confirm', (str) => {

      expect(str).to.equal(`I am a JS Confirm`);

    });

    cy.on('window:confirm', () => true);

    cy.get('#result').should('have.text', 'You clicked: Ok');

  });


  it('Click "Cancel" on JS Confirm', () => {

    cy.contains('Click for JS Confirm').click();


    cy.on('window:confirm', (str) => {

      expect(str).to.equal(`I am a JS Confirm`);

    });

    cy.on('window:confirm', () => false);

    cy.get('#result').should('have.text', 'You clicked: Cancel');

  });


  it('Fill JS Prompt', () => {

    cy.window().then(($win) => {

      cy.stub($win, 'prompt').returns('This is a test text');

      cy.contains('Click for JS Prompt').click();

    });

    cy.get('#result').should('have.text', 'You entered: This is a test text');

  });

});

Playwright:

 
  1. const { test, expect } = require('@playwright/test');

  2. test.describe('Handling Alerts in browser', () => {

  3. test.beforeEach(async ({ page }) => {

  4. await page.goto('/javascript_alerts');

  5. });

  6. test('Click "OK" on JS Alert', async ({ page }) => {

  7. page.on('dialog', (dialog) => {

  8. expect(dialog.message()).toBe('I am a JS Alert');

  9. dialog.accept();

  10. });

  11. const button = page.locator('button >> text=Click for JS Alert');

  12. await button.click();

  13. const result = page.locator('#result');

  14. await expect(result).toHaveText('You successfully clicked an alert');

  15. });

  16. test('Click "OK" on JS Confirm', async ({ page }) => {

  17. page.on('dialog', (dialog) => {

  18. expect(dialog.message()).toBe('I am a JS Confirm');

  19. dialog.accept();

  20. });

  21. const button = page.locator('button >> text=Click for JS Confirm');

  22. await button.click();

  23. const result = page.locator('#result');

  24. await expect(result).toHaveText('You clicked: Ok');

  25. });

  26. test('Click "Cancel" on JS Confirm', async ({ page }) => {

  27. page.on('dialog', (dialog) => {

  28. expect(dialog.message()).toBe('I am a JS Confirm');

  29. dialog.dismiss();

  30. });

  31. const button = page.locator('button >> text=Click for JS Confirm');

  32. await button.click();

  33. const result = page.locator('#result');

  34. await expect(result).toHaveText('You clicked: Cancel');

  35. });

  36. test('Fill JS Prompt', async ({ page }) => {

  37. page.on('dialog', (dialog) => {

  38. expect(dialog.message()).toBe('I am a JS prompt');

  39. dialog.accept('This is a test text');

  40. });

  41. const button = page.locator('button >> text=Click for JS Prompt');

  42. await button.click();

  43. const result = page.locator('#result');

  44. await expect(result).toHaveText('You entered: This is a test text');

  45. });

  46. });

  Playwright 使用相同的实现来处理所有类型的警报。用户可以轻松地验证给定窗口的内容,例如,选择他感兴趣的按钮。就 Cypress 而言,原生弹出窗口的处理不够完善。在每种情况下,3 种类型的窗口和运行无头测试的代码都不同,用户无法查看窗口是否得到了正确处理。

内嵌框架支持

  在实施测试时,通常需要使用 iframe,例如以外部支付网关的形式。过去,使用 iframe 对自动化测试人员来说是个大问题——现在这个过程要容易得多。在此挑战中,将尝试获取给定的 iframe 并将文本输入其中。

Cypress:

 
  1. it('Iframe support', () => {

  2. cy.visit('/iframe');

  3. const iframe = cy

  4. .get('#mce_0_ifr')

  5. .its('0.contentDocument.body')

  6. .should('be.visible')

  7. .then(cy.wrap);

  8. iframe.clear().type('Some text').should('have.text', 'Some text');

  9. });

Playwright:

 
  1. const { test, expect } = require('@playwright/test');

  2. test('Iframe support', async ({ page }) => {

  3. await page.goto('/iframe');

  4. const frame = page.frameLocator('#mce_0_ifr');

  5. const frameBody = frame.locator('body');

  6. await frameBody.fill('');

  7. await frameBody.fill('Some text');

  8. await expect(frameBody).toHaveText('Some text');

  9. });

  在 Playwright 中,使用 frameLocator() 方法可以轻松访问 Iframe。要在 Iframe 中执行操作,使用类似的定位器 (),可以在其中自由执行操作,例如键入文本、单击元素或用户执行的其他操作。另一方面,Cypress并不那么容易支持 iframe。如果要在iframe中执行操作,需要安装npm cypress-iframe包,然后将插件添加到commands.js中。最后,在测试或自定义命令中,创建一个辅助函数,在其中传递要对其执行操作的元素。同样,我的印象是 Cypress 比 Playwright 更难执行简单的 Iframe 操作。你怎么认为?

等待

  较旧的框架在等待加载缓慢的元素时总是存在问题,这有时会导致添加硬超时以验证给定元素的可见性。它非常坚持保持代码不稳定。

Cypress:

 
  1. it('Waiting for lazy elements', () => {

  2. cy.visit('/dynamic_loading/2');

  3. cy.contains('button', 'Start').click();

  4. // Elements is loading longer then global timeout: 5_000

  5. cy.get('#finish', { timeout: 10_000 }).should('be.visible').should('have.text', 'Hello World!');

  6. });

Playwright:

 
  1. const { test, expect } = require('@playwright/test');

  2. test('Waiting for lazy elements', async ({ page }) => {

  3. await page.goto('/dynamic_loading/2');

  4. await page.getByText('Start').click();

  5. const finish = page.locator('#finish');

  6. // Elements is loading longer then global timeout: 5_000

  7. await expect(finish).toBeVisible({ timeout: 10_000 });

  8. await expect(finish).toHaveText('Hello World!');

  9. });

  在 Cypress 和 Playwright 中,如果需要等待一个元素,不需要做任何额外的工作。唯一的额外工作是向元素添加 Cypress 超时。 Cypress 时不时地使用 cy.get,然后在页面上检查给定元素是否存在。如果该元素在默认超时(5_000m s)内不存在,则认为该元素不会出现。可以轻松地在全局范围内增加超时,或者仅针对具有延长加载时间的元素。在 Playwright 中,此机制可用于网络优先断言。在这种情况下,确保元素可见,然后验证其可见性。两种解决方案都令人满意,因为不必等待持续 10_000 毫秒的特定超时,一次可以是 4_500 毫秒,然后 9_500 毫秒,如果用时少于 10 秒,测试将通过。

Requests

  测试 Web 应用程序不仅是关于前端元素的验证,一个常见的工作元素也是验证 API 端的东西。可以直接从 Cypress 和 Playwright 框架中的测试代码拦截和发送请求。我们也有模拟查询的能力(请留意关于这个主题的另一篇文章,因为这个主题绝对值得深入研究)。可以直接从 Cypress 和 Playwright 框架中的测试代码拦截和发送请求。在这个简单的示例中,要执行的任务是针对特定用户向 Reqres.in 应用程序发送一个简单的“GET”查询。

Cypress:

 
  1. it('Request support - "then" example', () => {

  2. cy.request('GET', 'https://reqres.in/api/users/2').then(({ status, body }) => {

  3. expect(status).to.equal(200);

  4. const { email, id } = body.data;

  5. expect(typeof email).to.be.equal('string');

  6. expect(typeof id).to.be.equal('number');

  7. expect(email).to.be.equal('janet.weaver@reqres.in');

  8. expect(id).to.be.equal(2);

  9. });

  10. });

  11. it('Request support - "chain" example', () => {

  12. cy.request('GET', 'https://reqres.in/api/users/2').as('response');

  13. cy.get('@response').its('status').should('eq', 200);

  14. cy.get('@response')

  15. .its('body.data')

  16. .then(({ email, id }) => {

  17. expect(typeof email).to.be.equal('string');

  18. expect(typeof id).to.be.equal('number');

  19. expect(email).to.be.equal('janet.weaver@reqres.in');

  20. expect(id).to.be.equal(2);

  21. });

  22. });

Playwright:

 
  1. const { test, expect } = require('@playwright/test');

  2. test.use({

  3. baseURL: 'https://reqres.in',

  4. });

  5. test('Request support', async ({ request }) => {

  6. const response = await request.get('/api/users/2');

  7. await expect(response).toBeOK();

  8. const body = await response.json();

  9. const { email, id } = body.data;

  10. expect(typeof email).toBe('string');

  11. expect(typeof id).toBe('number');

  12. expect(email).toEqual('janet.weaver@reqres.in');

  13. expect(id).toEqual(2);

  14. });

  Cypress 和 Playwright 中请求的实现几乎是彼此的双胞胎。在 Cypress 中,有一个 .request() 方法,它将 URL 和要执行的方法作为参数,然后在回调中,得到一个响应,在解构之后,可以将其分为状态、正文、持续时间等在下一步中,用户使用来自 Chai.js 库的简单断言。在 Playwright 中,有一个同名的方法来发出请求并指定要使用的 REST API 方法。作为此解决方案的一大优势,可以将给定查询的响应分配给变量,可以随时使用 .json() 方法获取感兴趣的变量。如果目标是验证来自服务器的响应,或者如果愿意,可以使用 .status() 。检查状态码。在 Cypress 中,为了能够访问答案,应该将答案保存为别名,然后通过访问别名,以适当的方式验证其他内容。亲自看看哪种实施方式更适合。

语言支持选择

  自动化测试框架的关键方面之一是该工具支持的编程语言。在整个团队使用特定语言编写的情况下,使用相同语言编写测试以在合并请求期间支持整个团队要容易得多。目前,前端的很大一部分是用 JavaScript 或 TypeScript 编写的。两者都得到 Cypress 和 Playwright 的支持!

但是,如果团队使用 Java、Python 或 .NET (C#) 编写,则只有 Playwright 才能完成这项工作。许多测试人员在将 Selenium 与 Java 或 PyTest 结合使用方面具有丰富的经验,因此如果不会 JavaScript,则入门门槛不涉及需要学习一门新语言。如果选择 Cypress,团队有效执行测试需要 JS 知识。

最后: 下方这份完整的软件测试视频学习教程已经整理上传完成,朋友们如果需要可以自行免费领取【保证100%免费】

在这里插入图片描述

 这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值