itwaze
itwaze

Reputation: 199

How to properly mock the file reader using Jest?

I'm trying to mock a file reader, and I have a function that accepts a callback, but during the test, the line where the callback is called is not covered. How can this be solved?

function:

const testFunction = (uint8arr, callback) => {
  const bb = new Blob([uint8arr]);
  const f = new FileReader();
  f.onload = function (e) {
    callback(e.target.result);
  };

  f.readAsText(bb);
};

module.exports = { testFunction };

test:

const { testFunction } = require("./index");

class FileReaderMock {
  DONE = FileReader.DONE;
  EMPTY = FileReader.EMPTY;
  LOADING = FileReader.LOADING;
  readyState = 0;
  error = null;
  result = null;
  abort = jest.fn();
  addEventListener = jest.fn();
  dispatchEvent = jest.fn();
  onabort = jest.fn();
  onerror = jest.fn();
  onload = jest.fn();
  onloadend = jest.fn();
  onloadprogress = jest.fn();
  onloadstart = jest.fn();
  onprogress = jest.fn();
  readAsArrayBuffer = jest.fn();
  readAsBinaryString = jest.fn();
  readAsDataURL = jest.fn();
  readAsText = jest.fn();
  removeEventListener = jest.fn();
}

const mockCallback = jest.fn();

describe("testFunction", () => {
  const fileReader = new FileReaderMock();

  jest.spyOn(window, "FileReader").mockImplementation(() => fileReader);

  it("should be called mockCallback and readAsText with the blob value", () => {
    const uintArray = new Uint8Array();
    testFunction(uintArray, mockCallback);

    // expect(mockCallback).toHaveBeenCalled();
    // expect(fileReader.readAsText).toHaveBeenCalledWith(new Blob([uintArray]));
  });
});

codesandbox: https://codesandbox.io/s/jest-test-forked-ecejb?file=/index.test.js

Upvotes: 2

Views: 4144

Answers (1)

Lin Du
Lin Du

Reputation: 102247

You can use the getter and setter of the Object.defineProperty() method to intercept the assignment operation of the onload function, so you can get the onload function to test it as usual.

E.g.

index.js:

const testFunction = (uint8arr, callback) => {
  const bb = new Blob([uint8arr]);
  const f = new FileReader();
  f.onload = function (e) {
    callback(e.target.result);
  };

  f.readAsText(bb);
};

module.exports = { testFunction };

index.test.js:

const { testFunction } = require('./index');

describe('testFunction', () => {
  it('should be called mockCallback and readAsText with the blob value', () => {
    const mockCallback = jest.fn();
    const fileReader = {
      readAsText: jest.fn(),
    };
    let onloadRef;
    Object.defineProperty(fileReader, 'onload', {
      get() {
        return this._onload;
      },
      set(onload) {
        onloadRef = onload;
        this._onload = onload;
      },
    });
    jest.spyOn(window, 'FileReader').mockImplementation(() => fileReader);
    const uintArray = new Uint8Array();
    testFunction(uintArray, mockCallback);

    // onload test
    const event = { target: { result: 'teresa teng' } };
    onloadRef(event);
    expect(mockCallback).toBeCalledWith('teresa teng');
    expect(fileReader.readAsText).toBeCalledWith(new Blob([uintArray]));
  });
});

unit test result:

 PASS  examples/66964346/index.test.js (7.479 s)
  testFunction
    ✓ should be called mockCallback and readAsText with the blob value (4 ms)

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------|---------|----------|---------|---------|-------------------
All files |     100 |      100 |     100 |     100 |                   
 index.js |     100 |      100 |     100 |     100 |                   
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        8.575 s, estimated 9 s

Upvotes: 1

Related Questions