目前我们没有一个好的方法来以编程方式运行cucumber-js。 需求来自两个角度:
目前倾向于发生的是Cli
一个新实例是用串在一起的 argv input创建的。 它显然非常笨拙,也不在公共 API 上。
有时(可能是由于感知到上述的脆弱性)框架将只依赖黄瓜-js CLI,但很难找到集成的方法并有自己的选择。
Runtime
类目前是公共 API 的一部分,但它在这些上下文中没有用,这取决于调用者提供的泡菜和支持代码。
项目中的两个组件:
runCucumber
在进程中执行测试运行的新异步函数。 职责:
这将是公共 API 的一部分,我们鼓励框架维护者在“包装”cucumber-js 时使用它。 我们也会将它用于我们自己的测试。
尽可能避免与process
直接交互,而是接受规范化的选项和流接口用于输出,并将其留给调用者根据结果或未处理的错误决定如何退出。
另外Runtime
应该脱离公共 API,因为它确实是内部的事情。
实际上是runCucumber
的“客户”。 职责:
runCucumber
这将继续不在公共 API 上。 此外,它只会用功能/界面,是对公共API,这样,我们可以很容易地打破它变成自己的包在某些时候,因为是一种常见的模式现在喜欢的项目玩笑。
这种解耦也为一些有趣的新 CLI 功能铺平了道路,而不会让它们渗入内部,例如:
--gui
用于黄瓜电子的东西--interactive
用于在 TDD'ing 时快速有针对性地重新运行我们还将公开以下功能(由 CLI 和其他人使用):
i18nKeywords
和i18nLanguages
我们将在即将发布的 8.0.0 版本中实现这一目标。 我准备今天开始。
寻呼初始反馈: @aslakhellesoy @charlierudolph @aurelien-reeves @mattwynne @nicojs @jan-molak
我喜欢这个提议! 我们已经在 fake-cucumber 的runCucumber 中提供了这样的 API
@davidjgoss - 听起来很棒!
供您参考,以下是目前 Serenity/JS 调用 Cucumber 的方式 - CucumberCLIAdapter
这是将配置参数转换为 argv - CucumberOptions
的逻辑。
能够提供一个选项对象而不是 argv 会更好👍🏻
爱它!
在指定新的公共 API 的同时,我们还可以考虑最近发生的问题 #1489 并考虑提供公共 API 以与过滤器和测试中的结果功能进行更多更好的交互
拥有公共 API 总比没有好,所以继续吧👍!
最好我也有一个 API 来使用与cucumber-js
相同的规则加载配置文件,这样我就可以模拟普通黄瓜 js 调用的确切行为。
loadProfiles(directory = process.cwd()): Record<string, Profile>
StrykerJS 还将严重依赖custom_formatters API 和eventBroadcaster
发布的事件。 我们也可以将它们添加到公共 API 中吗? 参见: https :
我最好也有一个 API 来使用与 Cucumber-js 相同的规则来加载配置文件,这样我就可以模拟正常的 Cucumber-js 调用的确切行为。
这是个好的观点。 配置文件(至少以其当前形式)从根本上与 CLI 耦合,因此将它们保留在边界的那一侧感觉是正确的,但我们仍然可以公开一个函数来加载它们并生成部分选项对象。
在指定新的公共 API 的同时,我们还可以考虑最近发生的问题 #1489,并考虑提供公共 API 以便与过滤器和测试中的结果功能进行更多更好的交互。
我认为我们可以在调用 API 时包含一个提供自定义 pickle 过滤器的选项(除了驱动内置过滤的名称、标签等)。
如果非常命令行y,则配置文件的当前语法。
我希望 ❤️❤️❤️ 能够以更通用的格式指定配置文件,例如 JSON、JavaScript、YAML 或环境变量。 在 JSON 中,它可能如下所示:
.cucumber.json
{
"default": {
"requireModule": ["ts-node/register"],
"require": ["support/**/*./ts"],
"worldParameters": {
"appUrl": "http://localhost:3000/",
},
"format": ["progress-bar", "html:./cucumber-report.html"]
},
"ci": {
"requireModule": ["ts-node/register"],
"require": ["support/**/*./ts"],
"worldParameters": {
"appUrl": "http://localhost:3000/",
},
"format": ["html:./cucumber-report.html"],
"publish": true
}
}
或者,使用 JavaScript
.cucumber.js
const common = {
"requireModule": ["ts-node/register"],
"require": ["support/**/*./ts"],
"worldParameters": {
"appUrl": "http://localhost:3000/",
}
}
module.exports = {
default: {
...common,
"format": ["progress-bar", "html:./cucumber-report.html"]
},
ci: {
...common,
"format": ["html:./cucumber-report.html"],
"publish": true
}
}
或者甚至使用环境变量(例如加载了像 dotenv 这样的工具):
.cucumber.env
CUCUMBER_PROFILE_DEFAULT_REQUIREMODULE=ts-node/register
CUCUMBER_PROFILE_DEFAULT_REQUIRE=ts-node/register
CUCUMBER_PROFILE_DEFAULT_WORLDPARAMETERS_APPURL=http://localhost:3000/
CUCUMBER_PROFILE_DEFAULT_FORMAT=progress-bar,html:./cucumber-report.html
CUCUMBER_PROFILE_CI_REQUIREMODULE=ts-node/register
CUCUMBER_PROFILE_CI_REQUIRE=ts-node/register
CUCUMBER_PROFILE_CI_WORLDPARAMETERS_APPURL=http://localhost:3000/
CUCUMBER_PROFILE_CI_FORMAT=progress-bar,html:./cucumber-report.html
CUCUMBER_PROFILE_CI_PUBLISH=true
事实上,配置库正是这样做的。 我们最终没有将它集成到 Cucumber-JVM 中,因为其他东西妨碍了它,但也许我们可以尝试一下 JavaScript 实现?
@aslakhellesoy同意那会很棒! 我会尝试为这个提案建立一个 POC,所以我们有一些更具体的东西可以讨论,并且很乐意将个人资料作为其中的一部分(4.5 年,指望 #751 😄)
参考文献第1004章
这是个好的观点。 配置文件(至少以其当前形式)从根本上与 CLI 耦合,因此将它们保留在边界的那一侧感觉是正确的,但我们仍然可以公开一个函数来加载它们并生成部分选项对象。
是的,从插件创建者的角度来看,这将是非常棒的并且非常感谢。
我希望 ❤️❤️❤️ 能够以更通用的格式指定配置文件,例如 JSON、JavaScript、YAML 或环境变量。 在 JSON 中,它可能如下所示:
听起来不错! 这也正是我喜欢 API 以与 CucumberJS 相同的方式加载它们的原因。 加载单个cucumber.js
文件是微不足道的。 复制配置文件加载算法,包括优先级,文件格式等并维护它完全是另一回事😅。
问:我是否可以连续运行runCucumber
两次_而不清除require 缓存_? 这对于突变测试用例很重要。
我们希望加载环境并快速连续多次运行测试,同时更改全局变量以切换活动突变体。
现在,我们正在使用cli
私有 API ,我们需要在每次测试运行之间清除require.cache
的步骤定义文件。 这对于 CommonJS 来说并不理想,对于 esm 根本不起作用。
我们用例的伪代码:
const profiles = await loadProfiles();
const options = {
...profiles.default,
formatter: require.resolve('./our-awesomely-crafted-formatter'),
some: 'other options we want to override',
}
const cucumber = new Cucumber(options);
// Allow cucumber to load the step definitions once.
// This is `async`, so support for esm can be added without a breaking change
await cucumber.initialize();
// Initial test run ("dry run"), without mutants active
await cucumber.run();
collectMutantCoveragePerTestFromFormatter();
// Start mutation testing:
global.activeMutant = 1;
await cucumber.run({ features: ['features/a.feature:24']);
collectResultsFromFormatterToDetermineKilledOrSurvivedMutant()
global.activeMutant = 2;
await cucumber.run({ features: ['features/b.feature:24:25:26', 'features/c.feature:12']);
collectResultsFromFormatterToDetermineKilledOrSurvivedMutant()
// etc
@nicojs绝对同意我们需要这个,之前出现过几次,例如人们想要在 lambda 中运行黄瓜,我还想添加一个也需要它的交互模式。
到目前为止,我所勾画的更多是一种功能风格,但我认为是相同的基本概念:
const runnerOptions = {
support: {
require: ['features/support/**/*.js']
}
}
// initial run returns support code library
const { support } = await runCucumber(runnerOptions)
// subsequent run reuses support code library
await runCucumber({
...runnerOptions,
support
})
这对我们有用👍
对我们也有用👍🏻
我真的认为这样的事情将非常有用,作为我们今天测试工具集成(Jest,Cypress)的巨大混乱的替代方案,例如我发现了这些问题(按重要性排序):
cypress-cucumber-preprocessor
不支持示例上的标签 (https://github.com/TheBrainFamily/cypress-cucumber-preprocessor/issues/196)jest-cucumber
不支持 Cucumber JSON 报告 (https://github.com/bencompton/jest-cucumber/issues/27)cypress-cucumber-preprocessor
生成多个 Cucumber JSON 报告,没有官方支持聚合 (https://github.com/TheBrainFamily/cypress-cucumber-preprocessor/issues/423)jest-cucumber
不如jest-cucumber-fusion
cucumber-jest
...我宁愿在 Jest/Karma/Cypress/etc 之间看到一些最小的胶水代码。 和黄瓜-js,所以我不必为我需要使用的
好的,这是“运行”位的 API 签名的第一遍。它很大程度上基于我们内部拥有的IConfiguration
对象(所以不应该导致过多的重构),但只是少了一点“平坦”:
export interface IRunCucumberOptions {
cwd: string
features: {
defaultDialect?: string
paths: string[]
}
filters: {
name?: string[]
tagExpression?: string
}
support:
| {
transpileWith?: string[]
paths: string[]
}
| ISupportCodeLibrary
runtime: {
dryRun?: boolean
failFast?: boolean
filterStacktraces?: boolean
parallel?: {
count: number
}
retry?: {
count: number
tagExpression?: string
}
strict: boolean
worldParameters?: any
}
formats: {
stdout: string
files: Record<string, string>
options: IParsedArgvFormatOptions
}
}
export interface IRunResult {
success: boolean
support: ISupportCodeLibrary
}
export async function runCucumber(
options: IRunCucumberOptions
): Promise<IRunResult> {
// do stuff
}
还有一个非常人为的示例用法:
const result = await runCucumber({
cwd: process.cwd(),
features: {
paths: ['features/**/*.feature'],
},
filters: {
name: ['Acme'],
tagExpression: '<strong i="10">@interesting</strong>',
},
support: {
transpileWith: ['ts-node'],
paths: ['features/support/**/*.ts'],
},
runtime: {
failFast: true,
retry: {
count: 1,
tagExpression: '<strong i="11">@flaky</strong>',
},
strict: true,
worldParameters: {
foo: 'bar',
},
},
formats: {
stdout: '@cucumber/pretty-formatter',
files: {
'report.html': 'html',
'TEST-cucumber.xml': 'junit',
},
options: {
printAttachments: false,
},
},
})
欢迎反馈!请注意,这不包括配置文件/配置加载内容,这将是另一个功能。
我觉得这看起来不错。 问题:我将如何配置自定义格式化程序?
@nicojs有点像 CLI
formats: {
files: {
'report.html': './my/fancy-reporter.js',
'other-report.html': '@me/reporter-package',
}
},
很高兴看到这个@davidjgoss 的进展!
我不想放慢这方面的进展,但同时我想确保我们采用的格式也适用于其他 Cucumber 实现。
最终是一个 JSON 模式,但在我们讨论它时,我认为 TypeScript 类型对我们人类来说更容易解析。
我建议我们在cucumber/common
monorepo 中创建一个关于提议格式的新问题,并邀请核心团队在那里讨论。
@aslakhellesoy会做的。
您如何看待编程 API 不绑定到通用选项结构? 就像我们从那个映射到runCucumber
选项一样。 它可能会增加一些复杂性,但很有吸引力,因为它有一个support
块,它可以是要加载的参数,也可以是先前加载的支持代码库。 也可以对功能+泡菜做类似的事情。 我们会在 CLI 上支持各种不适合编程 API 的选项(例如--exit
)。
您如何看待编程 API 不绑定到通用选项结构?
我认为这很好,只要我们提供一个函数,将选项文件内容转换为runCucumber
想要的数据结构。
最终是一个 JSON 模式,但在我们讨论它时,我认为 TypeScript 类型对我们人类来说更容易解析。
为什么我们需要选择? 我们在 StrykerJS 中使用 JSON 模式使用json-schema-to- typescript 生成prebuild
步骤即时生成它们。
JSON 模式对于人类 IMO 来说仍然具有一定的可读性。 我们已经在 Stryker 仓库上发布了 PR,人们似乎知道该怎么做 🤷♀️
有点像 CLI
formats: { files: { 'report.html': './my/fancy-reporter.js', 'other-report.html': '@me/reporter-package', } },
对于不需要输出文件名的记者来说,这将如何工作? 像这样:
formats: {
files: {
'': require.resolve('./my-awesome-stryker-formatter')
}
}
为什么我们需要选择?
我认为我们应该使用 JSON Schema 作为配置结构的单一事实来源。 - 然后从该模式生成 TypeScript/Java/Whatever 代码。
但是 JSON Schema 对于人类来说有点难以阅读,因此当我们在cucumber/common
的 GitHub 问题中讨论模式时,我建议使用 TypeScript 来促进讨论。
明白了吗?
JSON 模式对于人类 IMO 仍然有些可读
不适合我:-) 太冗长了。
对于不需要输出文件名的记者来说,这将如何工作?
formats: {
stdout: './my-awesome-stryker-formatter',
files: {
'report.html': './my/fancy-reporter.js',
'other-report.html': '@me/reporter-package',
}
},
(只有一个格式化程序可以使用 stdout 流)
最有用的评论
(只有一个格式化程序可以使用 stdout 流)