Carmen
Carmen

Reputation: 35

How can I test the Filereader.onload callback function in Angular/Jasmine?

In the ngOnInit of my component I am defining the Filereader.onload callback function. I found several solutions that say to mock the filereader and the onload but I think this defeats my purpose to test if the callback is correctly executed when onload is being called. So I tried to mock the onload Event and then test if the functions inside the callback get called.

it("should import data on filereader onload event", () => {
 const fileReader = new FileReader();
      let loadEvent: ProgressEvent<FileReader>;
     
      spyOn(fileReader, "onload").and.callThrough();

      spyOn(mockServiceOne, "convertData").and.returnValue(
        mockData
      );
      spyOn(mockServiceTwo, "importData").and.callThrough();

      fileReader.onload(loadEvent);
      fixture.detectChanges();

      expect(fileReader.result).toBeDefined();

      expect(mockServiceOne.convertData).toHaveBeenCalled();
      expect(mockServiceTwo.importData).toHaveBeenCalledTimes(1);
    });
  });

The function I am trying to test is this:

this.fileReader.onload = (event) => {
      const jsonData = JSON.parse(event.target.result.toString());
      const data = this.serviceOne.convertData(
        jsonData
      );
      this.serviceTwo.importData(data);
    };

I mocked the two services and methods inside them. But in the test spec they never get called. However the first assertion that the result of the onload event (event.target.result) needs to be defined seems to check true.

Maybe there is a problem with the first line in the function that converts the data to json, since I am not actually giving the function a real file. However when I try to give anything other than the mocked ProgressEvent it gives an error.

Please help me with testing the onload callback. Is it correct that I can't mock the filereader onload for this?

Upvotes: 2

Views: 2982

Answers (1)

David Sosa
David Sosa

Reputation: 56

You don't have to mock the FileReader. I faced this same issue a few days ago. A friend helped me create a workaround by describing the asynchronous nature of the code.

Wrap your code in a Promise and call its resolve() callback passing the result of the FileReader.

// you would load your file here and return the result in a Promise
readFileMethod(file: File): Promise<string> {
      return new Promise<string>((resolve) => {
            const fileReader = new FileReader();
            fileReader.onload = (event) => {
                  const jsonData = JSON.parse(event.target.result.toString());
                  const data = this.serviceOne.convertData(jsonData);
                  resolve(data);
            }
            fileReader.readAsText(); // I suppose this is what you called
      });
}

Then, your onInit would look something like this:

ngOnInit(): void {
    let file; // init your file however you do
    this.readFileMethod(file).then((data) => {
        this.serviceTwo.importData(data);
    });
}

To test the new method, use waitForAsync and expect() inside the success callback:

it('should do whatever with the file', waitForAsync(() => {
    component.readFileMethod(file).then((data) => {
        expect(); // your test here
    });
}));

To test your onInit, use fakeAsync() and tick():

it('should do whatever with the file', fakeAsync(() => {
    spyOn(mockServiceOne, "convertData").and.returnValue(
        mockData
    );
    spyOn(mockServiceTwo, "importData").and.callThrough();

    fixture.detectChanges();
    tick();
    expect(mockServiceOne.convertData).toHaveBeenCalled();
    expect(mockServiceTwo.importData).toHaveBeenCalledTimes(1);
}));

Upvotes: 4

Related Questions