S3Client is not a constructor in Jest

Viewed 866

I am trying to test basic S3 commands with Jest but I am having an error after using mock. Not sure where I am wrong.

I am looking at both implementation and unit test part but didn't understand why I am having this.

https://docs.aws.amazon.com/code-samples/latest/catalog/javascriptv3-tests-s3-s3.test.js.html

aws-s3.js

const { S3Client,  HeadBucketCommand, CreateBucketCommand } = require("@aws-sdk/client-s3");
const { REGION } = require('../config');

// Create S3 service object
const s3 = new S3Client({ region: REGION });

const createS3Bucket = async ({ bucketName }) => {
  
  const bucket = await isBucketExists({ bucketName });//some other function in the same file

  if (!bucket) {
    try {
      const data = await s3.send(new CreateBucketCommand({ Bucket: bucketName }));
      console.log(`Bucket ${bucketName} created successfully`);      
      return data;
    } catch (err) {
      console.log(`Bucket ${bucketName} couldn't create`);      
      return null;
    }
  }
  
  return bucket;
};

s3.test.js

var faker = require('faker');

const mockCreateBucket = jest.fn();

jest.mock("@aws-sdk/client-s3", () => ({
    S3: function S3() {
        this.createBucket = mockCreateBucket;
    },
}));

const { createS3Bucket } = require("../utils/aws-s3");

test("has to mock S3#createBucket", async (done) => {
    const bucketName = faker.random.word();
    await createS3Bucket(bucketName);
    expect(mockCreateBucket).toHaveBeenCalledWith(bucketName);
    done();
});

Error

TypeError: S3Client is not a constructor

  3 |
  4 | // Create S3 service object
> 5 | const s3 = new S3Client({ region: REGION });
    |            ^
  6 |
  7 | const isBucketExists = async ({ bucketName }) => {
  8 |   try {

  at Object.<anonymous> (utils/aws-s3.js:5:12)
  at Object.<anonymous> (__test__/s3.test.js:11:28)

Thanks

1 Answers

Mostly you will get this error when you write test cases for it.

You're creating a new S3Client({ region: REGION }) in your S3 Class. Instead of creating a S3Client object what you can do

Create your custom class s3.api.ts

import { S3 as S3Client } from 'aws-sdk'
type S3Service = S3Client

export class S3API {
  private s3: S3Client
  constructor(s3: S3Client) {
   this.s3 = s3
  }

    public async uploadFile(file: IFile): Promise<any> {
    try {
      const params = {
        Bucket: 'test-bucket',
        Key: 'file-key',
        Body: 'buffer-image',
        ACL: 'public-read',
        ContentType: 'image/jpeg'
      }

      const result = await this.s3.putObject(params).promise()
      if (result) return { isSuccess: true, message: 'file upload successfully' }
      else return { isSuccess: false }
    } 
  }

   
}

Test case file: s3-api.test.ts

import AWS from 'aws-sdk'
import MockS3 from 'aws-sdk/clients/s3'
import { S3API } from 's3-api'

jest.mock('aws-sdk/clients/s3', () => {
  const mockS3 = {
    putObject: jest.fn().mockReturnThis(),
    promise: jest.fn()
  }
  return jest.fn(() => mockS3)
})

let mockS3: any
let s3API: any

beforeAll(() => {
  mockS3 = new MockS3()
  s3API = new S3API(mockS3)
})

describe('s3-api/uploadFile', () => {
  it('When an upload file function, an success `file upload successfully` should be returned', async () => {
    mockS3.putObject().promise.mockReturnValueOnce({})
    const file: any = {}
    const s3UploadFileResponse = await s3API.uploadFile(file)
    console.log('s3UploadFileResponse::', s3UploadFileResponse);
  })
})
when you will run this test case you will see the output like: 
s3UploadFileResponse::{ isSuccess: true, message: 'file upload successfully' }
Related