PolarisRouge
PolarisRouge

Reputation: 435

Jest unit test on('error') of createWriteStream

I am using Nestjs and have written the function below which recieves a file from a post request and saves it in a folder in my project. My issue is I'm not sure how to test the on('error') branch.

function to unit test.

  saveFile({ createReadStream, filename }: FileUpload): Promise<boolean> {
    return new Promise(async (resolve, reject) => {
      createReadStream().pipe(
        createWriteStream(join(process.cwd(), `apps/mull-api/uploads/${filename}`))
          .on('finish', () => resolve(true))
          .on('error', () => {
            console.log(createReadStream);
            reject(false);
          })
      );
    });
  }

How I am testing the on('finish') branch

  it('should save file', async () => {
    const returnedFile = await service.saveFile(mockFile);
    expect(returnedFile).toBe(true);
  });

This is what my mockFile looks like. I tried providing a mockFile with empty name and it errors out.

export const mockFile: FileUpload = {
  filename: 'zoro',
  mimetype: 'image/jpeg',
  encoding: '7bit',
  createReadStream(): ReadStream {
    return fs.createReadStream(join(process.cwd(), `apps/mull-api/uploads/mock-upload/zoro`));
  },
};

Upvotes: 7

Views: 6785

Answers (1)

Lin Du
Lin Du

Reputation: 102497

We can mock createWriteStream, .on('finish') and .on('error') methods using mockImplementation(). And trigger these two events in the mock implementation function by ourself.

The 'finish' event handler in the mock implementation function is () => resolve(true); The 'error' event handler in the mock implementation function is () => reject(false);

See Mock Implementations, and below example:

const myMockFn = jest.fn(cb => cb(null, true));

myMockFn((err, val) => console.log(val));
// > true

index.ts:

import { createWriteStream, ReadStream } from 'fs';
import { join } from 'path';

export interface FileUpload {
  filename: string;
  mimetype: string;
  encoding: string;
  createReadStream(): ReadStream;
}

export class FileService {
  public saveFile({ createReadStream, filename }: FileUpload): Promise<boolean> {
    return new Promise(async (resolve, reject) => {
      createReadStream().pipe(
        createWriteStream(join(process.cwd(), `apps/mull-api/uploads/${filename}`))
          .on('finish', () => resolve(true))
          .on('error', () => {
            reject(false);
          }),
      );
    });
  }
}

index.test.ts:

import { FileService, FileUpload } from './';
import { createWriteStream, WriteStream } from 'fs';
import { mocked } from 'ts-jest/utils';

jest.mock('fs');

describe('64485251', () => {
  afterAll(() => {
    jest.resetAllMocks();
    jest.clearAllMocks();
  });
  it('should save file', async () => {
    const mockReadStream = { pipe: jest.fn() };
    const mockFile: FileUpload = {
      filename: 'zoro',
      mimetype: 'image/jpeg',
      encoding: '7bit',
      createReadStream: jest.fn().mockReturnValueOnce(mockReadStream),
    };
    const mockWriteStream = {
      on: jest.fn().mockImplementation(function(this, event, handler) {
        if (event === 'finish') {
          handler();
        }
        return this;
      }),
    };
    mocked(createWriteStream).mockReturnValueOnce((mockWriteStream as unknown) as WriteStream);
    const service = new FileService();
    const actual = await service.saveFile(mockFile);
    expect(mockFile.createReadStream).toBeCalledTimes(1);
    expect(mockReadStream.pipe).toBeCalledTimes(1);
    expect(createWriteStream).toBeCalledWith(expect.stringContaining('apps/mull-api/uploads/zoro'));
    expect(mockWriteStream.on).toBeCalledWith('finish', expect.any(Function));
    expect(mockWriteStream.on).toBeCalledWith('error', expect.any(Function));
    expect(actual).toBeTruthy();
  });

  it('should handle error if save file failed', async () => {
    const mockReadStream = { pipe: jest.fn() };
    const mockFile: FileUpload = {
      filename: 'zoro',
      mimetype: 'image/jpeg',
      encoding: '7bit',
      createReadStream: jest.fn().mockReturnValueOnce(mockReadStream),
    };
    const mockWriteStream = {
      on: jest.fn().mockImplementation(function(this, event, handler) {
        if (event === 'error') {
          handler();
        }
        return this;
      }),
    };
    mocked(createWriteStream).mockReturnValueOnce((mockWriteStream as unknown) as WriteStream);
    const service = new FileService();
    await expect(service.saveFile(mockFile)).rejects.toEqual(false);
    expect(mockFile.createReadStream).toBeCalledTimes(1);
    expect(mockReadStream.pipe).toBeCalledTimes(1);
    expect(createWriteStream).toBeCalledWith(expect.stringContaining('apps/mull-api/uploads/zoro'));
    expect(mockWriteStream.on).toBeCalledWith('finish', expect.any(Function));
    expect(mockWriteStream.on).toBeCalledWith('error', expect.any(Function));
  });
});

unit test result:

 PASS  src/stackoverflow/64485251/index.test.ts (10.201s)
  64485251
    ✓ should save file (6ms)
    ✓ should handle error if save file failed (3ms)

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

Upvotes: 2

Related Questions