作者观点:很长时间以来,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.ts
import { 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.js
const 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随着每个新版本的发布而逐渐成熟。
最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:
这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!