Jest/Jasmine:beforeEach的奇怪执行顺序()

Zac*_*ose 6 testing unit-testing jasmine typescript jestjs

鉴于课程:

export class Foo {
  private static _count = 0;
  id: number;
  constructor() {
    this.id = ++Foo._count; // starts with 1
  }
}
Run Code Online (Sandbox Code Playgroud)

和测试套件:

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);
      });
    });
  });
});
Run Code Online (Sandbox Code Playgroud)

第二次测试失败:

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);
       |       });
       |     });
Run Code Online (Sandbox Code Playgroud)

生成的日志是:

0
1
start of beforeEach(): 3
end of beforeEach(): 0
start of beforeEach(): 0
end of beforeEach(): 0
Run Code Online (Sandbox Code Playgroud)

beforeEach所有测试都已完成之前,似乎代码实际上并未运行.

Zac*_*ose 10

[这个答案与Jest和Jasmine都有关!]

上面示例中的错误是对嵌套describe,it设置/拆卸回调如何工作的误解.

(注意,对于jest it只是一个别名test)

想象一下,如果你beforeEach在上面的每个describe/it调用之前添加了一个,那么嵌套看起来像这样:

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

该方法的顺序describe,beforeEach以及it回调将运行是:

  1. describe回调中的代码最终首先运行.你可以认为的describe代码的工作进行注册it/ test回调和beforeEach回调(以及beforeAll afterAllafterEach回调呢!).确实不应该在你describe的内部有任何代码(除了声明var引用之外)没有嵌套在一个itbeforeEach回调中 - 这最终是你的测试最初失败的原因.

  2. 开玩笑将每个注册it/ test回调"试验"来运行,并确保所有beforeAll,beforeEach,afterAll,和afterEach回调适当地运行到他们的嵌套.

根据这种方法给出假设树(每个层都有一个beforeEach),这将产生以下顺序:

  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. 它应该有1和2的ID

这解释了原木的原始顺序.

为了更好地测试这个,我们改为使用以下测试代码:

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);
      });
    });
  });
});
Run Code Online (Sandbox Code Playgroud)

注意:

  • 我们将私有静态属性的重置移到了beforeEach测试套件的顶层 - 因为如果Foo有其他方法或逻辑需要测试,那么重置那个你可以运行的每个Foo测试都可能是个好主意.
  • 我们在"创建两个Foo objs"中添加了另一个测试 describe
  • 我们增加了一个beforeEach,我们创造我们foo1,并foo2describe,因为这是建立,我们将要为我们的"创建两个富OBJ文件"测试做!

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

1
2
3
4A
1
2
3
4B
4B'
1
2
3
4B
4B''
Run Code Online (Sandbox Code Playgroud)

参考