相关文章:playwright系列教程
Playwright简介
微软出品的强大工具
Playwright是由微软推出的一款开源自动化测试工具,专门为Web测试和自动化场景而设计。在现代Web开发的快节奏环境下,其凭借出色的性能和丰富的功能,成为众多开发者进行自动化测试的得力助手,无论是前端开发者进行功能验证,还是测试人员构建全面的测试套件,Playwright都能很好地满足其需求。
Playwright的核心优势
跨浏览器支持特性
Playwright支持Chromium、Firefox和WebKit三大主流浏览器,并且为开发者提供了统一的API接口。这意味着无论你是针对Chrome进行测试,还是需要在Firefox或者Safari(基于WebKit)上验证功能,都可以使用相同的代码逻辑。例如,在测试一个电商网站的购物流程时,你可以轻松地在不同浏览器上运行相同的测试脚本,确保网站在各种浏览器环境下都能正常工作,为用户提供一致的体验。这种跨浏览器支持特性极大地提高了测试的效率和覆盖范围,减少了因浏览器差异而导致的兼容性问题。
自动等待与无闪测试机制
Playwright采用了先进的自动等待机制。在执行操作(如点击按钮、输入文本等)前,它会自动检查元素是否已处于可操作状态。例如,当一个页面加载后,某个按钮可能需要一定时间才会完全加载并可被点击,Playwright会等待该按钮具备可点击条件后再执行点击操作,而不是立即尝试点击导致测试失败。这种机制大大减少了测试中的不稳定因素,提高了测试的可靠性。同时,Playwright支持无闪测试,能够避免因页面元素闪烁或未完全加载而引起的误判,进一步提升了测试的准确性和稳定性,确保测试结果真实反映页面的实际状态。
丰富强大的工具链
Playwright配备了一系列丰富且实用的工具。其中,代码生成器可以帮助开发者快速生成测试脚本的基础代码,通过简单地操作页面,它就能自动记录并生成相应的代码片段,大大提高了开发效率。检查器则允许开发者在运行时查看页面元素的属性和状态,方便定位问题和调试测试脚本。追踪查看器能够记录测试过程中的各种事件和操作,为排查问题提供详细的信息。例如,在测试一个复杂的Web应用时,如果遇到某个操作未按预期执行的情况,开发者可以利用追踪查看器回溯整个操作过程,找出问题所在,从而快速修复问题,推动测试工作顺利进行。
多上下文管理能力
在现代Web应用中,常常会涉及多标签、多源甚至多用户的复杂测试场景,Playwright的多上下文管理能力使其能够轻松应对这些情况。例如,在测试一个社交媒体平台时,可能需要同时模拟多个用户在不同标签页中进行操作,如一个用户在一个标签页发布内容,另一个用户在另一个标签页进行评论和点赞。Playwright可以通过创建多个上下文,分别管理不同的页面实例和用户会话,确保各个操作之间相互独立且不受干扰,准确地模拟出真实世界中的复杂用户行为,为全面测试Web应用的功能和性能提供了有力支持。
Playwright的基本概念
Browser(浏览器实例)
Browser是Playwright中的一个重要概念,它代表着一个可以启动新浏览器窗口的实例。通过Browser,我们可以创建多个Page实例,实现多页面的同时操作和管理。例如,在进行一些需要同时打开多个页面并在不同页面间切换操作的测试场景中,首先需要创建Browser实例,然后基于该实例创建相应的Page实例,如下代码所示:
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
// 后续可以在这个browser实例上进行更多操作,如创建Page实例
await browser.close();
})();
这里,chromium.launch()
方法创建了一个Chromium浏览器实例,我们可以根据实际测试需求对这个实例进行进一步的配置和操作,如设置浏览器窗口大小、是否为无头模式等,然后在完成所有操作后,使用browser.close()
方法关闭浏览器实例,释放资源。
Page(页面实例)
Page代表着打开的一个标签页,是我们进行各种操作的主要对象。我们可以在Page上执行诸如点击按钮、输入文本、获取元素信息等操作。例如,在测试一个登录页面时,我们可以通过Page实例定位到用户名输入框和密码输入框,并输入相应的账号和密码,然后点击登录按钮,代码如下:
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://example.com/login');
await page.fill('input[name="username"]', 'testuser');
await page.fill('input[name="password"]', 'testpassword');
await page.click('button[type="submit"]');
// 可以继续在这个page上进行后续操作,如验证登录是否成功
await browser.close();
})();
在上述代码中,browser.newPage()
创建了一个新的Page实例,然后通过page.goto()
方法打开登录页面,接着使用page.fill()
方法向用户名和密码输入框中填充数据,最后使用page.click()
方法点击登录按钮,模拟了用户的登录操作流程。
ElementHandle(元素句柄)
ElementHandle代表着页面中的一个元素,通过它我们可以对页面元素进行更细致的操作,如获取元素的属性、文本内容,执行元素的特定方法等。例如,在测试一个列表页面时,我们可以通过ElementHandle获取列表项的数量,并检查特定列表项的文本内容是否符合预期,代码如下:
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://example.com/list');
const listItems = await page.$$('li');
console.log(`列表项数量: ${listItems.length}`);
const firstItem = listItems[0];
const textContent = await firstItem.textContent();
console.log(`第一个列表项内容: ${textContent}`);
await browser.close();
})();
在这段代码中,page.$$('li')
返回了一个包含所有li
元素的ElementHandle数组,我们可以通过这个数组获取列表项的数量,并通过firstItem.textContent()
获取第一个列表项的文本内容,从而对页面元素进行详细的检查和验证,确保页面的展示和数据符合预期。
Playwright与浏览器的交互方式
通过底层协议通信
Playwright通过与浏览器的底层协议通信来发送命令并接收结果。它利用浏览器提供的调试协议(如Chrome DevTools Protocol),能够深入地控制浏览器的行为和获取详细的页面信息。与一些传统的自动化测试工具相比,这种方式能够实现更精准、高效的控制。例如,在获取页面性能数据时,Playwright可以通过底层协议直接从浏览器获取诸如页面加载时间、资源加载顺序和时间等详细信息,而不仅仅是表面的页面状态。这使得开发者能够更全面地了解页面的性能表现,发现潜在的性能瓶颈,并进行针对性的优化。同时,在执行复杂的操作(如模拟鼠标移动轨迹、处理复杂的JavaScript事件)时,通过底层协议可以确保操作的准确性和稳定性,提高测试的可靠性和真实性。
API的运用
Playwright提供了丰富的API,涵盖了各种常见和复杂的操作场景。例如,在处理弹出窗口时,它提供了专门的API来监听、接受或拒绝弹出窗口,确保测试流程不会因为意外的弹出窗口而中断。当测试一个网站的文件上传功能时,Playwright的文件上传API可以方便地选择本地文件并模拟上传操作,代码如下:
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://example.com/upload');
const [fileChooser] = await page.waitForEvent('filechooser');
await fileChooser.setFiles('path/to/local/file.jpg');
// 继续后续的测试操作,如检查文件是否上传成功
await browser.close();
})();
在上述代码中,page.waitForEvent('filechooser')
等待文件选择器弹出事件,然后通过fileChooser.setFiles()
方法选择本地文件进行上传,整个过程简洁明了,且能够准确地模拟用户的实际操作,体现了Playwright API的强大和易用性,使得开发者能够轻松应对各种复杂的测试场景,提高测试的效率和质量。
Playwright的元素定位方法
get_by_XXXXX系列定位
Playwright提供了一系列方便的get_by_XXXXX
定位方法,例如get_by_role
、get_by_text
、get_by_placeholder
等。get_by_role
方法根据元素的角色(如按钮、链接、输入框等)进行定位,这对于遵循无障碍设计原则的页面元素定位非常有效。例如,在测试一个具有良好无障碍设计的表单页面时,如果我们要定位提交按钮,代码如下:
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://example.com/form');
const submitButton = await page.get_by_role('button', { name: '提交' });
// 对提交按钮进行操作,如点击按钮
await submitButton.click();
await browser.close();
})();
在上述代码中,page.get_by_role('button', { name: '提交' })
通过按钮的角色和名称准确地定位到了提交按钮,这种定位方式不仅简洁,而且具有较高的可读性和可维护性,即使页面结构发生一定变化,只要元素的角色和关键文本信息不变,定位依然有效。
get_by_text
方法则根据元素包含的文本内容进行定位,这在定位一些具有特定文本标识的元素时非常实用。例如,在测试一个文章列表页面时,如果要定位特定标题的文章链接,代码如下:
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://example.com/articles');
const articleLink = await page.get_by_text('特定文章标题');
// 可以对文章链接进行点击等操作,查看文章详情
await articleLink.click();
await browser.close();
})();
这里,page.get_by_text('特定文章标题')
能够快速准确地定位到包含指定文本的元素,方便进行后续的操作和验证。
get_by_placeholder
方法根据输入框的占位符文本进行定位,适用于定位那些用户尚未输入内容时具有提示性占位符文本的输入框。例如,在测试一个注册页面时,如果要定位用户名输入框,代码如下:
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://example.com/register');
const usernameInput = await page.get_by_placeholder('请输入用户名');
// 对用户名输入框进行操作,如输入用户名
await usernameInput.fill('testuser');
await browser.close();
})();
通过page.get_by_placeholder('请输入用户名')
,我们可以精准地定位到用户名输入框,进行后续的输入等操作,提高测试脚本的准确性和稳定性。
locator定位
locator定位是Playwright中一种强大且灵活的定位方式,它可以通过元素的id、css选择器、xpath等多种方式进行定位。当页面元素具有唯一的id属性时,使用locator('#elementId')
可以快速定位到该元素,这是一种非常高效且准确的定位方法,因为id在页面中通常是唯一的。例如,在测试一个具有特定id的按钮时,代码如下:
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://example.com/specialButton');
const button = await page.locator('#specialButtonId');
// 对按钮进行操作,如点击按钮
await button.click();
await browser.close();
})();
当页面结构较为复杂,需要使用更灵活的定位方式时,css选择器就派上用场了。例如,如果要定位一个属于特定类名且在某个父元素下的子元素,可以使用locator('parentElement.class > childElement.class')
这样的css选择器语法进行定位。例如,在测试一个导航菜单时,如果要定位某个下拉菜单中的特定菜单项,代码如下:
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://example.com/navigationMenu');
const menuItem = await page.locator('.nav-menu >.dropdown-menu > li:nth-child(2)');
// 对菜单项进行操作,如点击菜单项
await menuItem.click();
await browser.close();
})();
这里,page.locator('.nav-menu >.dropdown-menu > li:nth-child(2)')
通过css选择器准确地定位到了导航菜单中的第二个下拉菜单项,这种定位方式能够适应各种复杂的页面结构,满足不同的测试需求。
xpath定位方式则适用于那些难以用其他简单方式定位的复杂元素,特别是当页面结构不清晰或者元素没有明显的id、类名等属性时。例如,在测试一个XML格式的页面或者具有复杂嵌套结构的HTML页面时,如果要定位一个深层嵌套的元素,代码如下:
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://example.com/xmlPage');
const element = await page.locator('//div[@class="parent"]/div[@class="child"]/span[@id="targetSpan"]');
// 对定位到的元素进行操作,如获取元素文本
const text = await element.textContent();
console.log(text);
await browser.close();
})();
通过page.locator('//div[@class="parent"]/div[@class="child"]/span[@id="targetSpan"]')
这样的xpath表达式,能够精准地定位到目标元素,获取其文本内容或进行其他操作,为处理复杂页面结构提供了有力的支持。
其他定位辅助方法
除了上述主要的定位方法外,Playwright还提供了一些辅助定位方法,如nth()
、first
、last
、filter()
以及链式选择器等,这些方法可以在特定需求下进一步筛选、定位元素。例如,当一个页面上有多个相同类型的元素,而我们只需要定位其中的第二个元素时,可以使用nth(1)
方法(索引从0开始),代码如下:
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://example.com/multipleElements');
const secondElement = await page.locator('li').nth(1);
// 对第二个元素进行操作,如检查其文本内容
const text = await secondElement.textContent();
console.log(text);
await browser.close();
})();
first
和last
方法则分别用于定位一组元素中的第一个和最后一个元素,非常方便快捷。例如,在测试一个列表页面时,如果要验证最后一个列表项的内容是否符合预期,可以使用last
方法,代码如下:
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://example.com/list');
const lastListItem = await page.locator('li').last();
const text = await lastListItem.textContent();
console.log(text);
await browser.close();
})();
filter()
方法可以根据特定的条件对一组元素进行筛选定位。例如,如果一个页面上有多个按钮,我们只需要定位那些具有特定属性值的按钮,可以使用filter()
方法,代码如下:
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://example.com/buttons');
const specificButtons = await page.locator('button').filter({ hasText: '特定文本' });
// 对筛选出的按钮进行操作,如点击按钮
await specificButtons.click();
await browser.close();
})();
链式选择器则允许我们将多个定位操作连接起来,逐步缩小定位范围,提高定位的准确性。例如,在测试一个具有复杂结构的表格时,如果要定位某个单元格,可以先定位到表格,再定位到行,最后定位到列,代码如下:
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://example.com/table');
const cell = await page.locator('table').locator('tr:nth-child(2)').locator('td:nth-child(3)');
// 对定位到的单元格进行操作,如获取单元格文本
const text = await cell.textContent();
console.log(text);
await browser.close();
})();
通过这些辅助定位方法的灵活运用,我们可以在各种复杂的页面场景中准确地定位到所需的元素,进行有效的测试操作和验证。