Search code examples
unit-testingjestjsnestjsmulter

Mock file upload in unit test controller NestJs


I have a method in nest js controller which takes two arguments looks like below :

@UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(MemberType.OWNER)
  @Post('')
  @UseInterceptors(
    FilesInterceptor('images', 4, {
      storage: fileStorageProductImage,
      fileFilter: fileFilter,
      limits: fileSizeLimit,
    }),
  )
  @UseInterceptors(new MulterInterceptor('images'))
  public async addProduct(
    @Req()
    req: Request,
    @UploadedFiles() files: Express.Multer.File[],
  ) {
  
  //code
  //
  }

And here is my unit test

describe('Unit testing path 1', () => {
      const MemberServiceProvider = {
        provide: MemberService,
        useClass: MemberServiceStub,
      };
      beforeEach(async () => {
        const module: TestingModule = await Test.createTestingModule({
          controllers: [ProductsController],
          providers: [MemberServiceProvider],
        }).compile();
        controller = module.get<ProductsController>(ProductsController);
      });
      //
      const result = controller.addProduct(..., ...);
    });

I currently dont know what to pass into that addProduct method since i dont know how to mock request object and Express.Multer.File[]. Thanks in advance


Solution

  • For anyone who is still looking for the answer. I just figured it how to solve this. First thing first we need to define a method that can be used to read files from our local machine via stream.

    const fileToBuffer = (filename) => {
      const readStream = fs.createReadStream(filename);
      const chunks = [];
      return new Promise((resolve, reject) => {
        // Handle any errors while reading
        readStream.on('error', (err) => {
          // handle error
    
          // File could not be read
          reject(err);
        });
    
        // Listen for data
        readStream.on('data', (chunk) => {
          chunks.push(chunk);
        });
    
        // File is done being read
        readStream.on('close', () => {
          // Create a buffer of the image from the stream
          resolve(Buffer.concat(chunks));
        });
      });
    };

    Then, we call that method inside our unit test

    const imageBuffer = (await fileToBuffer(
              __dirname + 'path-to-file',
            )) as Buffer;

    In our controller, the uploaded files has Express.Multer.File[] types so we have to make variables that has the same type as above.

    const imageFiles: Express.Multer.File[] = [];
    const myReadableStreamBuffer = new streamBuffers.ReadableStreamBuffer({
          frequency: 10, // in milliseconds.
          chunkSize: 2048, // in bytes.
    });
    myReadableStreamBuffer.put(imageBuffer as Buffer);
    imageFiles.push({
        buffer: imageBuffer,
        fieldname: 'fieldname-defined-in-@UseInterceptors-decorator',
        originalname: 'original-filename',
        encoding: '7bit',
        mimetype: 'file-mimetyp',
        destination: 'destination-path',
        filename: 'file-name',
        path: 'file-path',
        size: 955578,
        stream: myReadableStreamBuffer,
    });

    Finally, we can call our addProduct method with newly created imagesFiles variable as arguments.