【问题标题】:Jest/Jasmine: Weird Execution Order Of beforeEach()Jest/Jasmine:beforeEach() 的奇怪执行顺序
【发布时间】:2019-01-16 18:49:37
【问题描述】:

给定班级:

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 代码直到所有测试都完成后才真正运行。

【问题讨论】:

    标签: typescript unit-testing testing jasmine jestjs


    【解决方案1】:

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

    上面示例中的错误是对嵌套describeit 和setup/teardown 回调如何工作的误解。

    (请注意,开玩笑的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 回调的运行顺序为:

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

    2. Jest 会将每个 it/test 回调注册为要运行的“测试”,并确保所有 beforeAllbeforeEachafterAllafterEach 回调适当地运行到它们的嵌套中。

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

    1. 最初在此处构造了一个 Foo(但不在 beforeEach 内部)
    2. 最初在此处再构建 2 个 Foo(但不在 beforeEach 内部)
    3. beforeEach 1
    4. 每 2 次之前
    5. 每 3 次之前
    6. 每4A之前
    7. 它的 id 应该是 1
    8. beforeEach 1
    9. 每 2 次之前
    10. 每 3 次之前
    11. 每 4B 之前
    12. 它的 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);
          });
        });
      });
    });
    

    请注意:

    • 我们将私有静态属性的重置移动到测试套件顶层的 beforeEach - 因为如果 Foo 有其他方法或逻辑来重置您可以运行的每个 Foo 测试,这可能是一个好主意测试。
    • 我们在“创建两个 Foo 对象”describe 中添加了另一个测试
    • 我们添加了一个beforeEach,我们在其中创建foo1foo2describe,因为这是我们想要为我们的“创建两个Foo objs”测试进行的设置!李>

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

    1
    2
    3
    4A
    1
    2
    3
    4B
    4B'
    1
    2
    3
    4B
    4B''
    

    参考文献

    【讨论】:

      猜你喜欢
      • 2011-10-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-06
      • 2021-09-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多