使用 Playwright 进行行为驱动开发 (BDD)
Playwright 是一个用于浏览器自动化的开源 NodeJS 框架。它由微软开发,其开发团队成员曾参与为谷歌开发Puppeteer。
Playwright 的主要功能之一是它可以通过单一 API 自动化 Chromium、Webkit 和 Firefox 浏览器。除了跨浏览器之外,它还支持跨平台和跨语言,支持 Windows、Linux、Mac 等主流操作系统以及 TypeScript、JavaScript、Python、.NET、Java 等语言。Playwright 还附带 codgen 等工具,可让您通过记录操作来自动生成代码。您可以在 Playwright 的官方网站上了解更多关于 Playwright 的信息。
在本篇博客中,我们将在 Playwright 中实现 BDD。我有一个小型的待办事项 Web 应用,我打算在其中设置 Playwright。如果您想继续操作,可以从这里fork 并克隆该项目。如果您有 Web 应用,也可以在那里设置 Playwright。让我们开始吧!
注意:整个设置是在 Ubuntu 20.04.3 LTS 中完成的,因此某些设置步骤可能会因您的平台而异
先决条件
- Node.js 12 或更高版本。如果您的系统尚未安装 Node,可以参考此博客。
注意:仅官方支持 Ubuntu 18.04 和 Ubuntu 20.04。
安装
从项目的根目录运行
npm i -D @playwright/test
npm i -D playwright
npx playwright install
Playwright 没有内置对 BDD 的支持,因此我们将借助另一个工具Cucumber
npm i -D @cucumber/cucumber@7.3.1 @cucumber/pretty-formatter
此后,devDependencies
你package.json
应该看到类似这样的内容
// package.json file
"devDependencies": {
"@cucumber/cucumber": "^7.3.1",
"@cucumber/pretty-formatter": "^1.0.0-alpha.1",
"@playwright/test": "^1.18.0",
"playwright": "^1.18.1"
}
配置
我们将使用Cucumber
它来运行测试,因此我们需要一个配置文件。在项目的根目录创建一个文件cucumber.conf.js
首先,我们需要以下内容:
// cucumber.conf.js file
const { Before, BeforeAll, AfterAll, After, setDefaultTimeout } = require("@cucumber/cucumber");
// you can choose other browsers like webkit or firefox according to your requirement
const { chromium } = require("playwright");
将默认超时设置为合理的时间量
// cucumber.conf.js file
// in milliseconds
setDefaultTimeout(60000)
将以下代码片段添加到您的文件中
// cucumber.conf.js file
// launch the browser
BeforeAll(async function () {
global.browser = await chromium.launch({
headless: false,
slowMo: 1000,
});
});
// close the browser
AfterAll(async function () {
await global.browser.close();
});
在上面的代码片段中,我们启动了一个chrome
浏览器,我们的测试将在其中自动化。您可以根据需要启动不同的浏览器,只需确保导入正确的浏览器即可。我们以头戴式模式运行浏览器,这可以通过设置来完成headless:false
,这意味着当测试运行时,我们可以在浏览器中看到它的自动化运行。true
如果您不想看到测试运行,可以将其设置为,但那有什么乐趣呢?另一个选项是,slowMo
这会将 Playwright 操作减慢指定的毫秒数,并有助于观察测试运行。启动浏览器时可以设置各种选项,您可以在此处浏览所有选项。完成操作后,我们将关闭浏览器。此配置适用于所有测试运行之前/之后。现在我们需要配置每个测试场景运行时发生的情况。为此,请看下面的代码片段:
// cucumber.conf.js file
// Create a new browser context and page per scenario
Before(async function () {
global.context = await global.browser.newContext();
global.page = await global.context.newPage();
});
// Cleanup after each scenario
After(async function () {
await global.page.close();
await global.context.close();
});
启动浏览器后,我们需要创建一个新的浏览器上下文。Playwright 允许incognito
使用方法创建浏览器上下文browser.newContext([options])
。每个浏览器上下文都有其对应的页面,提供与浏览器中单个选项卡交互的方法。我们可以创建一个带有context.newPage()
方法的页面。与启动浏览器类似,在创建浏览器上下文时,我们也可以提供许多选项,browser context
例如屏幕截图、录制视频、地理位置等等,您可以在此处查看所有选项。完成操作后,关闭page
和context
。
瞧,配置部分已经完成了。整个cucumber.conf.js
文件如下所示:
// cucumber.conf.js file
const { Before, BeforeAll, AfterAll, After, setDefaultTimeout } = require("@cucumber/cucumber");
const { chromium } = require("playwright");
setDefaultTimeout(60000)
// launch the browser
BeforeAll(async function () {
global.browser = await chromium.launch({
headless: false,
slowMo: 1000,
});
});
// close the browser
AfterAll(async function () {
await global.browser.close();
});
// Create a new browser context and page per scenario
Before(async function () {
global.context = await global.browser.newContext();
global.page = await global.context.newPage();
});
// Cleanup after each scenario
After(async function () {
await global.page.close();
await global.context.close();
});
编写测试
现在有一些有趣的东西,我们开始编写测试!
我们的文件结构如下
📦tests
┗ 📂acceptance
┃ ┣ 📂features
┃ ┃ ┗ 📜todo.feature
┃ ┗ 📂stepDefinitions
┃ ┃ ┗ 📜todoContext.js
按照上面的树创建文件tests/acceptance/features/todo.feature
。由于我们使用 BDD,我们将首先编写一个功能文件,并使用Gherkin
语言来实现。如果您不知道如何编写功能文件或什么Gherkin
是功能文件,可以参考以下博客,因为它们超出了本博客的讨论范围,因此不会详细解释。
以下是功能文件的基本语法
Feature: a short description of a software feature
As a user
I want to do this
So I can achieve that
Scenario: name of the scenario
Given [Preconditions or initial context of the system ]
When [Event or action]
Then [Expected output]
现在假设您已经对功能文件以及如何编写它们有一些了解,我们将继续进行。
我要测试的应用程序是一个待办事项应用程序,其 UI 如下所示。
我想测试我添加的项目是否显示在用户界面上。功能文件如下所示。
// todo.feature
Feature: todo
As a user
I want to add an item to the todo list
So that I can organize tasks
Scenario: Add item to the todo list
Given a user has navigated to the homepage
# the text inside the quote works as a variable that can be passed to a function
When the user adds "test" to the todo list using the webUI
Then card "test" should be displayed on the webUI
现在,我们使用 Playwright 来实现场景的每个步骤!创建一个上下文文件tests/acceptance/stepDefinitions/todoContext.js
。我们可以为场景中的每个步骤获取一个样板,并在其中提供我们的实现。为此,请在package.json
文件中添加以下脚本。
"test:e2e": "cucumber-js --require cucumber.conf.js --require tests/acceptance/stepDefinitions/**/*.js --format @cucumber/pretty-formatter"
我们将使用test:e2e
脚本来运行测试。现在打开终端并运行脚本
npm run test:e2e tests/acceptance/features/todo.feature
这将运行你的功能文件。由于步骤尚未实现,你将在屏幕上看到类似以下内容。
? Given a user has navigated to the homepage
Undefined. Implement with the following snippet:
Given('a user has navigated to the homepage', function () {
// Write code here that turns the phrase above into concrete actions
return 'pending';
});
? When the user adds "test" to the todo list using the webUI
Undefined. Implement with the following snippet:
When('the user adds {string} to the todo list using the webUI', function (string) {
// Write code here that turns the phrase above into concrete actions
return 'pending';
});
? Then card "test" should be displayed on the webUI
Undefined. Implement with the following snippet:
Then('card {string} should be displayed on the webUI', function (string) {
// Write code here that turns the phrase above into concrete actions
return 'pending';
});
您现在可以将生成的代码片段添加到您的上下文文件中并开始实现它们。
导入以下内容
// todoContext.js file
const {Given, When, Then} = require('@cucumber/cucumber')
// import expect for assertion
const { expect } = require("@playwright/test");
根据需要定义启动 URL 和不同 UI 元素的选择器,这些是项目特定的。Playwright 支持 CSS 和 Xpath 选择器。您可以在此处找到有关它们的详细信息
// todoContext.js file
//launch url
const url = 'http://localhost:3000'
//define selectors
const homepageElement = '.borderTodo'
const todoInput = '.todo-input';
const todoButton = '.todo-button';
const todoItem = '.todo .todo-item ';
现在我们可以实现单独的测试步骤,如下所示
// todoContext.js file
Given('a user has navigated to the homepage', async function () {
// navigate to the app
await page.goto(url)
// locate the element in the webUI
const locator = await page.locator(homepageElement)
// assert that it's visible
await expect(locator).toBeVisible()
})
When('the user adds {string} to the todo list using the webUI',async function (item) {
// fill the item that was input from the feature file , to the inputText field in the UI
await page.fill(todoInput , item)
// click the button
await page.click(todoButton)
})
Then('card {string} should be displayed on the webUI',async function (item) {
// get text of the item that is visible in the UI
const text = await page.innerText(todoItem)
// assert that its name is similar to what we provided
await expect(text).toBe(item)
})
您可以在Playwright 的官方文档中找到可用于与 UI 元素(如单击、填充等)交互的不同方法,它很好地解释了函数如何与示例代码一起工作。
我们使用在钩子page
中创建的before
与各种 Web 元素进行交互。Playwright 会执行自动等待,并对元素进行一系列可操作性检查,并确保元素已准备好执行预期操作。这是它的一大优势。
这是整个上下文文件
// todoContext.js file
const {Given, When, Then} = require('@cucumber/cucumber')
// import expect for assertion
const { expect } = require("@playwright/test")
//launch url
const url = 'http://localhost:3000'
//define selectors
const homepageElement = '.borderTodo'
const todoInput = '.todo-input'
const todoButton = '.todo-button'
const todoItem = '.todo .todo-item '
Given('a user has navigated to the homepage', async function () {
// navigate to the app
await page.goto(url)
// locate the element in the webUI
const locator = page.locator(homepageElement)
// assert that it's visible
expect(locator).toBeVisible()
})
When('the user adds {string} to the todo list using the webUI',async function (item) {
// fill the item that was input from the feature file , to the inputText field in the UI
await page.fill(todoInput , item)
// click the button
await page.click(todoButton)
})
Then('card {string} should be displayed on the webUI',async function (item) {
// get text of the item that is visible in the UI
const text = await page.innerText(todoItem)
// assert that its name is similar to what we provided
expect(text).toBe(item)
})
运行测试
首先,你需要运行你的应用程序,就我而言
npm run start
现在运行测试并在浏览器中观看
npm run test:e2e tests/acceptance/features/todo.feature
您应该会得到与此类似的日志。
Feature: todo # tests/acceptance/features/todo.feature:1
As a user
I want to add an item to the todo list
So that I can organize tasks
Scenario: Add item to the todo list # tests/acceptance/features/todo.feature:6
Given a user has navigated to the homepage
When the user adds "test" to the todo list using the webUI
Then card "test" should be displayed on the webUI
1 scenario (1 passed)
3 steps (3 passed)
0m04.266s (executing steps: 0m04.010s)
希望你的测试也能像我一样通过,并且你了解了一个新的库。
你可以扩展功能文件以添加更多场景,或者添加多个功能文件,并根据你的需求实现页面对象模型,它们应该都能正常工作。
您可以在此处找到此实现的源代码
鏂囩珷鏉ユ簮锛�https://dev.to/jankaritech/behavior-driven-development-bdd-using-playwright-n1o