第一次失败后,Jest停止测试套件

San*_*rez 15 javascript testing unit-testing jestjs

我正在使用Jest进行测试.

我想要的是,当测试套件中的测试失败时,停止执行当前的测试套件.

该选项--bail不是我需要的,因为它在一个测试套件失败后停止其他测试套件.

Lar*_*isa 7

我有连续且复杂的测试场景,如果该套件的其中一项测试失败,则没有必要继续测试套件。但我没有设法将它们标记为已跳过,因此它们显示为已通过。

我的测试套件的示例:

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)

如果第一个测试失败,则下一个测试将不会运行,但它们将获得“通过”状态。

该报告将如下所示:

  • 测试场景 1 > 可以创建该项目 -失败
  • 测试场景 1 > 该项目可以删除 -通过

这并不理想,但在我的情况下可以接受,因为我只想在报告中看到失败的测试。


ysf*_*ran 6

感谢github 上的评论,我能够使用自定义testEnvironment. 为此,jest-circus需要通过npm/安装yarn
\n值得注意的是,jest 会将 jest-circus 设置为 jest v27 的默认运行器

\n

首先需要调整 jest 配置:

\n

jest.config.js

\n
module.exports = {\n  rootDir: ".",\n  testRunner: "jest-circus/runner",\n  testEnvironment: "<rootDir>/NodeEnvironmentFailFast.js",\n}\n\n
Run Code Online (Sandbox Code Playgroud)\n

然后你需要实现一个自定义环境,上面的配置已经引用了它:

\n

NodeEnvironmentFailFast.js

\n
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\n
Run Code Online (Sandbox Code Playgroud)\n
\n

笔记

\n

我添加了registerTestEventHandler快速失败功能不需要的功能,但我认为它非常有用,特别是如果您jasmine.getEnv()之前使用过并且它与async/一起使用await!\n您可以在测试
注册自定义处理程序(例如钩子),如下所示:beforeAll

\n
// testEnvironment is globally available (see above NodeEnvironmentFailFast.setup)\ntestEnvironment.registerTestEventHandler(async (event) => {\n  if (event.name === "test_fn_failure") {\n    await takeScreenshot()\n  }\n})\n
Run Code Online (Sandbox Code Playgroud)\n
\n

当其中一个语句失败时,同一语句中的test其他语句将被跳过。这也适用于嵌套块,但块必须具有不同的名称。testdescribedescribedescribe

\n

执行以下测试:

\n
describe("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})\n
Run 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\n
Run Code Online (Sandbox Code Playgroud)\n


Mix*_*OID 3

我做了一些拼凑,但它对我有用。

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 进行测试