给定类:
export class Foo {
private static _count = 0;
id: number;
constructor() {
this.id = ++Foo._count; // starts with 1
}
}
和测试套件:
describe('Foo Class', () => {
describe('constructor', () => {
const fooClassRef = Foo as any; // to pass typechecking
beforeEach(() => {
console.log(`start of beforeEach: ${fooClassRef._count}`);
fooClassRef._count = 0;
console.log(`end of beforeEach: ${fooClassRef._count}`);
});
describe('creating one Foo obj', () => {
console.log(fooClassRef._count);
const foo = new Foo();
it('should have an id of 1', () => {
expect(foo.id).toBe(1);
});
});
describe('creating two Foo objs', () => {
console.log(fooClassRef._count);
const foo1 = new Foo();
const foo2 = new Foo();
it('should have ids of 1 and 2', () => {
expect(foo1.id).toBe(1);
expect(foo2.id).toBe(2);
});
});
});
});
第二次测试失败:
expect(received).toBe(expected) // Object.is equality
Expected: 1
Received: 2
| const foo2 = new Foo();
| it('should have ids of 1 and 2', () => {
> | expect(foo1.id).toBe(1);
| ^
| expect(foo2.id).toBe(2);
| });
| });
生成的日志是:
0
1
start of beforeEach(): 3
end of beforeEach(): 0
start of beforeEach(): 0
end of beforeEach(): 0
似乎 beforeEach
代码直到所有测试都完成后才真正运行。
最佳答案
[这个答案与 Jest 和 Jasmine 都相关!]
上面示例中的错误是对嵌套 describe
、it
和设置/拆卸回调的工作方式的误解。
(注意开 Jest it
只是test
的别名)
想象一下,如果您在上面的每个 describe/it 调用之前添加一个 beforeEach
,那么嵌套看起来像这样:
┬─ beforeEach 1
└┬ describe Foo Class
├─ beforeEach 2
└┬ describe constructor
├── beforeEach 3
├─┬ describe creating one Foo obj
│ ├─ * - originally constructed a Foo here (but not inside of a beforeEach)
│ ├─ beforeEach 4A
│ └─ it should have an id of 1
└─┬ describe creating two Foo objs
├─ * - originally constructed 2 more Foo's here (but not inside of a beforeEach)
├─ beforeEach 4B
└─ it should have ids of 1 and 2
describe
、beforeEach
和 it
回调的运行顺序是:
describe
回调中的代码最终首先运行。您可以认为describe
代码的工作是注册it
/test
回调和beforeEach
回调(以及beforeAll
afterAll
和afterEach
回调!)。您的describe
中真的不应该有任何代码(除了声明要引用的 var 之外)没有嵌套在it
或beforeEach 中
回调 - 这就是您的测试最初失败的最终原因。Jest 会将每个
it
/test
回调注册为要运行的“测试”,并确保所有beforeAll
、beforeEach
、afterAll
和afterEach
回调会根据它们的嵌套适当运行。
在给定假设树(其中每一层都有一个 beforeEach
)的情况下,按照这种方法,结果如下:
- 最初在这里构建了一个 Foo(但不在 beforeEach 内部)
- 最初在这里构造了另外 2 个 Foo(但不在 beforeEach 内部)
- 在每个 1 之前
- 在每个 2 之前
- 在每个 3 之前
- 在每个 4A 之前
- 它的 id 应该是 1
- 在每个 1 之前
- 在每个 2 之前
- 在每个 3 之前
- 在每个 4B 之前
- 它的 ID 应该是 1 和 2
这解释了日志的原始顺序。
为了更好地测试这一点,让我们使用以下测试代码:
beforeEach(() => {
console.log(1);
const fooClassRef = Foo as any;
fooClassRef._count = 0;
});
describe('Foo Class', () => {
beforeEach(() => console.log(2));
describe('constructor', () => {
beforeEach(() => console.log(3));
describe('creating one Foo obj', () => {
beforeEach(() => console.log('4A'));
test('should have an id of 1', () => {
console.log('A');
const foo = new Foo();
expect(foo.id).toBe(1);
});
});
describe('creating two Foo objs', () => {
let foo1;
let foo2;
beforeEach(() => {
console.log('4B');
foo1 = new Foo();
foo2 = new Foo();
});
it('should have ids of 1 and 2', () => {
console.log(`4B'`);
expect(foo1.id).toBe(1);
expect(foo2.id).toBe(2);
});
it('should originally start with ids of 1 and 2, but they could be changed', () => {
console.log(`4B''`);
expect(foo1.id).toBe(1);
expect(foo2.id).toBe(2);
foo2.id = 47;
expect(foo1.id).toBe(1);
expect(foo2.id).toBe(47);
});
});
});
});
注意:
- 我们将私有(private)静态属性的重置移动到测试套件顶层的
beforeEach
- 因为如果 Foo 有,为您可以运行的每个 Foo 测试重置它可能是个好主意其他要测试的方法或逻辑。 - 我们在“创建两个 Foo 对象”
describe
中添加了另一个测试
- 我们添加了一个
beforeEach
,我们在其中创建我们的foo1
和foo2
到那个describe
因为这是设置我们要为我们的两个“创建两个 Foo objs”测试做!
现在,我们所有的测试都通过了,这个测试套件的结果日志是:
1
2
3
4A
1
2
3
4B
4B'
1
2
3
4B
4B''
引用
关于typescript - Jest/ Jasmine : Weird Execution Order Of beforeEach(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54166833/