为什么我不能模拟 `crypto.createHash`?

Why can't I mock `crypto.createHash`?

我正在尝试测试一个使用 Node 的 crypto.createHash 函数的函数。但是,当我尝试使用 jest.mock('crypto', () => ({ createHash: … })) 模拟它时,模拟似乎不起作用。

被测单元同时使用 Hash#updateHash#digest,所以我试图将它们添加到 createHash 模拟中,但是当单元运行时,createHash returns undefined.

src/hash.ts

import { createHash } from 'crypto';

export function hash(input: string | Uint8Array): Uint8Array {
  return new Uint8Array(createHash('sha512').update(input).digest().buffer);
}

src/__test__/hash.test.ts

import { createHash } from 'crypto';

import { hash } from '../hash-node.js';

const mockDigest = jest.fn();

jest.mock('crypto', () => ({
  createHash: jest.fn().mockReturnValue({
    update: jest.fn().mockReturnThis(),
    digest: mockDigest,
  }),
}));

describe('hash', () => {
  it('uses SHA512', () => {
    hash('foo');

    expect(createHash).toHaveBeenCalledWith(
      expect.stringMatching(/sha512/i),
    );
  });
});

输出

    TypeError: Cannot read properties of undefined (reading 'update')

      3 |
      4 | export function hash(input: string | Uint8Array): Uint8Array {
    > 5 |   return new Uint8Array(createHash('sha512').update(input).digest().buffer);
        |                         ^
      6 | }
      7 |

      at hash (src/hash.ts:4:25)
      at Object.<anonymous> (src/__test__/hash.test.ts:16:5)

如您所见,createHash 显然返回了 undefined,就好像 .mockReturnValue 没有被调用一样。

我做错了什么?我怎样才能让这个模块 mock 工作?

我回到这个问题上并弄明白了;这是配置 Jest 以在测试之间重置模拟的结果。一旦我将 createHash 上的 mockReturnValue 调用移动到 beforeEach 块内,它就会按预期开始工作。

import { type Hash, createHash } from 'crypto';

import { hash } from '../hash.js';

const mockDigest = jest.fn();

jest.mock('crypto', () => ({
  createHash: jest.fn(),
}));

describe('hash', () => {
  beforeEach(() => {
    (createHash as jest.MockedFunction<typeof createHash>).mockReturnValue({
      update: jest.fn().mockReturnThis(),
      digest: mockDigest,
    } as unknown as Hash);
  });

  it('uses SHA512', () => {
    hash('foo');

    expect(createHash).toHaveBeenCalledWith(
      expect.stringMatching(/sha512/i),
    );
  });
});