San*_*rez 15 javascript testing unit-testing jestjs
我正在使用Jest进行测试.
我想要的是,当测试套件中的测试失败时,停止执行当前的测试套件.
该选项--bail不是我需要的,因为它在一个测试套件失败后停止其他测试套件.
我有连续且复杂的测试场景,如果该套件的其中一项测试失败,则没有必要继续测试套件。但我没有设法将它们标记为已跳过,因此它们显示为已通过。
我的测试套件的示例:
describe('Test scenario 1', () => {
test('that item can be created', async () => {
expect(true).toBe(false)
})
test('that item can be deleted', async () => {
...
})
...
Run Code Online (Sandbox Code Playgroud)
我将其更改为以下内容:
let hasTestFailed = false
const sequentialTest = (name, action) => {
test(name, async () => {
if(hasTestFailed){
console.warn(`[skipped]: ${name}`)}
else {
try {
await action()}
catch (error) {
hasTestFailed = true
throw error}
}
})
}
describe('Test scenario 1', () => {
sequentialTest('that item can be created', async () => {
expect(true).toBe(false)
})
sequentialTest('that item can be deleted', async () => {
...
})
Run Code Online (Sandbox Code Playgroud)
如果第一个测试失败,则下一个测试将不会运行,但它们将获得“通过”状态。
该报告将如下所示:
这并不理想,但在我的情况下可以接受,因为我只想在报告中看到失败的测试。
感谢github 上的评论,我能够使用自定义testEnvironment. 为此,jest-circus需要通过npm/安装yarn。
\n值得注意的是,jest 会将 jest-circus 设置为 jest v27 的默认运行器。
首先需要调整 jest 配置:
\njest.config.js
module.exports = {\n rootDir: ".",\n testRunner: "jest-circus/runner",\n testEnvironment: "<rootDir>/NodeEnvironmentFailFast.js",\n}\n\nRun Code Online (Sandbox Code Playgroud)\n然后你需要实现一个自定义环境,上面的配置已经引用了它:
\nNodeEnvironmentFailFast.js
const NodeEnvironment = require("jest-environment-node")\n\nclass NodeEnvironmentFailFast extends NodeEnvironment {\n failedDescribeMap = {}\n registeredEventHandler = []\n\n async setup() {\n await super.setup()\n this.global.testEnvironment = this\n }\n\n registerTestEventHandler(registeredEventHandler) {\n this.registeredEventHandler.push(registeredEventHandler)\n }\n\n async executeTestEventHandlers(event, state) {\n for (let handler of this.registeredEventHandler) {\n await handler(event, state)\n }\n }\n\n async handleTestEvent(event, state) {\n await this.executeTestEventHandlers(event, state)\n\n switch (event.name) {\n case "hook_failure": {\n const describeBlockName = event.hook.parent.name\n\n this.failedDescribeMap[describeBlockName] = true\n // hook errors are not displayed if tests are skipped, so display them manually\n console.error(`ERROR: ${describeBlockName} > ${event.hook.type}\\n\\n`, event.error, "\\n")\n break\n }\n case "test_fn_failure": {\n this.failedDescribeMap[event.test.parent.name] = true\n break\n }\n case "test_start": {\n if (this.failedDescribeMap[event.test.parent.name]) {\n event.test.mode = "skip"\n }\n break\n }\n }\n\n if (super.handleTestEvent) {\n super.handleTestEvent(event, state)\n }\n }\n}\n\nmodule.exports = NodeEnvironmentFailFast\nRun Code Online (Sandbox Code Playgroud)\n笔记
\n我添加了registerTestEventHandler快速失败功能不需要的功能,但我认为它非常有用,特别是如果您jasmine.getEnv()之前使用过并且它与async/一起使用await!\n您可以在测试中
注册自定义处理程序(例如钩子),如下所示:beforeAll
// testEnvironment is globally available (see above NodeEnvironmentFailFast.setup)\ntestEnvironment.registerTestEventHandler(async (event) => {\n if (event.name === "test_fn_failure") {\n await takeScreenshot()\n }\n})\nRun Code Online (Sandbox Code Playgroud)\n当其中一个语句失败时,同一语句中的test其他语句将被跳过。这也适用于嵌套块,但块必须具有不同的名称。testdescribedescribedescribe
执行以下测试:
\ndescribe("TestJest 3 ", () => {\n describe("TestJest 2 ", () => {\n describe("TestJest 1", () => {\n beforeAll(() => expect(1).toBe(2))\n test("1", () => {})\n test("1.1", () => {})\n test("1.2", () => {})\n })\n\n test("2", () => expect(1).toBe(2))\n test("2.1", () => {})\n test("2.2", () => {})\n })\n\n test("3", () => {})\n test("3.1", () => expect(1).toBe(2))\n test("3.2", () => {})\n})\nRun Code Online (Sandbox Code Playgroud)\n将产生以下日志:
\n FAIL suites/test-jest.spec.js\n TestJest 3 \n \xe2\x9c\x93 3\n \xe2\x9c\x95 3.1 (1 ms)\n \xe2\x97\x8b skipped 3.2\n TestJest 2 \n \xe2\x9c\x95 2\n \xe2\x97\x8b skipped 2.1\n \xe2\x97\x8b skipped 2.2\n TestJest 1\n \xe2\x97\x8b skipped 1\n \xe2\x97\x8b skipped 1.1\n \xe2\x97\x8b skipped 1.2\n\n \xe2\x97\x8f TestJest 3 \xe2\x80\xba TestJest 2 \xe2\x80\xba TestJest 1 \xe2\x80\xba 1\n\n expect(received).toBe(expected) // Object.is equality\n\n Expected: 2\n Received: 1\n\n 2 | describe("TestJest 2 ", () => {\n 3 | describe("TestJest 1", () => {\n > 4 | beforeAll(() => expect(1).toBe(2))\n | ^\n 5 | test("1", () => {})\n 6 | test("1.1", () => {})\n 7 | test("1.2", () => {})\n\n at suites/test-jest.spec.js:4:33\n\n \xe2\x97\x8f TestJest 3 \xe2\x80\xba TestJest 2 \xe2\x80\xba TestJest 1 \xe2\x80\xba 1.1\n\n expect(received).toBe(expected) // Object.is equality\n\n Expected: 2\n Received: 1\n\n 2 | describe("TestJest 2 ", () => {\n 3 | describe("TestJest 1", () => {\n > 4 | beforeAll(() => expect(1).toBe(2))\n | ^\n 5 | test("1", () => {})\n 6 | test("1.1", () => {})\n 7 | test("1.2", () => {})\n\n at suites/test-jest.spec.js:4:33\n\n \xe2\x97\x8f TestJest 3 \xe2\x80\xba TestJest 2 \xe2\x80\xba TestJest 1 \xe2\x80\xba 1.2\n\n expect(received).toBe(expected) // Object.is equality\n\n Expected: 2\n Received: 1\n\n 2 | describe("TestJest 2 ", () => {\n 3 | describe("TestJest 1", () => {\n > 4 | beforeAll(() => expect(1).toBe(2))\n | ^\n 5 | test("1", () => {})\n 6 | test("1.1", () => {})\n 7 | test("1.2", () => {})\n\n at suites/test-jest.spec.js:4:33\n\n \xe2\x97\x8f TestJest 3 \xe2\x80\xba TestJest 2 \xe2\x80\xba 2\n\n expect(received).toBe(expected) // Object.is equality\n\n Expected: 2\n Received: 1\n\n 8 | })\n 9 | \n > 10 | test("2", () => expect(1).toBe(2))\n | ^\n 11 | test("2.1", () => {})\n 12 | test("2.2", () => {})\n 13 | })\n\n at Object.<anonymous> (suites/test-jest.spec.js:10:31)\n\n \xe2\x97\x8f TestJest 3 \xe2\x80\xba 3.1\n\n expect(received).toBe(expected) // Object.is equality\n\n Expected: 2\n Received: 1\n\n 14 | \n 15 | test("3", () => {})\n > 16 | test("3.1", () => expect(1).toBe(2))\n | ^\n 17 | test("3.2", () => {})\n 18 | })\n 19 | \n\n at Object.<anonymous> (suites/test-jest.spec.js:16:31)\n\nTest Suites: 1 failed, 1 total\nTests: 2 failed, 6 skipped, 1 passed, 9 total\nSnapshots: 0 total\nTime: 0.638 s, estimated 1 s\n\nRun Code Online (Sandbox Code Playgroud)\n
我做了一些拼凑,但它对我有用。
stopOnFirstFailed.js:
/**
* This is a realisation of "stop on first failed" with Jest
* @type {{globalFailure: boolean}}
*/
module.exports = {
globalFailure: false
};
// Injects to jasmine.Spec for checking "status === failed"
!function (OriginalSpec) {
function PatchedSpec(attrs) {
OriginalSpec.apply(this, arguments);
if (attrs && attrs.id) {
let status = undefined;
Object.defineProperty(this.result, 'status', {
get: function () {
return status;
},
set: function (newValue) {
if (newValue === 'failed') module.exports.globalFailure = true;
status = newValue;
},
})
}
}
PatchedSpec.prototype = Object.create(OriginalSpec.prototype, {
constructor: {
value: PatchedSpec,
enumerable: false,
writable: true,
configurable: true
}
});
jasmine.Spec = PatchedSpec;
}(jasmine.Spec);
// Injects to "test" function for disabling that tasks
test = ((testOrig) => function () {
let fn = arguments[1];
arguments[1] = () => {
return module.exports.globalFailure ? new Promise((res, rej) => rej('globalFailure is TRUE')) : fn();
};
testOrig.apply(this, arguments);
})(test);
Run Code Online (Sandbox Code Playgroud)
在所有测试之前导入该文件(在first之前test(...)),例如 my index.test.js:
require('./core/stopOnFirstFailed'); // before all tests
test(..., ()=>...);
...
Run Code Online (Sandbox Code Playgroud)
failed当第一个错误发生时,该代码用标签标记所有接下来的测试globalFailure is TRUE。
如果你想排除failing, 例如。您可以这样做一些清理测试:
const stopOnFirstFailed = require('../core/stopOnFirstFailed');
describe('some protected group', () => {
beforeAll(() => {
stopOnFirstFailed.globalFailure = false
});
test(..., ()=>...);
...
Run Code Online (Sandbox Code Playgroud)
它将整个组排除在外failing。
使用 Node 8.9.1 和 Jest 23.6.0 进行测试