[英]Jest stop test suite after first fail
I am using Jest for testing.我正在使用Jest进行测试。
What I want, is to stop executing the current test suite when a test in that test suite fails.我想要的是当该测试套件中的测试失败时停止执行当前测试套件。
The --bail
option is not what I need, since it will stop other test suites after one test suite fails. --bail
选项不是我需要的,因为它会在一个测试套件失败后停止其他测试套件。
I've made some kludge but it works for me.我做了一些 kludge 但它对我有用。
stopOnFirstFailed.js
: 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);
Imports that file before all tests (before first test(...)
), for ex my index.test.js
:在所有测试之前(在第一次
test(...)
之前test(...)
导入该文件,例如我的index.test.js
:
require('./core/stopOnFirstFailed'); // before all tests
test(..., ()=>...);
...
That code marks all next tests failed
with label globalFailure is TRUE
when first error happens.当第一个错误发生时,该代码标记所有下一个测试
failed
,标签globalFailure is TRUE
。
If you want to exclude failing
, for ex.如果你想排除
failing
,例如。 some cleanup tests you can do like this:您可以像这样执行一些清理测试:
const stopOnFirstFailed = require('../core/stopOnFirstFailed');
describe('some protected group', () => {
beforeAll(() => {
stopOnFirstFailed.globalFailure = false
});
test(..., ()=>...);
...
It excludes whole group from failing
.它排除了整个小组的
failing
。
Tested with Node 8.9.1 and Jest 23.6.0使用 Node 8.9.1 和 Jest 23.6.0 进行测试
Thanks to this comment on github I was able to resolve this with a custom testEnvironment
.感谢github上的这个评论,我能够使用自定义
testEnvironment
解决这个问题。 For this to work jest-circus
need to be installed via npm
/ yarn
.为此,
jest-circus
需要通过npm
/ yarn
安装。
It's worth noting that jest will set jest-circus to the default runner with jest v27 .值得注意的是, jest 将 jest-circus 设置为 jest v27 的默认跑步者。
First of all jest configuration needs to be adapted:首先需要调整jest配置:
jest.config.js
module.exports = {
rootDir: ".",
testRunner: "jest-circus/runner",
testEnvironment: "<rootDir>/NodeEnvironmentFailFast.js",
}
Then you need to implement a custom environment, which is already referenced by the config above:然后你需要实现一个自定义环境,上面的配置已经引用了:
NodeEnvironmentFailFast.js
const NodeEnvironment = require("jest-environment-node")
class NodeEnvironmentFailFast extends NodeEnvironment {
failedDescribeMap = {}
registeredEventHandler = []
async setup() {
await super.setup()
this.global.testEnvironment = this
}
registerTestEventHandler(registeredEventHandler) {
this.registeredEventHandler.push(registeredEventHandler)
}
async executeTestEventHandlers(event, state) {
for (let handler of this.registeredEventHandler) {
await handler(event, state)
}
}
async handleTestEvent(event, state) {
await this.executeTestEventHandlers(event, state)
switch (event.name) {
case "hook_failure": {
const describeBlockName = event.hook.parent.name
this.failedDescribeMap[describeBlockName] = true
// hook errors are not displayed if tests are skipped, so display them manually
console.error(`ERROR: ${describeBlockName} > ${event.hook.type}\n\n`, event.error, "\n")
break
}
case "test_fn_failure": {
this.failedDescribeMap[event.test.parent.name] = true
break
}
case "test_start": {
if (this.failedDescribeMap[event.test.parent.name]) {
event.test.mode = "skip"
}
break
}
}
if (super.handleTestEvent) {
super.handleTestEvent(event, state)
}
}
}
module.exports = NodeEnvironmentFailFast
NOTE笔记
I added registerTestEventHandler
functionality which is not necessary for the fail fast feature, but I thought it's quite useful, especially if you used jasmine.getEnv()
before and it works with async
/ await
!我添加了
registerTestEventHandler
功能,这对于快速失败功能不是必需的,但我认为它非常有用,特别是如果您之前使用过jasmine.getEnv()
并且它与async
/ await
!
You can register custom handler inside of your tests (eg beforeAll
hook) like so:您可以在测试中注册自定义处理程序(例如
beforeAll
钩子),如下所示:
// testEnvironment is globally available (see above NodeEnvironmentFailFast.setup)
testEnvironment.registerTestEventHandler(async (event) => {
if (event.name === "test_fn_failure") {
await takeScreenshot()
}
})
When one test
fails, other test
statements in the same describe
will be skipped.当一个
test
失败时,同一describe
中的其他test
语句将被跳过。 This also works for nested describe
blocks, but the describe
blocks must have different names.这也适用于嵌套的
describe
块,但describe
块必须具有不同的名称。
Executing following test:执行以下测试:
describe("TestJest 3 ", () => {
describe("TestJest 2 ", () => {
describe("TestJest 1", () => {
beforeAll(() => expect(1).toBe(2))
test("1", () => {})
test("1.1", () => {})
test("1.2", () => {})
})
test("2", () => expect(1).toBe(2))
test("2.1", () => {})
test("2.2", () => {})
})
test("3", () => {})
test("3.1", () => expect(1).toBe(2))
test("3.2", () => {})
})
will produce following log:将产生以下日志:
FAIL suites/test-jest.spec.js
TestJest 3
✓ 3
✕ 3.1 (1 ms)
○ skipped 3.2
TestJest 2
✕ 2
○ skipped 2.1
○ skipped 2.2
TestJest 1
○ skipped 1
○ skipped 1.1
○ skipped 1.2
● TestJest 3 › TestJest 2 › TestJest 1 › 1
expect(received).toBe(expected) // Object.is equality
Expected: 2
Received: 1
2 | describe("TestJest 2 ", () => {
3 | describe("TestJest 1", () => {
> 4 | beforeAll(() => expect(1).toBe(2))
| ^
5 | test("1", () => {})
6 | test("1.1", () => {})
7 | test("1.2", () => {})
at suites/test-jest.spec.js:4:33
● TestJest 3 › TestJest 2 › TestJest 1 › 1.1
expect(received).toBe(expected) // Object.is equality
Expected: 2
Received: 1
2 | describe("TestJest 2 ", () => {
3 | describe("TestJest 1", () => {
> 4 | beforeAll(() => expect(1).toBe(2))
| ^
5 | test("1", () => {})
6 | test("1.1", () => {})
7 | test("1.2", () => {})
at suites/test-jest.spec.js:4:33
● TestJest 3 › TestJest 2 › TestJest 1 › 1.2
expect(received).toBe(expected) // Object.is equality
Expected: 2
Received: 1
2 | describe("TestJest 2 ", () => {
3 | describe("TestJest 1", () => {
> 4 | beforeAll(() => expect(1).toBe(2))
| ^
5 | test("1", () => {})
6 | test("1.1", () => {})
7 | test("1.2", () => {})
at suites/test-jest.spec.js:4:33
● TestJest 3 › TestJest 2 › 2
expect(received).toBe(expected) // Object.is equality
Expected: 2
Received: 1
8 | })
9 |
> 10 | test("2", () => expect(1).toBe(2))
| ^
11 | test("2.1", () => {})
12 | test("2.2", () => {})
13 | })
at Object.<anonymous> (suites/test-jest.spec.js:10:31)
● TestJest 3 › 3.1
expect(received).toBe(expected) // Object.is equality
Expected: 2
Received: 1
14 |
15 | test("3", () => {})
> 16 | test("3.1", () => expect(1).toBe(2))
| ^
17 | test("3.2", () => {})
18 | })
19 |
at Object.<anonymous> (suites/test-jest.spec.js:16:31)
Test Suites: 1 failed, 1 total
Tests: 2 failed, 6 skipped, 1 passed, 9 total
Snapshots: 0 total
Time: 0.638 s, estimated 1 s
This was my solution
-- if there are major downsides please let me know, for my purposes it seems to work as intended这是我的
solution
——如果有重大缺点,请告诉我,就我的目的而言,它似乎按预期工作
I only have one top-level describe block, for my purposes I want the entire test file to fail when one test fails我只有一个顶级描述块,出于我的目的,当一个测试失败时,我希望整个测试文件失败
export class FailEarly {
msg: string | undefined;
failed: boolean = false;
jestIt: jest.It;
constructor(jestIt: jest.It) {
this.jestIt = jestIt;
}
test = (name: string, fn: jest.EmptyFunction, timeout?: number) => {
const failEarlyFn = async () => {
if (this.failed) {
throw new Error(`failEarly: ${this.msg}`);
}
try {
await fn();
} catch (error) {
this.msg = name;
this.failed = true;
throw error;
}
};
this.jestIt(name, failEarlyFn, timeout);
};
}
Gives me a context (class attributes) to store global-esq variables给我一个上下文(类属性)来存储 global-esq 变量
const failEarlyTestRunner = new FailEarly(global.it);
const test = failEarlyTestRunner.test;
const it = failEarlyTestRunner.test;
overloads the test
and it
functions with my class' method (thus accessing the class attributes)过载
test
和it
的功能与我的类的方法(因此访问类属性)
describe('my stuff', () => {
it('passes', async () => {
expect(1).toStrictEqual(1);
})
test('it fails', async () => {
expect(1).toStrictEqual(2);
})
it('is skipped', async () => {
expect(1).toStrictEqual(1);
})
})
results in:结果是:
my stuff
✓ can create a sector (2 ms)
✕ it fails (2 ms)
✕ is skipped (1 ms)
● my stuff › it fails
expect(received).toStrictEqual(expected) // deep equality
Expected: 2
Received: 1
> ### | expect(1).toStrictEqual(2);
| ^
### | });
● my stuff › is skipped
failEarly: it fails
69 | const failEarlyFn = async () => {
70 | if (this.failed) {
> 71 | throw new Error(`failEarly: ${this.msg}`);
| ^
72 | }
73 |
74 | try {
Where each skipped test is failed w/ an error indicating the upstream, failing test每个跳过的测试都失败了,并有一个错误指示上游,失败的测试
As others have pointed out -- you've got to run jest with the --runInBand
flag正如其他人指出的那样——你必须使用
--runInBand
标志来运行 jest
Hope this helps someone -- if there are meaningful drawbacks or better ways please comment;希望这对某人有所帮助 - 如果有有意义的缺点或更好的方法,请发表评论; I'm always happy to learn
我总是乐于学习
hack the global.jasmine.currentEnv_.fail works for me.破解 global.jasmine.currentEnv_.fail 对我有用。
describe('Name of the group', () => {
beforeAll(() => {
global.__CASE_FAILED__= false
global.jasmine.currentEnv_.fail = new Proxy(global.jasmine.currentEnv_.fail,{
apply(target, that, args) {
global.__CASE__FAILED__ = true
// you also can record the failed info...
target.apply(that, args)
}
}
)
})
afterAll(async () => {
if(global.__CASE_FAILED__) {
console.log("there are some case failed");
// TODO ...
}
})
it("should xxxx", async () => {
// TODO ...
expect(false).toBe(true)
})
});
I guess that other tests in that suite depend on all previous tests be successful.我猜该套件中的其他测试依赖于所有以前的测试是否成功。 This is a bad practice in unit testing.
这是单元测试中的一种不好的做法。 Try using
beforeEach
and afterEach
to decouple individual test cases within a suite, so that they don't rely on each other.尝试使用
beforeEach
和afterEach
来分离套件中的各个测试用例,这样它们就不会afterEach
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.