如何将Selenium中基于this.driver.findElement()的函数抽象到独立文件?
Selenium + Cucumber.js 复用元素检查逻辑的问题解决
问题描述
在Node.js环境中基于Selenium Webdriver和Cucumber.js搭建测试框架时,原本在homePageSteps.js中的横幅检查代码可正常运行:
Then('there should be a banner', async function() { var banner = await this.driver.findElement(By.className('banner')).getRect(); assert.ok(banner.height!==null); });
为了让该检查逻辑能在任意步骤文件中复用,将代码抽离到banners.js:
function checkBanner(driver) { var banner = this.driver.findElement(By.className('banner')).getRect(); return banner.height; }; module.exports = { checkBanner };
随后在步骤文件中引入并调用:
const banners = require("../../support/components/banners"); // Other things... Then('there should be a banner', async function() { assert.ok(banners.checkBanner()!==null); });
运行时出现错误:Cannot read properties of undefined (reading 'findElement'),本质是抽离的函数无法获取当前有效的Selenium driver实例。
错误原因
- 抽离的
checkBanner函数错误使用this.driver——此处的this并非Cucumber步骤上下文对象,且定义了driver参数却未实际使用。 - 调用
checkBanner()时未传入this.driver实例,导致函数内部无法获取可操作的driver对象。 - Selenium API均为异步操作,抽离的函数未添加
async/await处理异步流程,会直接返回Promise而非实际结果。
解决方案
方案一:修正参数传递与异步处理
修改banners.js,正确使用传入的driver参数并处理异步:
const { By } = require('selenium-webdriver'); async function checkBanner(driver) { const banner = await driver.findElement(By.className('banner')).getRect(); return banner.height; }; module.exports = { checkBanner };
在步骤文件中调用时传入this.driver:
const banners = require("../../support/components/banners"); const assert = require('assert'); Then('there should be a banner', async function() { const bannerHeight = await banners.checkBanner(this.driver); assert.ok(bannerHeight !== null); });
方案二:采用页面对象模式(推荐)
对于复杂测试场景,推荐使用页面对象模式封装元素操作,这是UI自动化测试的最佳实践,便于维护和复用:
创建BannerPage.js:
const { By } = require('selenium-webdriver'); class BannerPage { constructor(driver) { this.driver = driver; this.bannerLocator = By.className('banner'); } // 获取横幅高度 async getBannerHeight() { const banner = await this.driver.findElement(this.bannerLocator).getRect(); return banner.height; } // 检查横幅是否显示 async isBannerDisplayed() { try { const banner = await this.driver.findElement(this.bannerLocator); return await banner.isDisplayed(); } catch (err) { // 元素找不到时返回false return false; } } } module.exports = BannerPage;
在步骤文件中实例化并使用:
const BannerPage = require("../../support/pages/BannerPage"); const assert = require('assert'); Then('there should be a banner', async function() { const bannerPage = new BannerPage(this.driver); const isBannerVisible = await bannerPage.isBannerDisplayed(); assert.ok(isBannerVisible); });
方案说明
- 方案一快速解决当前问题,通过正确传递driver实例和异步处理,确保函数能正常操作页面元素。
- 方案二的页面对象模式将元素定位与测试逻辑分离,后续若横幅的定位规则变化,只需修改
BannerPage中的定位器即可,大幅提升代码可维护性,适合长期迭代的测试框架。
内容的提问来源于stack exchange,提问作者MeltingDog
相关产品推荐
相关产品推荐

