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

Cypress分离基础设施与测试时自定义task无法使用

问题场景

尝试将测试基础设施与被测项目分离,在被测项目外部的公共plugins目录中新增名为queryDb的自定义task,已完成代码编译,且在项目配置文件中正确配置了该plugins文件的路径,当前公共plugins文件内的其他插件可正常运行。

错误表现

执行测试时Cypress抛出如下错误:

"value": "CypressError: `cy.task('queryDb')` failed with the following error:\n\nThe task 'queryDb' was not handled in the plugins file. The following tasks are registered: log\n\nFix this in your plugins file here:\n./../testilize/cypress/plugins/index.ts\n    at ...

错误提示plugins文件中仅注册了log任务,未识别到queryDb任务。

相关配置

当前项目配置继承自被测项目外部的基础配置文件,配置内容如下:

{
  "extends": "./../testilize/cypress.json",
  "baseUrl": "https://www.blabla.com/",
  "env": {
    "client": "https://www.blabla.com/",
    "server": "https://www.blabla.com/"
  },
  "pluginsFile": "./../testilize/cypress/plugins/index.ts",
  "supportFile": "./../testilize/cypress/support/index.js",
  "fixturesFolder": "e2e-tests/fixtures",
  "integrationFolder": "e2e-tests/test-files"
}

公共plugins文件(./../testilize/cypress/plugins/index.ts)代码如下:

// cypress/plugins/index.ts
/// <reference types="cypress" />

/**
 * @type {Cypress.PluginConfig}
 */

const preprocess = require('./preprocess');
const deepmerge = require('deepmerge')
const path = require('path');
require('dotenv').config({ path: './../testilize/.env' , override: true })
import { my_connection } from '../support/db-handlers/connections';

function queryTestDb(query, config) {
    // start connection to db
    my_connection.connect();
    // exec query + disconnect to db as a Promise
    return new Promise((resolve, reject) => {
        my_connection.query(query, (error, results) => {
            if (error) reject(error);
            else {
                my_connection.end();
                // console.log(results)
                return resolve(results);
            }
        });
    });
}

module.exports = (on, config) => {
    require('cypress-log-to-output').install(on)

    on('task', {
        log (message) {
            console.log(message)
            return true
        }
    })

    const configJson = require(config.configFile)
    if (configJson.extends) {
        const baseConfigFilename = path.join(config.projectRoot, configJson.extends)
        const baseConfig = require(baseConfigFilename)
        console.log('merging %s with %s', baseConfigFilename, config.configFile)
        configJson.env.my_db_name = process.env.my_DB_NAME;
        configJson.env.my_db_host = process.env.my_DB_HOST;
        configJson.env.my_db_user = process.env.my_DB_USER;
        configJson.env.my_db_password = process.env.my_DB_PASSWORD;
        configJson.env.my_db_port = process.env.my_DB_PORT;

        return deepmerge(baseConfig, configJson);
    }

    on("file:preprocessor", preprocess);

    on('before:browser:launch', (browser , launchOptions) => {
        if (browser.name === 'chrome' && browser.isHeadless) {
            launchOptions.args.push('--disable-gpu', '--no-sandbox', '--disable-dev-shm-usage', '--window-size=1920,1080');
            return launchOptions
        }
    })

    // Usage: cy.task('queryDb', query)
    on('task', {
        'queryDb': query => {
            return queryTestDb(query, config);
        }
    });

    return configJson
}

测试用例代码如下:

/// <reference types="./../../../testilize/node_modules/cypress" />

let allProjectIDs: any = [];


describe('Tests', () => {

  it('send graphQL request for internal api', () => {
    cy.task(
        'queryDb',
        `SELECT project_id FROM table_name LIMIT 100;`
    ).then(res => {
      console.log(res);
      allProjectIDs.push(res);
      console.log(allProjectIDs);
    });
    
  });

});
技术栈
  • TypeScript 4.6
  • Node 14x
  • Cypress 9.6
故障原因

插件导出函数存在提前返回的逻辑分支,导致queryDb的注册代码从未执行:
函数运行时首先完成cypress-log-to-output安装、log任务注册,之后读取配置判断存在extends字段时,直接执行return deepmerge(baseConfig, configJson)退出函数。Cypress的插件导出函数一旦返回,后续所有事件监听、任务注册逻辑都不会运行,因此后续写的文件预处理器注册、浏览器启动钩子、queryDb任务注册的代码全部被跳过,最终Cypress只能识别到最早注册的log任务。

额外注意:Cypress 9.x中多次调用on('task', 任务对象)不会自动合并已注册的任务,后一次传入的任务对象会覆盖前一次的注册内容,原有写法即使去掉提前返回,也会出现log任务被queryDb覆盖的问题。

修复方案

调整插件函数的执行顺序,遵循「先处理配置合并,再统一注册所有事件/任务,最后返回配置」的结构:

  1. 把配置合并、环境变量注入的逻辑移到函数最前部,先拿到最终的完整配置对象,不要在注册流程中间插入提前返回的分支
  2. 合并所有task到同一个on('task', {})调用中,避免注册被覆盖
  3. 统一注册预处理器、浏览器启动钩子等其他事件
  4. 最后返回合并完成的配置对象

调整后的插件核心结构参考:

module.exports = (on, config) => {
    // 1. 最先处理配置合并
    const configJson = require(config.configFile)
    let finalConfig = configJson
    if (configJson.extends) {
        const baseConfigFilename = path.join(config.projectRoot, configJson.extends)
        const baseConfig = require(baseConfigFilename)
        console.log('merging %s with %s', baseConfigFilename, config.configFile)
        configJson.env.my_db_name = process.env.my_DB_NAME;
        configJson.env.my_db_host = process.env.my_DB_HOST;
        configJson.env.my_db_user = process.env.my_DB_USER;
        configJson.env.my_db_password = process.env.my_DB_PASSWORD;
        configJson.env.my_db_port = process.env.my_DB_PORT;
        finalConfig = deepmerge(baseConfig, configJson);
    }

    // 2. 再注册所有插件、事件、任务
    require('cypress-log-to-output').install(on)
    on("file:preprocessor", preprocess);

    on('before:browser:launch', (browser , launchOptions) => {
        if (browser.name === 'chrome' && browser.isHeadless) {
            launchOptions.args.push('--disable-gpu', '--no-sandbox', '--disable-dev-shm-usage', '--window-size=1920,1080');
            return launchOptions
        }
    })

    // 所有task统一注册,避免覆盖
    on('task', {
        log (message) {
            console.log(message)
            return true
        },
        'queryDb': query => {
            return queryTestDb(query, finalConfig);
        }
    });

    // 3. 最后返回配置
    return finalConfig
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 19:54:23