You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Playwright验证断言不匹配时测试未失败问题排查

问题

我写了一段Playwright测试代码,用来断言结账页面的验证错误消息是否和元素文本匹配,但运行时哪怕有一条预期验证消息不对,测试还是显示通过。我希望验证不匹配时测试能直接失败,请问哪里出错了?

代码如下:

test('Assert validations are present on the checkout page', async ({ page }) => {
  await page.goto('private');
 
  await page.getByRole('button', { name: 'Headwear' }).click()
  await page.locator("//a[contains(text(),'Adult')]").click()

  const productGrid = await page.waitForSelector('div[id="productGrid"]');
  const products = await productGrid.$$('div[class="product-card relative w-full"]');
  for (const product of products) {
    const outOfStockLabel = await product.$('div:has-text("Out of stock")');
    if (!outOfStockLabel) {
      await product.click();
      break;
    }
  }

  await page.click('#btnAddToCart');
  await page.locator("//button[@title='Toggle Basket']//*[name()='svg']").click();
  await page.click("#btnCartPopUpCheckout");
  await page.click("#pay-button-container");

    // Step 2: Assert the validation messages for each field
    await page.waitForSelector("#error-for-email");
    await page.waitForSelector("#error-for-TextField1");
    await page.waitForSelector("#error-for-TextField2");
    await page.waitForSelector("#error-for-shipping-address1");
    await page.waitForSelector("#error-for-TextField4");
    await page.waitForSelector("#error-for-TextField5");
    await page.waitForSelector("#error-for-number");
    await page.waitForSelector("#error-for-expiry");
    await page.waitForSelector("#error-for-verification_value");
    await page.waitForSelector("#error-for-name");
  
    const emailValidation = await page.$eval('#error-for-email', (element) => element.textContent);
    const firstNameValidation = await page.$eval('#error-for-TextField1', (element) => element.textContent);
    const lastNameValidation = await page.$eval('#error-for-TextField2', (element) => element.textContent);
    const addressValidation = await page.$eval('#error-for-shipping-address1', (element) => element.textContent);
    const cityValidation = await page.$eval('#error-for-TextField4', (element) => element.textContent);
    const postcodeValidation = await page.$eval('#error-for-TextField5', (element) => element.textContent);
    const cardNumberValidation = await page.$eval('#error-for-number', (element) => element.textContent);
    const expirationDateValidation = await page.$eval('#error-for-expiry', (element) => element.textContent);
    const securityCodeValidation = await page.$eval('#error-for-verification_value', (element) => element.textContent);
    const nameOnCardValidation = await page.$eval('#error-for-name', (element) => element.textContent);
  
    const expectedValidations = {
      email: 'Enter an emdail',
      firstName: 'Enter a first name',
      lastName: 'Enter a last name',
      address: 'Enter an address',
      city: 'Enter a city',
      postcode: 'Enter a ZIP / postal code',
      cardNumber: 'Enter a card number',
      expirationDate: 'Enter a valid expiration date',
      securityCode: 'Enter the CVV or security code on your card',
      nameOnCard: "Enter your name exactly as it’s written on your card",
    };
  
    const validationResults = {
      email: emailValidation,
      firstName: firstNameValidation,
      lastName: lastNameValidation,
      address: addressValidation,
      city: cityValidation,
      postcode: postcodeValidation,
      cardNumber: cardNumberValidation,
      expirationDate: expirationDateValidation,
      securityCode: securityCodeValidation,
      nameOnCard: nameOnCardValidation,
    };
  
    let allValidationsPassed = true;
  
    for (const field in expectedValidations) {
      if (validationResults[field] !== expectedValidations[field]) {
        console.log(`Validation failed for ${field}: Expected "${expectedValidations[field]}", but got "${validationResults[field]}"`);
        allValidationsPassed = false;
      }
    }
  
    if (allValidationsPassed) {
      console.log('All field validations passed!');
    } else {
      console.log('Some field validations failed.');
    }

})
解决方案

核心问题

你的代码只是通过console.log输出了验证失败的信息,但没有主动抛出断言失败的异常。Playwright的测试只有在遇到未捕获的错误或断言失败时才会标记为失败,单纯的日志输出不会触发测试失败。

修复方式

有两种可靠的修复方案,推荐使用第一种(Playwright官方最佳实践):

方案1:使用Playwright内置的断言API(推荐)

Playwright的expect断言自带重试机制,能避免因元素文本未完全加载导致的误判,且失败时会直接终止测试并给出清晰的错误信息。

修改后的验证部分代码:

const expectedValidations = {
  email: 'Enter an email', // 修正原代码拼写错误:emdail -> email
  firstName: 'Enter a first name',
  lastName: 'Enter a last name',
  address: 'Enter an address',
  city: 'Enter a city',
  postcode: 'Enter a ZIP / postal code',
  cardNumber: 'Enter a card number',
  expirationDate: 'Enter a valid expiration date',
  securityCode: 'Enter the CVV or security code on your card',
  nameOnCard: "Enter your name exactly as it’s written on your card",
};

// 替换原来的多个waitForSelector,提升执行效率
await Promise.all([
  page.locator('#error-for-email').waitFor(),
  page.locator('#error-for-TextField1').waitFor(),
  page.locator('#error-for-TextField2').waitFor(),
  page.locator('#error-for-shipping-address1').waitFor(),
  page.locator('#error-for-TextField4').waitFor(),
  page.locator('#error-for-TextField5').waitFor(),
  page.locator('#error-for-number').waitFor(),
  page.locator('#error-for-expiry').waitFor(),
  page.locator('#error-for-verification_value').waitFor(),
  page.locator('#error-for-name').waitFor(),
]);

// 直接对每个错误元素断言文本
await expect(page.locator('#error-for-email')).toHaveText(expectedValidations.email);
await expect(page.locator('#error-for-TextField1')).toHaveText(expectedValidations.firstName);
await expect(page.locator('#error-for-TextField2')).toHaveText(expectedValidations.lastName);
await expect(page.locator('#error-for-shipping-address1')).toHaveText(expectedValidations.address);
await expect(page.locator('#error-for-TextField4')).toHaveText(expectedValidations.city);
await expect(page.locator('#error-for-TextField5')).toHaveText(expectedValidations.postcode);
await expect(page.locator('#error-for-number')).toHaveText(expectedValidations.cardNumber);
await expect(page.locator('#error-for-expiry')).toHaveText(expectedValidations.expirationDate);
await expect(page.locator('#error-for-verification_value')).toHaveText(expectedValidations.securityCode);
await expect(page.locator('#error-for-name')).toHaveText(expectedValidations.nameOnCard);

方案2:保留循环逻辑,主动抛出错误

如果你想保留原有的循环校验逻辑,需要在最后判断allValidationsPassed,若为false则主动抛出错误:

let allValidationsPassed = true;
const errorMessages = [];

for (const field in expectedValidations) {
  if (validationResults[field] !== expectedValidations[field]) {
    const errorMsg = `Validation failed for ${field}: Expected "${expectedValidations[field]}", but got "${validationResults[field]}"`;
    errorMessages.push(errorMsg);
    console.log(errorMsg);
    allValidationsPassed = false;
  }
}

if (!allValidationsPassed) {
  throw new Error(`部分验证失败:\n${errorMessages.join('\n')}`);
}

额外提示

你原代码里预期的邮箱验证消息存在拼写错误:Enter an emdail 应该是 Enter an email,这会导致验证失败,记得修正。


内容的提问来源于stack exchange,提问作者Amar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 09:47:11