笑话/茉莉花:beforeEach()的奇怪执行顺序

时间:2019-01-13 07:13:02

标签: typescript unit-testing testing jasmine jestjs

上课:

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代码实际上并未运行,直到所有测试都已完成。

1 个答案:

答案 0 :(得分:7)

[此答案与Jest和Jasmine都有关系!]

以上示例中的错误是对嵌套describeit和设置/拆卸回调的工作方式的误解。

(请注意,开玩笑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

运行describebeforeEachit回调的顺序是:

    describe回调内部的
  1. 代码最终将首先运行。您可以想到describe代码的工作是注册it / test回调和beforeEach回调(以及beforeAll afterAll和{ {1}}回调!)。在afterEach的内部(除了声明var的引用之外),实际上应该没有嵌套在describeit回调中的任何代码-这最终是为什么您的测试最初失败了。

  2. Jest将每个beforeEach / it回调注册为要运行的“测试”,并确保所有testbeforeAll,{{ 1}}和beforeEach回调在嵌套中正确运行。

在给定假设树(其中每个层都有一个afterAll)的情况下,遵循此方法,结果如下:

  1. 最初在这里构造了一个Foo(但不是在beforeEach内部)
  2. 最初在这里又建了2个Foo(但不是在beforeEach内部)
  3. 之前1
  4. 之前每2
  5. 每次3次
  6. 每4A之前
  7. 它的ID应该为1
  8. 之前1
  9. 之前每2
  10. 每次3次
  11. 每4B之前
  12. 它的ID应该为1和2

解释日志的原始顺序。

为了更好地测试,请使用以下测试代码:

afterEach

请注意:

  • 我们将私有静态属性的重置移到了测试服顶层的beforeEach上-因为重置为每个Foo测试都可以运行,如果Foo有其他方法或测试逻辑。
  • 我们在“创建两个Foo objs” 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); }); }); }); });
  • 中添加了另一个测试
  • 我们在beforeEach上添加了describebeforeEach,并在其中创建了foo1foo2,因为这是我们为创建“两个Foo objs”测试!

现在,我们所有的测试都通过了,该测试套件的结果日志为:

describe

参考