Jest - 了解 describe() 和 it() 的执行顺序

san*_*a-p 3 javascript unit-testing jestjs

我想了解为什么所有块describe()都在it()每个describe().

这里有一个CodeSandbox例如,每次使用日志describeit块。

describe("sum A", () => {
  window.innerWidth = 100;
  console.log("describe A", window.innerWidth);

  beforeAll(() => {
    window.innerWidth = 105;
    console.log("describe A before", window.innerWidth);
  });

  it("1. should add 1 and 2", () => {
    console.log("it A1", window.innerWidth);
    expect(1 + 2).toBe(3);
  });
});

describe("sum B", () => {
  console.log("describe B", window.innerWidth, "why does it run before it A1?");

  beforeAll(() => {
    window.innerWidth = 205;
    console.log("describe B before", window.innerWidth);
  });

  it("1. should add 1 and 2", () => {
    console.log("it B1", window.innerWidth);
    expect(1 + 2).toBe(3);
  });
});
Run Code Online (Sandbox Code Playgroud)

您会注意到第二个 describe 块中的日志it()在第一个 describe 块内部之前运行。

为什么会这样?我们是否应该避免在代码的那部分中做一些事情,而beforeAll()在需要在那个描述块中共享和范围数据时更喜欢使用?

Bri*_*ams 12

Jest运行时,它查找所有的测试文件,并运行每一个。

所提供的环境中每个测试文件运行时Jest,包括全局喜欢describeitbeforeAll,等。所有这些全局的具有限定其行为的回调参数。

当测试文件运行时,顶级代码会运行……包括任何顶级describe调用。

当 adescribe运行时,它注册一个测试套件,然后立即调用它的回调

这比不同itbeforeAllbeforeEach等在那里的回调记录,但不会立即调用

这意味着所有describe回调函数都按照它们在测试文件中出现的顺序调用深度优先,如以下简单示例所示:

describe('1', () => {
  console.log('1');
  describe('2', () => { console.log('2'); });
  describe('3', () => {
    console.log('3');
    describe('4', () => { console.log('4'); })
    describe('5', () => { console.log('5'); })
  })
  describe('6', () => { console.log('6'); })
})
describe('7', () => {
  console.log('7');
  it('(since there has to be at least one test)', () => { });
})
Run Code Online (Sandbox Code Playgroud)

...哪些日志1-7秩序。

这个初始的所有的运行describe的回调被称为收集阶段,在此期间测试套件的定义,所有的回调任何beforeAllbeforeEachittest,等等被收集。

收集阶段完成后,Jest...

按照在收集阶段遇到的顺序连续运行所有测试,等待每个测试完成并在继续之前进行整理。

对于每个测试(使用全局it或函数注册的每个回调函数testJest 将任何之前的回调、测试回调本身和任何之后的回调链接在一起,并按顺序运行结果函数。


我们是否应该避免在代码的那部分中做一些事情,而beforeAll()在需要在那个描述块中共享和范围数据时更喜欢使用?

对于不共享的简单内容,可以将其放在describe

describe('val', () => {
  const val = '1';

  it('should be 1', () => {
    expect(val).toBe('1');  // Success!
  });
});
Run Code Online (Sandbox Code Playgroud)

...但是中的代码describe可能会导致共享数据出现问题:

describe('val', () => {
  let val;

  describe('1', () => {
    val = '1';
    it('should be 1', () => {
      expect(val).toBe('1');  // FAIL! (val gets set to 2 in the second describe)
    })
  })

  describe('2', () => {
    val = '2';
    it('should be 2', () => {
      expect(val).toBe('2');  // Success!
    })
  })

});
Run Code Online (Sandbox Code Playgroud)

...可以通过使用before调用来修复:

describe('val', () => {
  let val;

  describe('1', () => {
    beforeEach(() => {
      val = '1';
    });
    it('should be 1', () => {
      expect(val).toBe('1');  // Success!
    })
  })

  describe('2', () => {
    beforeEach(() => {
      val = '2';
    });
    it('should be 2', () => {
      expect(val).toBe('2');  // Success!
    })
  })

});
Run Code Online (Sandbox Code Playgroud)

...或简单地将数据范围限定为describe

describe('val', () => {

  describe('1', () => {
    const val = '1';
    it('should be 1', () => {
      expect(val).toBe('1');  // Success!
    })
  })

  describe('2', () => {
    const val = '2';
    it('should be 2', () => {
      expect(val).toBe('2');  // Success!
    })
  })

});
Run Code Online (Sandbox Code Playgroud)

在您的示例中,您使用的window.innerWidth是共享全局before变量,因此您将需要使用函数,因为它不能限定为describe.


另请注意,您不能从 a 返回任何内容,describe因此如果您的测试需要任何异步设置,那么您将需要使用一个before函数,您可以在其中返回PromiseforJest以等待:

const somethingAsync = () => Promise.resolve('1');

describe('val', () => {
  let val;

  beforeAll(async () => {
    val = await somethingAsync();
  });

  it('should be 1', () => {
    expect(val).toBe('1');  // Success!
  });
});
Run Code Online (Sandbox Code Playgroud)