使用 Playwright 进行行为驱动开发 (BDD)

2025-06-09

使用 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


Enter fullscreen mode Exit fullscreen mode

Playwright 没有内置对 BDD 的支持,因此我们将借助另一个工具Cucumber



   npm i -D @cucumber/cucumber@7.3.1 @cucumber/pretty-formatter


Enter fullscreen mode Exit fullscreen mode

此后,devDependenciespackage.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"
  }


Enter fullscreen mode Exit fullscreen mode

配置

我们将使用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");


Enter fullscreen mode Exit fullscreen mode

将默认超时设置为合理的时间量



// cucumber.conf.js file

// in milliseconds
setDefaultTimeout(60000)


Enter fullscreen mode Exit fullscreen mode

将以下代码片段添加到您的文件中



// 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();
});


Enter fullscreen mode Exit fullscreen mode

在上面的代码片段中,我们启动了一个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();
});



Enter fullscreen mode Exit fullscreen mode

启动浏览器后,我们需要创建一个新的浏览器上下文。Playwright 允许incognito使用方法创建浏览器上下文browser.newContext([options])。每个浏览器上下文都有其对应的页面,提供与浏览器中单个选项卡交互的方法。我们可以创建一个带有context.newPage()方法的页面。与启动浏览器类似,在创建浏览器上下文时,我们也可以提供许多选项,browser context例如屏幕截图、录制视频、地理位置等等,您可以在此处查看所有选项。完成操作后,关闭pagecontext

,配置部分已经完成了。整个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();
});



Enter fullscreen mode Exit fullscreen mode

编写测试

现在有一些有趣的东西,我们开始编写测试!

我们的文件结构如下



📦tests
┗ 📂acceptance
┃ ┣ 📂features
┃ ┃ ┗ 📜todo.feature
┃ ┗ 📂stepDefinitions
┃ ┃ ┗ 📜todoContext.js



Enter fullscreen mode Exit fullscreen mode

按照上面的树创建文件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]


Enter fullscreen mode Exit fullscreen mode

现在假设您已经对功能文件以及如何编写它们有一些了解,我们将继续进行。

我要测试的应用程序是一个待办事项应用程序,其 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


Enter fullscreen mode Exit fullscreen mode

现在,我们使用 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"



Enter fullscreen mode Exit fullscreen mode

我们将使用test:e2e脚本来运行测试。现在打开终端并运行脚本



npm run test:e2e tests/acceptance/features/todo.feature


Enter fullscreen mode Exit fullscreen mode

这将运行你的功能文件。由于步骤尚未实现,你将在屏幕上看到类似以下内容。



? 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';
        });



Enter fullscreen mode Exit fullscreen mode

您现在可以将生成的代码片段添加到您的上下文文件中并开始实现它们。

导入以下内容



// todoContext.js file

const {Given, When, Then} = require('@cucumber/cucumber')
// import expect for assertion
const { expect } = require("@playwright/test");


Enter fullscreen mode Exit fullscreen mode

根据需要定义启动 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 ';


Enter fullscreen mode Exit fullscreen mode

现在我们可以实现单独的测试步骤,如下所示



// 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)
})



Enter fullscreen mode Exit fullscreen mode

您可以在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)
})



Enter fullscreen mode Exit fullscreen mode

运行测试

首先,你需要运行你的应用程序,就我而言



npm run start


Enter fullscreen mode Exit fullscreen mode

现在运行测试并在浏览器中观看



npm run test:e2e tests/acceptance/features/todo.feature


Enter fullscreen mode Exit fullscreen mode

您应该会得到与此类似的日志。



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)


Enter fullscreen mode Exit fullscreen mode

希望你的测试也能像我一样通过,并且你了解了一个新的库。
你可以扩展功能文件以添加更多场景,或者添加多个功能文件,并根据你的需求实现页面对象模型,它们应该都能正常工作。

您可以在此处找到此实现的源代码

鏂囩珷鏉ユ簮锛�https://dev.to/jankaritech/behavior-driven-development-bdd-using-playwright-n1o
PREV
Stop Using JavaScript Classes!
NEXT
在 Ubuntu 上安装 JetBrains ToolBox 系统要求 安装 Jetbrains Toolbox 配置 JetBrains Toolbox 和安装应用程序 如何卸载 Jetbrains Toolbox