如何在playwright的测试用例里参数化被测接口的域名?
· 1,974 词 · 10 分钟 读完 playwright进阶 翻译
最近有人问我如何在 Playwright 测试中处理 apiURL 和 baseURL。Playwright 的一大优势是可以直接进行 API 调用来创建数据,或者拦截网络请求以获取用于断言的数据。并非所有人都会遇到这个问题,但如果你的 API 和 UI 使用不同的 URL,希望这篇文章能对你有所帮助。
我发现有 4 种方法可以解决这个问题:
- 在测试中用例里硬编码 API URL
- 使用 process.env 环境变量
- 使用类来创建公共值
- 使用 Fixtures(我会介绍两种不同的方法)
在示例中,我将使用Practice Software Testing,这是一个现代化的结账体验演示网站,由Roy De Kleijn创建。以下是一些了解该网站的资源:
在测试中硬编码 API URL
第一种方法是直接在代码中使用 API URL,这并不推荐。因为如果你想测试本地环境、临时环境或沙盒环境,就需要在多处更新代码,这并不理想。
使用 process.env 环境变量
第二种方法是在代码中使用环境变量。这也不是最佳方案,但可以让你的项目快速启动。当你有相当数量的测试后,可能需要进一步抽象。
使用类来创建公共值
第三种方法是使用静态类。在类中,你可以定义一个公共静态变量,在导入类时可以在任何 Playwright 文件中使用。这种方法非常灵活且可扩展,因为你还可以添加多个静态数据信息或环境变量,同时还能享受智能提示的便利!
// lib/helpers/staticVariables.ts
export class StaticVariables {
public static staticApiURL = process.env.API_URL;
}
以下 3 个例子可以在名为login.ts
的数据工厂文件中看到。这个数据工厂函数getLoginToken()
的作用是在 UI 测试中使用任意邮箱/密码组合进行 API 调用,获取认证 token,然后将其保存到会话存储中,以便在 Angular UI 应用程序中进行身份验证。
// lib/datafactory/login.ts
import { expect, request } from "@playwright/test";
import { StaticVariables } from "../helpers/staticVariables";
let apiURL;
// 硬编码URL
apiURL = "https://api.practicesoftwaretesting.com";
// 直接使用环境变量
apiURL = process.env.API_URL;
// 使用专用类访问变量
apiURL = StaticVariables.staticApiURL;
export async function getLoginToken(email: string, password: string) {
const createRequestContext = await request.newContext();
const response = await createRequestContext.post(apiURL + "/users/login", {
data: {
email: email,
password: password,
},
});
expect(response.status()).toBe(200);
const body = await response.json();
return body.access_token;
}
在这 3 种方法中,我最喜欢的是第 3 种。这种方法可以在任何 TypeScript 文件中使用,这让我觉得它是一个很好的前进方向。
使用 Fixtures 创建 apiURL
我将提供两种方法。
方法 1:使用 page.ts fixture
这是我最初解决问题的方法。这种方法创建了一个 fixture 来扩展test
,在 Test Options 中添加了apiURL
类型的字符串。默认情况下,如果没有提供 apiURL,将分配一个空字符串。我把下面的例子添加到了lib/pages.ts
文件中,这个文件是我用于基础页面对象的,这样我就不用在每个 UI 测试中导入所有文件。
// lib/pages.ts
import { test as base } from "@playwright/test";
export * from "./pages/loginPage";
export * from "./pages/homePage";
export * from "./pages/checkoutPage";
export type TestOptions = {
apiURL: string,
};
// 这允许你在playwright.config.ts中设置apiURL
export const test =
base.extend <
TestOptions >
{
apiURL: ["", { option: true }],
};
export default test;
要使用这个方法,你必须在 Playwright 测试用例中从 lib/pages 导入 test,并在playwright.config.ts
中设置apiURL
(注意我们这里使用的是apiURL
,下一节我们将使用apiBaseURL
。我这样做主要是为了看两个 fixtures 如何并行工作)。
// playwright.config.ts
import { defineConfig } from "@playwright/test";
import type { APIRequestOptions } from "./lib/fixtures/apiRequest";
import { TestOptions } from "./lib/pages";
require("dotenv").config();
export default (defineConfig < APIRequestOptions) &
(TestOptions >
{
testDir: "./tests",
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: [["html"], ["list"]],
use: {
baseURL: process.env.UI_URL,
apiURL: process.env.API_URL,
apiBaseURL: process.env.API_URL,
trace: "retain-on-failure",
},
});
在下面的测试文件中,注意我们从"../lib/pages"导入test
,这让我们可以在 beforeEach 块中使用在playwright.config.ts
中设置的 apiURL。
// tests/checkoutWithPageFixture.spec.ts
import { expect } from "@playwright/test";
import { test, CheckoutPage, HomePage } from "../lib/pages";
test.describe("使用Page Fixture的基本UI检查", () => {
const username = process.env.USERNAME || "";
const password = process.env.PASSWORD || "";
test.beforeEach(async ({ page, request, apiURL }) => {
// 使用fixture中的apiBaseURL通过API调用获取登录token
const response = await request.post(apiURL + "/users/login", {
data: {
email: username,
password: password,
},
});
expect(response.status()).toBe(200);
const body = await response.json();
const token = body.access_token;
// 使用登录token设置本地存储,使用户保持登录状态
await page.addInitScript((value) => {
window.localStorage.setItem("auth-token", value);
}, token);
});
test("添加到购物车并结账", async ({ page }) => {
const homePage = new HomePage(page);
const checkoutPage = new CheckoutPage(page);
await homePage.goto();
await homePage.product2.click();
await homePage.addToCart.click();
await homePage.navCart.click();
await checkoutPage.proceed1.click();
await checkoutPage.proceed2.click();
await checkoutPage.address.fill("123 test street");
await checkoutPage.city.fill("testville");
await checkoutPage.state.fill("test");
await checkoutPage.country.fill("united states");
await checkoutPage.postcode.fill("12345");
await checkoutPage.proceed3.click();
await checkoutPage.paymentMethod.selectOption("2: Cash on Delivery");
await checkoutPage.accountName.fill("testy");
await checkoutPage.accountNumber.fill("1234124");
await checkoutPage.finish.click();
await expect(checkoutPage.success.first()).toBeVisible();
});
});
方法 2:使用 apiRequests.ts fixture
这种方法的功劳完全归于Yury Semikhatsky,他在为添加api 接口 baseURL的功能请求提供反馈时提出了这个方法(如果感兴趣,可以去给这个请求点个 👍)。
这种方法与第一种 fixture 方法非常相似,但 Yury 更进一步,不仅创建了一个可以从playwright.config.ts
文件导入的apiBaseURL
TestOption(与上面的文件相同),还用apiRequest
扩展了test
,当调用时会默认使用apiBaseURL
替换baseURL
。这非常巧妙,但如果你与项目中的初级开发人员一起工作,可能不太直观。
// lib/fixtures/apiReqeusts.ts
import { test as base, APIRequestContext, request } from "@playwright/test";
export type APIRequestOptions = {
apiBaseURL: string,
};
type APIRequestFixture = {
apiRequest: APIRequestContext,
};
// 这个fixture会在使用时用playwright.config.ts中的apiBaseURL覆盖baseURL
export const test =
(base.extend < APIRequestOptions) &
(APIRequestFixture >
{
apiBaseURL: ["", { option: true }],
apiRequest: async ({ apiBaseURL }, use) => {
const apiRequestContext = await request.newContext({
baseURL: apiBaseURL,
});
await use(apiRequestContext);
await apiRequestContext.dispose();
},
});
在 spec 中的实现需要你从../lib/fixtures/apiRequest
文件导入test
,当使用apiRequest
进行 API 调用时,你甚至不需要传入 baseURL,它会自动用playwright.config.ts
中的 apiBaseURL 替换 baseURL。
// tests/checkoutWithApiFixture.spec.ts
import { expect } from "@playwright/test";
import { test } from "../lib/fixtures/apiRequest";
import { CheckoutPage, HomePage } from "../lib/pages";
test.describe("使用API Fixture的基本UI检查", () => {
const username = process.env.USERNAME || "";
const password = process.env.PASSWORD || "";
test.beforeEach(async ({ page, apiRequest }) => {
// 使用fixture中的apiBaseURL通过API调用获取登录token,但全部都在fixture中,所以你甚至不需要在测试中添加apiURL
const response = await apiRequest.post("/users/login", {
data: {
email: username,
password: password,
},
});
expect(response.status()).toBe(200);
const body = await response.json();
const token = body.access_token;
// 使用登录token设置本地存储,使用户保持登录状态
await page.addInitScript((value) => {
window.localStorage.setItem("auth-token", value);
}, token);
});
test("添加到购物车并结账", async ({ page }) => {
const homePage = new HomePage(page);
const checkoutPage = new CheckoutPage(page);
await homePage.goto();
await homePage.product2.click();
await homePage.addToCart.click();
await homePage.navCart.click();
await checkoutPage.proceed1.click();
await checkoutPage.proceed2.click();
await checkoutPage.address.fill("123 test street");
await checkoutPage.city.fill("testville");
await checkoutPage.state.fill("test");
await checkoutPage.country.fill("united states");
await checkoutPage.postcode.fill("12345");
await checkoutPage.proceed3.click();
await checkoutPage.paymentMethod.selectOption("2: Cash on Delivery");
await checkoutPage.accountName.fill("testy");
await checkoutPage.accountNumber.fill("1234124");
await checkoutPage.finish.click();
await expect(checkoutPage.success.first()).toBeVisible();
});
});
Yury 的代码库/示例可以在这里找到:
GitHub - yury-s/bug-23738: bug-23738 bug-23738
本文中的所有代码示例都可以在这个仓库中找到:
GitHub - playwrightsolutions/playwright-practicesoftwaretesting.com
总结一下,有很多方法可以解决这个问题,我相信还有更多我没有涉及到的方法,但希望这些方法中的一种能让你的 Playwright 测试更加简洁和易于维护。如果你希望 Playwright 原生支持这个功能,请为这个功能请求投票!
[Feature]: 在 playwright.config.ts 中添加 apiEndpoint(类似于 baseUrl) · Issue #23738
感谢阅读!如果你觉得这篇文章有帮助,可以在LinkedIn上联系我,或者考虑给我买杯咖啡。如果你想接收更多内容,可以在下方订阅,别忘了点个 ❤️ 表示支持。
来源
URL 来源: https://playwrightsolutions.com/how-do-you-define-an-apiurl-along-with-the-baseurl-in-playwright/
发布时间: 2023-06-19T12:30:01.000Z