How To Mock S3 With Jest?
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 buck
Solution 1:
Here is the unit test solution:
s3Service.ts
:
import { S3 } from'aws-sdk';
exportclassS3Service {
private readonly s3: S3;
private readonly bucket: string;
constructor(private readonly configService) {
this.s3 = newS3({
accessKeyId: this.configService.get(''),
secretAccessKey: this.configService.get(''),
region: this.configService.get(''),
});
this.bucket = this.configService.get('');
}
public asyncupload(name: string, contentType: string, buffer: Buffer): Promise<any> {
const bucket = this.bucket;
const params = { Bucket: bucket, Key: 'key', Body: buffer };
const upload = awaitthis.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 = newS3Service(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', () => {
classmockS3 {
getSignedUrl(op, obj) {
return'url';
}
}
return {
...jest.requireActual('aws-sdk'),
S3: mockS3,
};
});
Solution 3:
If you are using NestJS, it can be easier than you think.
File Upload Module:
import { Module } from'@nestjs/common';
import { ConfigService } from'@nestjs/config';
import { TypeOrmModule } from'@nestjs/typeorm';
import { S3 } from'aws-sdk';
import { FileUploadController } from'./file-upload.controller';
import { FileUploadRepository } from'./file-upload.repository';
import { FileUploadService } from'./file-upload.service';
@Module({
imports: [TypeOrmModule.forFeature([FileUploadRepository])],
controllers: [FileUploadController],
providers: [
FileUploadService,
{
provide: S3,
useFactory: (configService: ConfigService) =>
new S3({
accessKeyId: configService.get('AWS_ACCESS_KEY'),
secretAccessKey: configService.get('AWS_ACCESS_SECRET'),
region: configService.get('AWS_REGION'),
}),
},
],
})
export classFileUploadModule {}
The service itself:
import { Injectable, InternalServerErrorException } from'@nestjs/common';
import { ConfigService } from'@nestjs/config';
import { InjectRepository } from'@nestjs/typeorm';
import { S3 } from'aws-sdk';
import { v4 as uuid } from'uuid';
import { FileUpload } from'./file-upload.entity';
import { FileUploadRepository } from'./file-upload.repository';
@Injectable()
exportclassFileUploadService {
constructor(@InjectRepository(FileUploadRepository)
privatereadonly publicFileRepository: FileUploadRepository,
privatereadonly configService: ConfigService,
privatereadonly s3: S3,
) {}
asyncuploadPublicFile(
dataBuffer: Buffer,
filename: string,
): Promise<FileUpload> {
const uploadResult = awaitthis.s3
.upload({
Bucket: this.configService.get('AWS_BUCKET_NAME'),
Body: dataBuffer,
Key: `${uuid()}-${filename}`,
})
.promise();
const createdFile = this.publicFileRepository.create({
key: uploadResult.Key,
url: uploadResult.Location,
});
awaitthis.publicFileRepository.save(createdFile);
return createdFile;
}
asyncdeletePublicFile(
publicFileId: string,
publicFileKey: string,
): Promise<FileUpload> {
const response = awaitthis.s3
.deleteObject({
Bucket: this.configService.get('AWS_BUCKET_NAME'),
Key: publicFileKey,
})
.promise();
if (!response) {
thrownewInternalServerErrorException(
`Could not delete file ${publicFileKey}`,
);
}
const { raw: deletedItem } = awaitthis.publicFileRepository.delete(
publicFileId,
);
return deletedItem;
}
}
and then finally the test:
import { InternalServerErrorException } from'@nestjs/common';
import { ConfigService } from'@nestjs/config';
import { Test, TestingModule } from'@nestjs/testing';
import { S3 } from'aws-sdk';
import { mockFileUpload } from'./file-upload.mock';
import { FileUploadRepository } from'./file-upload.repository';
import { FileUploadService } from'./file-upload.service';
exportconstmockFileUploadRepository = () => ({
create: jest.fn(),
save: jest.fn(),
delete: jest.fn(),
});
const mS3Instance = {
upload: jest.fn().mockReturnThis(),
promise: jest.fn(),
deleteObject: jest.fn().mockReturnThis(),
};
describe('FileUploadService', () => {
letservice: FileUploadService;
let repository;
let s3Service;
beforeEach(async () => {
constmodule: TestingModule = awaitTest.createTestingModule({
providers: [
FileUploadService,
{
provide: FileUploadRepository,
useFactory: mockFileUploadRepository,
},
{
provide: ConfigService,
useValue: {
get: jest.fn(),
},
},
{
provide: S3,
useFactory: () => mS3Instance,
},
],
}).compile();
service = module.get<FileUploadService>(FileUploadService);
repository = module.get<FileUploadRepository>(FileUploadRepository);
repository = module.get<FileUploadRepository>(FileUploadRepository);
s3Service = module.get<S3>(S3);
});
describe('FileUploadService.uploadPublicFile', () => {
it('should create public file and throw no error', async () => {
repository.create.mockResolvedValue(mockFileUpload);
repository.save.mockResolvedValue(mockFileUpload);
s3Service.promise = jest.fn().mockResolvedValue({
Key: 'some-key',
Location: 'some-location',
});
const file = Buffer.alloc(513, '0');
const response = await service.uploadPublicFile(file, 'somefilename');
expect(response).toBeDefined();
});
});
describe('FileUploadService.deletePublicFile', () => {
it('should delete public file and throw no error', async () => {
repository.delete.mockResolvedValue({ raw: mockFileUpload });
repository.save.mockResolvedValue(mockFileUpload);
s3Service.promise = jest.fn().mockResolvedValue({
Key: 'some-key',
Location: 'some-location',
});
const response = await service.deletePublicFile('someid', 'somefilename');
expect(response).toBeDefined();
});
it('should not delete public file and throw InternalServerErrorException', async () => {
repository.delete.mockResolvedValue({ raw: mockFileUpload });
repository.save.mockResolvedValue(mockFileUpload);
s3Service.promise = jest.fn().mockResolvedValue(null);
const promise = service.deletePublicFile('someid', 'somefilename');
expect(promise).rejects.toThrow(InternalServerErrorException);
});
});
});
Post a Comment for "How To Mock S3 With Jest?"