Playwright+PO模式下获取元素总数失败:Target closed报错求解
修复Playwright中"Target closed"错误(库存页元素计数问题)
在基于Page Object模式的Playwright自动化测试中,尝试获取https://www.saucedemo.com/inventory.html页面中商品元素总数时,出现以下错误:
Error: locator.waitFor: Target closed =========================== logs =========================== waiting for locator('xpath=//div[@class="inventory_item"]') to be visible ============================================================ at ../pages/InventoryPage.ts:19 17 | 18 | async selectRandomItem(){ > 19 | await this.colTable.waitFor({state: "visible"}); //issue is here | ^ 20 | const itemCount = await this.colTable.count(); 21 | return await itemCount; 22 | }
核心修复步骤
1. 确保登录后页面完全加载
登录操作完成后,必须等待页面成功跳转到库存页,避免在页面未就绪时执行元素操作。有两种实现方式:
方式一:在LoginPage的login方法中添加页面跳转等待
修改LoginPage.ts中的login方法:
async login(username: string, password: string) { // 原有输入用户名、密码、点击登录按钮的逻辑 await this.page.waitForURL('**/inventory.html'); // 等待跳转到库存页 }
方式二:在测试步骤中等待页面标识元素
在测试用例的登录步骤后,等待库存页的标题元素出现:
await test.step("2.输入正确的用户名和密码", async () => { await loginPage.login("standard_user", "secret_sauce"); await inventoryPage.lblLoggedIn.waitFor(); // 等待"Products"标题出现,确认页面加载完成 });
2. 正确await异步函数
测试用例中调用selectRandomItem()时,必须使用await获取返回值,否则会直接打印Promise对象,同时导致时序混乱:
await test.step("3.获取元素总数", async () => { const itemCount = await inventoryPage.selectRandomItem(); console.log(itemCount); });
3. 优化元素等待与计数逻辑
Playwright的count()方法默认会等待元素出现,无需额外调用waitFor,可以简化代码:
async selectRandomItem(){ const itemCount = await this.colTable.count(); return itemCount; }
如果需要显式等待,建议使用state: "attached"(等待元素加入DOM)替代visible,避免因元素暂时不可见导致的错误:
async selectRandomItem(){ await this.colTable.waitFor({state: "attached"}); const itemCount = await this.colTable.count(); return itemCount; }
4. 优化元素定位器
推荐使用Playwright更稳定的定位方式替代XPath,比如基于class的定位:
this.colTable = page.locator('.inventory_item'); // 等效于原XPath,更简洁
修改后的完整代码示例
inventory.spec.ts
import { test, expect } from '@playwright/test'; import { LoginPage } from '../pages/LoginPage'; import { InventoryPage } from '../pages/InventoryPage'; test.describe("库存页测试", () => { let loginPage: LoginPage; let inventoryPage: InventoryPage; test.beforeEach(({ page }) => { loginPage = new LoginPage(page); inventoryPage = new InventoryPage(page); }); test("TC-003: 验证随机商品可添加到购物车", async ({ page }) => { await test.step("1.访问登录页", async () => { await loginPage.visit(); }); await test.step("2.输入正确的用户名和密码", async () => { await loginPage.login("standard_user", "secret_sauce"); await inventoryPage.lblLoggedIn.waitFor(); // 等待页面加载完成 }); await test.step("3.获取商品总数", async () => { const itemCount = await inventoryPage.selectRandomItem(); console.log(itemCount); }); }); });
InventoryPage.ts
import { Locator, Page } from "@playwright/test"; export class InventoryPage { readonly page: Page; readonly lblLoggedIn: Locator; readonly colTable: Locator; constructor(page: Page){ this.page = page; this.lblLoggedIn = page.getByText('Products'); this.colTable = page.locator('.inventory_item'); // 简化定位器 } async selectRandomItem(){ const itemCount = await this.colTable.count(); // 无需额外waitFor return itemCount; } }
内容的提问来源于stack exchange,提问作者AVI
相关产品推荐
相关产品推荐

