如何在 Jest 的 beforeEach 中监视实例化的 class?

How to spy a class instantiated within beforeEach in Jest?

我有一个 class 实例应该在每次测试之间清除,所以我这样做:

class Foo {
  constructor() {}
  public bar() {
    return console.log("bar")
  }
}

describe('tests on class Foo', () => {
  let foo: Foo
  beforeEach(() => {
    foo = new Foo()
    jest.clearAllMocks()
  })

  const spyOnBar = jest.spyOn(foo, "bar")

  test("should call bar", () => {
    foo.bar()
    expect(spyOnBar).toHaveBeenCalled()      
  })
})

但是 spyOnBar 一直给我错误 Cannot spyOn on a primitive value; undefined given

有没有一种方法可以监视方法栏而无需在每个测试中声明间谍?

参见 Order of execution of describe and test blocks

让我们看看执行的顺序。

describe('tests on class Foo', () => {
  beforeEach(() => {
    console.log('beforeEach');
  });

  console.log('describe');

  test('should call bar', () => {
    console.log('test');
  });
});

日志:

 PASS  Whosebug/72035574/foo.test.ts (10.376 s)
  tests on class Foo
    ✓ should call bar (2 ms)

  console.log
    describe

      at Suite.<anonymous> (Whosebug/72035574/foo.test.ts:16:11)

  console.log
    beforeEach

      at Object.<anonymous> (Whosebug/72035574/foo.test.ts:13:13)

  console.log
    test

      at Object.<anonymous> (Whosebug/72035574/foo.test.ts:20:13)

当您在 describe 块中调用 jest.spyOn(foo, 'bar') 方法时,尚未创建 foo 实例。这就是您收到错误的原因。

选项 1.describe 块中创建 foo 实例并在其方法上添加监视。由于所有测试用例共享一个 foo 实例和间谍,我们需要在 beforeEach 钩子中调用 jest.clearAllMocks() 来清除 mock.callsmock.instances 属性,以便每个测试案件将使用明确的间谍对象。 toBeCalledTimes(1) 会起作用。

class Foo {
  constructor() {}
  public bar() {
    return console.log('bar');
  }
}

describe('tests on class Foo', () => {
  const foo = new Foo();
  const spyOnBar = jest.spyOn(foo, 'bar');
  beforeEach(() => {
    jest.clearAllMocks();
  });

  test('should call bar', () => {
    foo.bar();
    expect(spyOnBar).toBeCalledTimes(1);
  });

  test('should call bar - 2', () => {
    foo.bar();
    expect(spyOnBar).toBeCalledTimes(1);
  });
});

选项 2. 创建 foo 并监视 beforeEach 挂钩,以便每个测试用例都使用一个新的。没有必要使用 jest.clearAllMocks().

describe('tests on class Foo', () => {
  let spyOnBar: jest.SpyInstance;
  let foo: InstanceType<typeof Foo>;
  beforeEach(() => {
    foo = new Foo();
    spyOnBar = jest.spyOn(foo, 'bar');
  });

  test('should call bar', () => {
    foo.bar();
    expect(spyOnBar).toBeCalledTimes(1);
  });

  test('should call bar - 2', () => {
    foo.bar();
    expect(spyOnBar).toBeCalledTimes(1);
  });
});