作者观点:很长时间以来,Selenium是QA工程师寻求测试自动化解决方案的首选测试框架。它能够测试任何浏览器(这在IE浏览器的统治时期尤其重要)和任何平台。然而,现在看来,那个时代已经过去了。

今天,大多数web应用程序都是用现代前端框架开发的,如React、Angular或Next.js,所有的浏览器可以分为三类:基于Chromium引擎的浏览器,在Gecko上运行的Firefox,以及在WebKit上运行的Safari。

自Playwright出现以来,Chrome、Firefox和Safari都可以支持。这就消除了曾经普遍存在的需要跨浏览器框架的说法。所以,可以说Playwright涵盖了一切。

Playwright卓越的跨浏览器测试能力是由于其独特的架构。与Selenium不同,Playwright在API层面与浏览器进行交互,并绕过传统浏览器自动化框架的限制,从而能够更有效地发挥作用。这使得它更快,更可靠,更容易维护。

Playwright和Selenium一样,具有支持多种编程语言的独特优势,包括JavaScript、TypeScript、Java、Python和C#,这很容易集成到现有项目中,因此每个团队可以用他们最熟悉的语言工作。

总的来说,Playwright是一个强大而灵活的工具,可以帮助QA工程师简化测试过程,并使得web应用程序在所有主要的浏览器和平台上完美地运行。

为了总结Playwright官方网站上强调的突出功能,我们可以列出它们并逐一聊聊:

跨浏览器、跨平台和跨语言支持(Any browser • Any platform • One API)

弹性(Resilient • No flaky tests)

没有取舍的问题(No trade-offs • No limits)

简单的测试隔离和认证持久性(Full isolation • Fast execution)

Visual Studio Code集成,测试代码生成以及选择器挑选(Powerful Tooling)

安装

有两种方法来安装该软件。

第一种方法是使用Node.js包管理器,如npm。如果选择这种方法,需要在电脑上安装Node.js。对于其他语言,Python使用pypi,Java使用Maven,C#可以使用 “dotnet add package “等.NET CLI命令来安装playwright的依赖项。

第二种方法是安装VSCode的playwright扩展,它带有方便的安装命令。这种方法能够让整个过程更顺畅、更容易。

Playwright的第一个特点(跨浏览器、跨平台和跨语言支持),我们可以说,它支持所有现代浏览器,在任何平台上都没有真正的限制。虽然它也支持流行的语言和技术,但支持的最好的语言是Typescript/Javascript。例如,如果你想在Java中使用Playwright,你需要选择你喜欢的测试运行器,如JUnit,并管理Playwright对象,如BrowserContext和Page。此外,你还需要为HTML报告找到一个解决方案。

● ● ●

当选择一个新的测试框架时,往往需要考虑权衡和限制。虽然现代测试框架通常专注于现代技术和前端框架,但QA工程师可能仍然需要处理用JSF等旧技术开发的遗留项目,以及使用iFrame或其后续的shadow-roots等组件的Web应用。因为,即便是Web应用组件的缓慢响应时间也会对现代测试框架构成真正的挑战。

根据我的经验,用Selenium自动化JSF应用程序要比用其他现代测试框架(如Playwright、Cypress或Puppeteer)更无缝。对于现代Web框架,我推荐Playwright或类似的测试框架。虽然任何Web应用程序仍然可以使用Selenium进行自动化,但具有许多影子根元素的应用程序可能需要一个更强大的方法。用Playwright穿透影子DOM可以使自动化变得更容易,而且脚本对未来的变化更有弹性。综上所述,选择Playwright并不需要很多取舍。然而,需要注意的是,它的重点是现代web应用和速度。

● ● ●

为了更好理解测试隔离的概念,我们首先应该看一下Playwright中的某些对象(objects)。要创建一个利用API库的自动化脚本,必须打开一个浏览器会话。现在,一个浏览器对象包含一些方法,其中之一是newContext()。正是这个对象能够开始一个新的会话,就像我们在电脑上开始一个新的隐身浏览器会话一样。而没有必要结束当前的主浏览器会话,例如,如果我们需要用不同的用户登录来检查变化。

(async () => {  const browser = await playwright.firefox.launch();  // Or 'chromium' or 'webkit'.    // Create two isolated browser contexts  const adminContext = await browser.newContext();  const userContext = await browser.newContext();    // Create pages and interact with contexts independently  const adminPage = await adminContext.newPage();  await adminPage.goto('https://example.com/admin');  const userPage = await userContext.newPage();  await userPage.goto('https://example.com/user');  // Gracefully close up everything  await adminContext.close();  await userContext.close();  await browser.close();})();

Playwright还提供了一种方法来实现登录和在json文件中保存cookie会话。现有的会话可以在每次新的测试需要会话时使用,节省了登录应用程序的时间,大大减少了执行时间。

// auth.setup.tsimport { test as setup } from '@playwright/test';const authFile = 'playwright/.auth/user.json';setup('authenticate', async ({ page }) => {  // Perform authentication steps. Replace these actions with your own.  await page.goto('https://github.com/login');  await page.getByLabel('Username or email address').fill('username');  await page.getByLabel('Password').fill('password');  await page.getByRole('button', { name: 'Sign in' }).click();  // End of authentication steps.  await page.context().storageState({ path: authFile });});

● ● ●

Playwright提供的工具很强大,然而必须注意的是,这只有在你使用VSCode时才有。如果选择Java和IntelliJ,就不能使用其中的一些功能。

Playwright的卖点之一无疑是其自动代码生成功能。虽然这个功能并不新鲜,其他框架也有这个功能,但首先生成代码,然后改进它,总是很实用

编写定位器和现场调试是相当有帮助的。可以节省时间,并通过观察元素定位器的解析位置来提高编写健壮脚本的能力。然而,我必须承认,当创建、调试和执行动作都在同一个窗口中进行时,实时调试可以得到改善,并使之更加流畅。

编写测试

我们已经看到了一些关于如何编写测试的代码。如果你选择用Library API编写测试,你将不得不管理像Browser、BrowserContext和Page这样的对象。

Playwright的好处是它为我们提供了一个playwright/test依赖项,它可以导出测试、页面和期望等固定装置。先看看一个简单的例子:

import { test, expect } from '@playwright/test';test('has title', async ({ page }) => {  await page.goto('https://playwright.dev/');  // Expect a title "to contain" a substring.  await expect(page).toHaveTitle(/Playwright/);});test('get started link', async ({ page }) => {  await page.goto('https://playwright.dev/');  // Click the get started link.  await page.getByRole('link', { name: 'Get started' }).click();  // Expects the URL to contain intro.  await expect(page).toHaveURL(/.*intro/);});

在这个例子中,我们没有看到像Browser或BrowserContext这样的对象的创建。然而,我们必须知道,这些对象在我们的脚本中的每一次测试中都会自动创建和关闭。哪一个浏览器将被使用是在playwright.config文件中定义的。当然,不仅仅是使用哪个浏览器,还有很多配置可以在全局配置文件中定义。

运行测试

运行测试可以使用VSCode扩展或nodejs命令行。最简单的命令是npx playwright test。这个命令在TestDir(默认:./tests)中搜索所有扩展名为.spec.js或.spec.ts的文件。我们可以为特定的文件夹、文件、项目、无头选项等提供额外参数。更多细节,请参考官方文档(https://playwright.dev/docs/running-tests)。

固定装置(Fixtures)

如前所述,使用API库或Playwright Test编写测试是不同的。Playwright测试的基础是固定装置的概念,这基本上意味着我们正在创建一个环境,以便运行测试。Playwright带有一些内置的固定装置,其中一些是常用的。

● page类型的页面——测试运行的独立页面。

● BrowserContext类型的context——测试运行的孤立的上下文。页面夹具也属于这个上下文。

● browser of type 浏览器 ——浏览器在测试中被共享,以优化资源。

● browserName of type string——当前运行测试的浏览器的名称。可以是chromium、firefox或webkit。

● request of type APIRequestContext——测试运行的隔离的APIRequestContext实例。

如果我们遵循页面对象模型模式,我们可能会创建自己的页面类和对象实例。如果在多个测试中使用同一个实例,必须对测试进行分组,并在beforeEach hook中实例化该对象。我们可以使用 afterEach hook在每次测试执行结束时清理所有的东西。由于这可能是完美的工作,我们可以提取这段代码,并通过固定装置使其更可重复使用。让我们看看一个用页面对象制作的固定程序的例子:

// my-test.jsconst base = require('@playwright/test');const { TodoPage } = require('./todo-page');const { SettingsPage } = require('./settings-page');// Extend base test by providing "todoPage" and "settingsPage".// This new "test" can be used in multiple test files, and each of them will get the fixtures.exports.test = base.test.extend({  todoPage: async ({ page }, use) => {    // Set up the fixture.    const todoPage = new TodoPage(page);    await todoPage.goto();    await todoPage.addToDo('item1');    await todoPage.addToDo('item2');    // Use the fixture value in the test.    await use(todoPage);    // Clean up the fixture.    await todoPage.removeAll();  },  settingsPage: async ({ page }, use) => {    await use(new SettingsPage(page));  },});exports.expect = base.expect;

这个夹具(fixture)现在扩展了内置的测试夹具(test fixtures)并实例化了TodoPage类对象。我们可以进行一些操作,并通过方法的使用(todoPage)暴露这个对象。这允许我们在测试中使用该对象,要做到这一点,必须从夹具中导入测试,如下所示:

const { test, expect } = require('./my-test');

除了内置的固定装置,我们还可以使用自己的固定装置并将其结合起来。这些测试有干净的结构和一个抽象层,使测试更容易阅读。让我们看看完整的例子:

const { test, expect } = require('./my-test');test.beforeEach(async ({ settingsPage }) => {  await settingsPage.switchToDarkMode();});test('basic test', async ({ todoPage, page }) => {  await todoPage.addToDo('something nice');  await expect(page.getByTestId('todo-title')).toContainText(['something nice']);});

固定装置是Playwright的高级话题,但了解它们可以帮助我们解开Playwright的一些最佳功能。

总结

选择一个测试框架不是一件容易的事情,因为必须考虑到我们日常工作中的许多自动化场景。我们最不想做的事情是:使用一个新的花哨的框架,然后在一段时间后因为它没有满足我们的需求而抛弃它。可以看到,Playwright随着每个新版本的发布而逐渐成熟。

Playwright目前可以在任何技术栈中使用,并且是大多数团队进行前端应用自动化测试的首选。我很想知道你最喜欢哪些功能,以及是否有什么东西让你想坚持使用其他框架而不是Playwright,大家可以来聊一聊。

最后如果你想学习自动化测试和需要软件测试资料,欢迎加入笔者的交流群:320231853,里面会有很多测试资源和大佬答疑解惑,我们一起交流一起学习!