How to mock S3 with jest?

17,950

Solution 1

Here is the unit test solution:

s3Service.ts:

import { S3 } from 'aws-sdk';
export class S3Service {
  private readonly s3: S3;
  private readonly bucket: string;
  constructor(private readonly configService) {
    this.s3 = new S3({
      accessKeyId: this.configService.get(''),
      secretAccessKey: this.configService.get(''),
      region: this.configService.get(''),
    });
    this.bucket = this.configService.get('');
  }
  public async upload(name: string, contentType: string, buffer: Buffer): Promise<any> {
    const bucket = this.bucket;
    const params = { Bucket: bucket, Key: 'key', Body: buffer };
    const upload = await this.s3.upload(params).promise();
    return upload;
  }
}

s3Service.test.ts:

import { S3Service } from './s3Service';
const mS3Instance = {
  upload: jest.fn().mockReturnThis(),
  promise: jest.fn(),
};
jest.mock('aws-sdk', () => {
  return { S3: jest.fn(() => mS3Instance) };
});
describe('61830632', () => {
  it('should upload correctly', async () => {
    const configService = {
      get: jest
        .fn()
        .mockReturnValueOnce('accessKeyId')
        .mockReturnValueOnce('secretAccessKey')
        .mockReturnValueOnce('us-east')
        .mockReturnValueOnce('bucket-dev'),
    };
    mS3Instance.promise.mockResolvedValueOnce('fake response');
    const s3Service = new S3Service(configService);
    const actual = await s3Service.upload('name', 'contentType', Buffer.from('ok'));
    expect(actual).toEqual('fake response');
    expect(mS3Instance.upload).toBeCalledWith({ Bucket: 'bucket-dev', Key: 'key', Body: Buffer.from('ok') });
  });
});

unit test results with 100% coverage:

 PASS  stackoverflow/61830632/s3Service.test.ts (11.362s)
  61830632
    ✓ should upload correctly (6ms)
--------------|---------|----------|---------|---------|-------------------
File          | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
--------------|---------|----------|---------|---------|-------------------
All files     |     100 |      100 |     100 |     100 |                   
 s3Service.ts |     100 |      100 |     100 |     100 |                   
--------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        12.738s

Solution 2

Here is my solution:

jest.mock('aws-sdk', () => {
    class mockS3 {
        getSignedUrl(op, obj) {
            return 'url';
        }
    }
    return {
        ...jest.requireActual('aws-sdk'),
        S3: mockS3,
    };
});
Share:
17,950
luickx
Author by

luickx

Updated on June 10, 2022

Comments

  • luickx
    luickx 12 months

    I am tryng to code a test for upload. But i am not understating how to properly use jest.mock('aws-sdk')

    export class S3Service {
      private readonly s3: S3;
      private readonly bucket: string;
      constructor(private readonly configService: ConfigService) {
        this.s3 = new S3({
          accessKeyId: this.configService.get(''),
          secretAccessKey: this.configService.get(''),
          region: this.configService.get(''),
        });
        this.bucket = this.configService.get('');
      }
    async upload(name: string, contentType: string, buffer: Buffer): Promise<string> {
        const upload = await this.s3.upload({params...}).promise();
        return upload;
      }
    }
    
  • cischa
    cischa over 2 years
    Thanks for the illustrative answer. Conceptually, what does jest.fn().mockReturnThis() do? Does it let you chain calls?
  • slideshowp2
    slideshowp2 over 2 years
    @cischa Yes. It let you chain calls.
  • Benjamin Heinke
    Benjamin Heinke over 2 years
    @slideshowp2 Copying your code in my test returns this error from jest: ReferenceError: .../__tests__/user_handler.spec.ts: The module factory of jest.mock() is not allowed to reference any out-of-scope variables. Invalid variable access: mS3Instance Allowed objects: Array, ArrayBuffer, Atomics, BigInt, BigInt64Array, BigUint64Array, Boolean, Buffer, DTRACE_HTTP_CLIENT_REQUEST, DTRACE_HTTP_CLIENT_RESPONSE, DTRACE_HTTP_SERVER_REQUEST, DTRACE_HTTP_SERVER_RESPONSE, DTRACE_NET_SERVER_CONNECTION, DTRACE_NET_STREAM_END, DataView, Date, Error, EvalError,.....
  • timomeinen
    timomeinen over 2 years
    @BenjaminHeinke This is just a warning. Rename mS3Instance to something that begins with mock - for example mockS3.
  • Elte156
    Elte156 over 1 year
    Thanks for this. I can confirm that the above works.
  • Kaluk
    Kaluk 12 months
    Man that was a life saver thank you very much. I've worked with NestJS services in my other tests but for some reason i went full retard when dealing with S3, if only i had realized sooner that there was an easier way.