Im testing a standard S3 Get Object Call, but when Jest tests my function, it skips the Get Object call and jumps to end of function, ending the execution and returning nothing. When I checked code coverage, all lines are covered expect the getObject call.
This is my s3 file being tested. I have checkpoints there to show that the s3.getObject call is being skipped by Jest.
const AWS = require('aws-sdk'); const s3 = new AWS.S3({ apiVersion: '2012-11-05 ' }) function getS3Object(bucket, file) { return new Promise((resolve, reject) => { console.log('Checkpoint 1') const parameters = { Bucket: bucket, Key: file } s3.getObject(parameters, (error, data) => { console.log('checkpoint 2') if (error) { console.log('error') reject(error) } else { console.log('success') resolve(data) } }) console.log('Checkpoint 3') }) } module.exports = { getS3Object }
This is my test file.
const s3Handler = require('../s3') const AWS = require('aws-sdk') jest.mock('aws-sdk', () => { const mockS3 = { getObject: jest.fn().mockReturnThis(), promise: jest.fn() } return { config: { update() { return {}; } }, S3: jest.fn(() => mockS3) } }) describe('S3 get object test', () => { test('test', async () => { const mockS3 = new AWS.S3(); mockS3.getObject('test-bucket', 'test-key').promise.mockResolvedValueOnce('s3file.js'); const output = s3Handler.getS3Object('test-bucket', 'test-key'); expect(output).resolves.toEqual('blah') }) })
This test passes (even though the expect state is wrong. It should return ‘s3File.js’, not ‘blah’ but the test passes regardless). For the console logs, only checkpoints 1 and 3 are hit – the entire S3 call is ignored by the function. Is this an issue with how I mocked s3?
Advertisement
Answer
Since the s3.getObject()
accepts an error-first callback, it’s NOT a JS promise, you should use .mockImplementation((params, callback) => callback(mockErr, mockData))
to mock the error-first callback and invoke it with mock data or error.
E.g.
s3.js
:
const AWS = require('aws-sdk'); const s3 = new AWS.S3({ apiVersion: '2012-11-05 ' }); function getS3Object(bucket, file) { return new Promise((resolve, reject) => { console.log('Checkpoint 1'); const parameters = { Bucket: bucket, Key: file, }; s3.getObject(parameters, (error, data) => { console.log('checkpoint 2'); if (error) { console.log('error'); reject(error); } else { console.log('success'); resolve(data); } }); console.log('Checkpoint 3'); }); } module.exports = { getS3Object };
s3.test.js
:
const s3Handler = require('./s3'); const AWS = require('aws-sdk'); jest.mock('aws-sdk', () => { const mockS3 = { getObject: jest.fn() }; return { config: { update() { return {}; }, }, S3: jest.fn(() => mockS3), }; }); describe('S3 get object test', () => { let mockS3; beforeAll(() => { mockS3 = new AWS.S3(); }); test('should get object', async () => { mockS3.getObject.mockImplementation((params, callback) => { callback(null, 's3file.js'); }); const output = s3Handler.getS3Object('test-bucket', 'test-key'); await expect(output).resolves.toEqual('s3file.js'); }); test('should handle error', async () => { const mError = new Error('fake error'); mockS3.getObject.mockImplementation((params, callback) => { callback(mError); }); const output = s3Handler.getS3Object('test-bucket', 'test-key'); await expect(output).rejects.toThrow(mError); }); });
Test result:
PASS stackoverflow/72694206/s3.test.js (10.902 s) S3 get object test ✓ should get object (18 ms) ✓ should handle error (4 ms) console.log Checkpoint 1 at stackoverflow/72694206/s3.js:7:13 console.log checkpoint 2 at stackoverflow/72694206/s3.js:13:15 console.log success at stackoverflow/72694206/s3.js:18:17 console.log Checkpoint 3 at stackoverflow/72694206/s3.js:22:13 console.log Checkpoint 1 at stackoverflow/72694206/s3.js:7:13 console.log checkpoint 2 at stackoverflow/72694206/s3.js:13:15 console.log error at stackoverflow/72694206/s3.js:15:17 console.log Checkpoint 3 at stackoverflow/72694206/s3.js:22:13 ----------|---------|----------|---------|---------|------------------- File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s ----------|---------|----------|---------|---------|------------------- All files | 100 | 100 | 100 | 100 | s3.js | 100 | 100 | 100 | 100 | ----------|---------|----------|---------|---------|------------------- Test Suites: 1 passed, 1 total Tests: 2 passed, 2 total Snapshots: 0 total Time: 11.513 s, estimated 12 s