aws-sdk-mock - 模拟 s3.upload 不使用模拟实现

aws-sdk-mock - Mocking s3.upload is not using mock implementation

我正在使用这个包装函数将对象上传到 s3

// upload.js
async function uploadToS3 (body, bucket, key) {
    console.log(`Uploading data to s3://${bucket}${key}`)
    await s3
        .upload({
                Body: body,
                Bucket: bucket,
                Key: key
            })
        .promise()
        .then((data) => {
                console.log(`Successfully uploaded data to ${data.Location}`)
            }
        )
        .catch((err) => {
            console.error(err)
        })
}

我正在尝试使用 aws-sdk-mock 来模拟该函数,因此当它被调用时它实际上不会将项目推送到 s3,我可以验证它正在记录成功还是失败。

这是我试过的

// upload.test.js

describe( 'uploadToS3', () => {
    test('should upload a file to s3', async () => {
        const body = 'test|data'
        const bucket = 'testBucket'
        const key = 'testKey'

        AWS.mock( 'S3', 'upload',function (params, callback){
            callback(null, 'successfully put item in s3');
        });

        await util.uploadToS3(body, bucket, key)
    })
})

不幸的是,当我调用 uploadToS3 函数时,它仍然使用实际的 s3.upload 实现并尝试将对象发送到 S3。我已经成功地对其他 AWS 服务使用了类似的模拟方法,但这个方法似乎给我带来了问题。

如何模拟 AWS.S3.upload 函数以及后续的 .then.catch 函数?

也许这可以用 Jest 代替?

您不需要使用 aws-sdk-mock 包。您可以使用 jest.mock(moduleName, factory, options) 方法自行模拟 aws-sdk 包。

例如

upload.js:

import AWS from 'aws-sdk';

const s3 = new AWS.S3();

export async function uploadToS3(body, bucket, key) {
  console.log(`Uploading data to s3://${bucket}${key}`);
  await s3
    .upload({
      Body: body,
      Bucket: bucket,
      Key: key,
    })
    .promise()
    .then((data) => {
      console.log(`Successfully uploaded data to ${data.Location}`);
    })
    .catch((err) => {
      console.error(err);
    });
}

upload.test.js:

import { uploadToS3 } from './upload';
import AWS from 'aws-sdk';

jest.mock('aws-sdk', () => {
  const mockedS3 = {
    upload: jest.fn().mockReturnThis(),
    promise: jest.fn(),
  };
  return { S3: jest.fn(() => mockedS3) };
});

describe('uploadToS3', () => {
  afterAll(() => {
    jest.resetAllMocks();
  });
  afterEach(() => {
    jest.clearAllMocks();
  });
  it('should upload file to s3', async () => {
    const mockedS3 = new AWS.S3();
    const logSpy = jest.spyOn(console, 'log');
    mockedS3.promise.mockResolvedValueOnce({ Location: 'us' });
    const body = 'test|data';
    const bucket = 'testBucket';
    const key = 'testKey';
    await uploadToS3(body, bucket, key);
    expect(mockedS3.upload).toBeCalledWith({ Body: body, Bucket: bucket, Key: key });
    expect(mockedS3.promise).toBeCalledTimes(1);
    expect(logSpy).toBeCalledWith('Successfully uploaded data to us');
  });

  it('should handle error when upload file to s3 failed', async () => {
    const mockedS3 = new AWS.S3();
    const errorLogSpy = jest.spyOn(console, 'error');
    const mError = new Error('network');
    mockedS3.promise.mockRejectedValueOnce(mError);
    const body = 'test|data';
    const bucket = 'testBucket';
    const key = 'testKey';
    await uploadToS3(body, bucket, key);
    expect(mockedS3.upload).toBeCalledWith({ Body: body, Bucket: bucket, Key: key });
    expect(mockedS3.promise).toBeCalledTimes(1);
    expect(errorLogSpy).toBeCalledWith(mError);
  });
});

单元测试结果:

 PASS  examples/67204024/upload.test.js (6.42 s)
  uploadToS3
    ✓ should upload file to s3 (16 ms)
    ✓ should handle error when upload file to s3 failed (6 ms)

  console.log
    Uploading data to s3://testBuckettestKey

      at console.<anonymous> (node_modules/jest-environment-enzyme/node_modules/jest-mock/build/index.js:866:25)

  console.log
    Successfully uploaded data to us

      at console.<anonymous> (node_modules/jest-environment-enzyme/node_modules/jest-mock/build/index.js:866:25)

  console.log
    Uploading data to s3://testBuckettestKey

      at console.<anonymous> (node_modules/jest-environment-enzyme/node_modules/jest-mock/build/index.js:866:25)

  console.error
    Error: network
        at /Users/dulin/workspace/github.com/mrdulin/jest-v26-codelab/examples/67204024/upload.test.js:35:20
        at Generator.next (<anonymous>)
        at /Users/dulin/workspace/github.com/mrdulin/jest-v26-codelab/examples/67204024/upload.test.js:8:71
        at new Promise (<anonymous>)
        at Object.<anonymous>.__awaiter (/Users/dulin/workspace/github.com/mrdulin/jest-v26-codelab/examples/67204024/upload.test.js:4:12)
        at Object.<anonymous> (/Users/dulin/workspace/github.com/mrdulin/jest-v26-codelab/examples/67204024/upload.test.js:32:70)
        at Object.asyncJestTest (/Users/dulin/workspace/github.com/mrdulin/jest-v26-codelab/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:106:37)
        at /Users/dulin/workspace/github.com/mrdulin/jest-v26-codelab/node_modules/jest-jasmine2/build/queueRunner.js:45:12
        at new Promise (<anonymous>)
        at mapper (/Users/dulin/workspace/github.com/mrdulin/jest-v26-codelab/node_modules/jest-jasmine2/build/queueRunner.js:28:19)
        at /Users/dulin/workspace/github.com/mrdulin/jest-v26-codelab/node_modules/jest-jasmine2/build/queueRunner.js:75:41
        at processTicksAndRejections (internal/process/task_queues.js:93:5)

      16 |     })
      17 |     .catch((err) => {
    > 18 |       console.error(err);
         |               ^
      19 |     });
      20 | }
      21 | 

      at console.<anonymous> (node_modules/jest-environment-enzyme/node_modules/jest-mock/build/index.js:866:25)
      at examples/67204024/upload.js:18:15

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        6.896 s, estimated 7 s

这也是一个选项:

const getObjectStub = AWS.S3.prototype.getObject = Sinon.stub();
    getObjectStub.yields(null, {
        AcceptRanges: "bytes", 
        ContentLength: 3191, 
        ContentType: "image/jpeg", 
        Metadata: {
            ...
        }, 
        TagCount: 2, 
        VersionId: "null"
    }
);