Jest / Jasmine: Странный Порядок Исполнения beforeEach () - PullRequest
0 голосов
/ 13 января 2019

Учитывая класс:

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 голосов
/ 13 января 2019

[Этот ответ актуален как для Джеста, так и для Жасмин!]

Ошибка в приведенном выше примере - неправильное понимание того, как работают обратные вызовы вложенности describe, it и установки / разрыва.

(Обратите внимание, что для jest it это просто псевдоним для test)

Представьте, что вы добавили 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:

  1. Код внутри describe обратных вызовов, в конечном счете, запускается первым. Вы можете подумать о том, что работа кода describe состоит в регистрации it / test обратных вызовов и beforeEach обратных вызовов (а также beforeAll afterAll и afterEach обратных вызовов!). На самом деле не должно быть никакого кода внутри ваших describe (кроме объявления var для ссылки), который не был бы вложен в обратный вызов it или beforeEach - поэтому в конечном итоге ваш тест изначально провалился ,

  2. Jest будет регистрировать каждый обратный вызов it / test как «Тест» для запуска и будет гарантировать, что все обратные вызовы beforeAll, beforeEach, afterAll и afterEach бегите соответственно к их вложенности.

Следуя этой методологии с учетом гипотетического дерева (где каждый слой имеет beforeEach), это приводит к следующему порядку:

  1. изначально построил Foo здесь (но не внутри beforeEach)
  2. изначально построил еще 2 Foo здесь (но не внутри beforeEach)
  3. beforeEach 1
  4. beforeEach 2
  5. beforeEach 3
  6. beforeEach 4A
  7. должен иметь идентификатор 1
  8. beforeEach 1
  9. beforeEach 2
  10. beforeEach 3
  11. beforeEach 4B
  12. он должен иметь идентификаторы 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, где мы создаем наши foo1 и foo2 к этому describe, так как это настройка, которую мы хотим выполнить для обоих наших тестов "создания двух Foo objs"!

Теперь все наши тесты пройдены, и полученный журнал этого набора тестов выглядит так:

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

Ссылки

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...