jest で Blob をモックにしてテストするには

jest で Blob をモックにしてテストしたかったのですが、Node.js に Blob は定義されていませんでした。
未定義なので定義することで対応してみました。

作成したコード

まずはテスト対象のコードです。前回のコードを typescript で別ファイルにしました。

export default function downloadText(fileName: string, text: string) {
    const blob = new Blob([text], { type: 'text/plain' });
    const aTag = document.createElement('a');
    aTag.href = URL.createObjectURL(blob);
    aTag.target = '_blank';
    aTag.download = fileName;
    aTag.click();
    URL.revokeObjectURL(aTag.href);
  }

そして、テストコードは以下です。
ここでは意図した値で Blob を作成したか?を確認し、Aタグについては割愛しています。

import downloadText from './downloadText';

class BlobMock {
  constructor(
    content: Array<ArrayBuffer | ArrayBufferView | Blob | String> | undefined,
    options: BlobPropertyBag | undefined,
  ) {
    this.content = content;
    this.options = options;
  }
  public content: Array<ArrayBuffer | ArrayBufferView | Blob | String> | undefined;
  public options: BlobPropertyBag | undefined;
}

test('downloadText', () => {
  (global as any).Blob = BlobMock;
  const backup1 = window.URL.createObjectURL;
  const backup2 = window.URL.revokeObjectURL;
  window.URL.createObjectURL = jest.fn();
  window.URL.revokeObjectURL = jest.fn();

  downloadText('sample.txt', '一富士二鷹三茄子');
  expect(window.URL.createObjectURL).toBeCalledWith({
    content: ['一富士二鷹三茄子'],
    options: { type: 'text/plain' },
  });

  window.URL.createObjectURL = backup1;
  window.URL.revokeObjectURL = backup2;
});

悩んだポイント1:どう Blob を定義するか

未定義だからといって、

  global.Blob = BlobMock;

にすると、“A file-like object of immutable, raw data. Blobs represent data that isn’t necessarily in a JavaScript-native format. The File interface is based on Blob, inheriting blob functionality and expanding it to support files on the user’s system.” と言われてしまうので、

  (global as any).Blob = BlobMock;

の通り、global を一旦 any にして Blob を設定しています。

悩んだポイント2:URL.createObjectURL をモックにできない

モックにできないというか、

  jest.spyOn(window.URL, 'createObjectURL').mockImplementation(jest.fn());

だと、

Cannot spy the createObjectURL property because it is not a function; undefined given instead

となってしまうので、関数を定義することで乗り越えました。

コメント