我的问题与此非常相似:Behave:用动态示例编写场景大纲。不同的是我不使用Python。我使用 Cypress 处理我的 Gherkin 场景(通过 cypress-cucumber-preprocessor 库:https://github.com/TheBrainFamily/cypress-cucumber-preprocessor)。
假设我有这个场景大纲(用我的 Jira 编写):
Given I provide <a list of numbers> and <another list of numbers>
Then I check wether they are equals
Examples:
| list of numbers | another list of numbers |
| 1 | 1 |
| 100 | 200 |
我想动态设置我的号码,因为我将从 REST 呼叫中收到它们。有办法做到吗?
在具有Behaviour的Python中,似乎可以使用before_feature()来做到这一点。
场景是这样的:
Given I provide <a list of numbers> and <another list of numbers>
Then I check wether they are equals
Examples:
| list of numbers | another list of numbers |
| . | . |
但我不知道如何迭代我的示例来设置它们。可以吗?
理想情况下,测试不应该很复杂,并且其结果应该是固定的和预期的。因此,您可以模拟服务调用以根据您的测试返回响应。
但是,对于您的解决方案,您可以使用一些可以在开始测试之前更换的支架。例如
Given I provide <a list of numbers> and <another list of numbers>
Then I check wether they are equals
Examples:
| list of numbers | another list of numbers |
| %A% | %B% |
编写代码,将持有者的值替换为您从 REST API 调用收到的响应
//..
const contents = fs.readFileSync("path/of/file.feature").toString();
contents.replace("%A%", "23");
//..
是的,这是可能的! :D
功能文件:
Given I provide <a list of numbers> and <another list of numbers>
Then I check wether they are equals
Examples:
| list of numbers | another list of numbers |
| . | . |
测试文件:
import { Given, When, Then, And } from 'cypress-cucumber-preprocessor/steps'
Given("I provide {} and {}", (aListOfNumbers, anotherListOfNumbers) => {
// You can pass value from step to step using .as('something')
});
是的,这是可能的,但有点棘手。
阿米特的答案提供了核心答案,但没有提供任何必需的细节,所以我想我会让你知道我是如何解决这个问题的。
因此,解决方案是更新 esBuildPlugin,以便我们可以在将动态数据编译到打字稿之前插入动态数据。
您可以看到 esBuildPlugin 已导入到 cypress.config.ts 中,如此处的示例配置所示:https://github.com/badeball/cypress-cucumber-preprocessor/blob/master/docs/quick-start .md
我们需要做的是将“@badeball/cypress-cucumber-preprocessor/esbuild”文件替换为我们自己的文件,该文件将扫描文件中的场景大纲并将我们的动态数据添加到其中。幸运的是,这是一个非常简单的文件(可以在他们的github中找到),所以并不困难。
我扩展了这个文件,以便您可以传入任意数量的“插件”,这些“插件”将接受一个字符串并返回一个字符串的承诺。这些插件将依次传递原始特征文件,并且它们可以在返回最终值之前根据需要更改文件。
import esbuild from 'esbuild';
import { ICypressConfiguration } from '../../node_modules/@badeball/cypress-cucumber-preprocessor/dist/subpath-entrypoints/esbuild';
import { compile } from '../../node_modules/@badeball/cypress-cucumber-preprocessor/dist/template';
export type IPlugin = (content: string) => Promise<string>;
function getPluginContent(content: string, plugins?: IPlugin[]): Promise<string> {
if (!plugins) {
return Promise.resolve(content);
}
return plugins.reduce(async (acc, plugin) => {
return plugin(await acc);
}, Promise.resolve(content));
}
export default function myEsbuildPlugin(
configuration: ICypressConfiguration,
plugins?: IPlugin[]
): esbuild.Plugin {
return {
name: 'feature',
setup(build) {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const fs = require("fs") as typeof import("fs");
build.onLoad({ filter: /\.feature$/ }, async (args) => {
const originalContent = await fs.promises.readFile(args.path, 'utf8');
const content = await getPluginContent(originalContent, plugins);
return {
contents: await compile(configuration, content, args.path),
loader: 'js',
};
});
},
};
}
有了这个插件架构,现在我们需要做的就是提供一个插件来查找需要动态数据的测试并插入动态数据。
我创建了一个名为 DynamicScenarioOutlinePlugin.ts 的插件。它非常简单,它只是查找任何与“Examples:fileToLoad.examples”匹配的行,然后从fixtures目录加载匹配的文件并将其插入到文件中。
const fs = require('fs') as typeof import('fs');
export default async function myEsbuildPluginPlugin(
content: string
): Promise<string> {
if (!content.includes('Examples:')) {
return content;
}
let match: RegExpExecArray | null;
while ((match = /Examples:\s*(.*?\.examples)/gi.exec(content))) {
let file = await fs.promises.readFile(`./cypress/fixtures/${match[1]}`);
content = content.replace(match[0], 'Examples:\n' + file);
}
return content;
}
最后,我们只需要将它们全部放在 cypress.config.ts 中即可
import { defineConfig } from 'cypress';
import createBundler from '@bahmutov/cypress-esbuild-preprocessor';
import { addCucumberPreprocessorPlugin } from '@badeball/cypress-cucumber-preprocessor';
import myEsbuildPlugin from './src/utils/myEsbuildPlugin'
import DynamicScenarioOutlinePlugin from './src/utils/DynamicScenarioOutlinePlugin';
async function addCucumber(on, config) {
await addCucumberPreprocessorPlugin(on, config); // This is required for the preprocessor to be able to generate JSON reports after each run, and more,
on(
'file:preprocessor', //This converts the feature files into js
createBundler({ plugins: [myEsbuildPlugin(config, [DynamicScenarioOutlinePlugin]) ] })
);
}
export default defineConfig({
projectId: '6oef4g',
e2e: {
async setupNodeEvents(on, config) {
await addCucumber(on, config);
return config;
},
specPattern: '**/*.{feature,cy.ts,xxx}',
viewportHeight: 667, //iphone 8
viewportWidth: 375, //iphone 8
},
env: {
url: 'http://127.0.0.1:8080',
},
});
使用以下测试文件:
Feature: Translate and Save
Scenario Outline: save to selected deck
Given I am on the search page
When I set the translation direction to "<translation-direction>"
When I type "<spanish>" in the search box
Then I should see "<english>" in the response
When I open the deck dropdown
And I set the save direction to "<save-direction>"
And I select "<deck>"
When I click the save button
Then I should see a POST request to /addCard with the following body:
| deckName | front | back |
| <deck> | <front> | <back> |
Examples:saveToSelectedDeck.examples
这个 saveToSelectedDeck.examples 文件:
| translation-direction | spanish | english | save-direction | deck | front | back |
| S->E | Hola | Hello | E->S | default | Hola | Hello |
| S->E| Hola | Hello | E->S | deck2 | Hola | Hello |
| S->E | Hola | Hello | E->S | deck3 | Hola | Hello |
我们带着下面的 cypress 到达,我们已经加载了所有 3 个示例