如何使用 sinon 存根 aws s3 upload

Rif*_*ife 5 javascript amazon-s3 mocha.js node.js sinon

如何在 Node.js 中对 S3 上传进行存根?

我用的是摩卡和诗农。我有一个导出包含上传方法的类实例的文件。它看起来像这样:

// storage.ts
import * as AWS from 'aws-sdk';
import archiver from 'archiver';
import retry from 'bluebird-retry';


export class Storage {
  private readonly s3: AWS.S3 = new AWS.S3({
    endpoint: MINIO_ENDPOINT,
    accessKeyId: AWS_ACCESS_KEY_ID,
    secretAccessKey: AWS_SECRET_ACCESS_KEY,
    s3ForcePathStyle: true,
    signatureVersion: 'v4',
  });
  private readonly uploadBucket: string = UPLOAD_BUCKET;
  private readonly downloadBucket: string = DOWNLOAD_BUCKET;

  public async upload(localPath: string, s3Key: string, onProgress: (progress: number) => void): Promise<void> {
    await retry(async () => { // Be careful, it will influence stub.
      const stat = fse.statSync(localPath);
      let readable: stream.Readable;
      let archive: archiver.Archiver | undefined;
      if (stat.isFile()) {
        readable = fse.createReadStream(localPath);
      } else {
        archive = archiver('zip', { zlib: { level: 0 } }).directory(localPath, false);
        readable = archive;
      }
      const request = this.s3.upload({ Bucket: this.uploadBucket, Key: s3Key, Body: readable });
      request.on('httpUploadProgress', ({ loaded }) => {
        onProgress(loaded);
      });
      if (archive) {
        archive.finalize().catch(console.error);
      }

      await request.promise().catch((err) => {
        fse.removeSync(localPath);
        throw err;
      });
    }, { max_tries: UPLOAD_RETRY_TIMES, throw_original: true });
  }

}

export const storage = new Storage();

Run Code Online (Sandbox Code Playgroud)

我尝试在单元测试中存根此上传方法,它看起来像:

import { storage } from './storage';
import * as AWS from 'aws-sdk';
import sinon from 'sinon';

describe('Storage', () => {
  let sandbox: sinon.SinonSandbox;

  before(() => {
    sandbox = sinon.createSandbox();
  });

  afterEach(() => {
    sandbox.restore();
  });

  it('upload', async () => {

    const s3Stub = sandbox.stub(AWS.S3.prototype, 'upload'); // something wrong

    await storage.upload(
      './package.json',
      's3Key',
      uploadBytes => { return uploadBytes; });

    expect(s3Stub).to.have.callCount(1);
    s3Stub.restore();

  });
});

Run Code Online (Sandbox Code Playgroud)

我收到一个错误:

Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.
Run Code Online (Sandbox Code Playgroud)

我想测试上传方法,但不真正上传文件到s3。

我应该怎么做 ?

谢谢大家。

sli*_*wp2 3

在您的文件中启用esModuleInterop: true配置tsconfig.json并将其更改import * as AWS from 'aws-sdk'import AWS from 'aws-sdk'.

\n

由于该new Storage()语句将在您导入此模块时立即执行,因此您应该./storage.ts在存根AWS.S3类之后导入该模块。

\n

PS我删除了与问题无关的代码

\n

例如

\n

storage.ts

\n
import AWS from 'aws-sdk';\n\nconst UPLOAD_BUCKET = 'UPLOAD_BUCKET';\nconst MINIO_ENDPOINT = 'MINIO_ENDPOINT';\nconst AWS_ACCESS_KEY_ID = 'AWS_ACCESS_KEY_ID';\nconst AWS_SECRET_ACCESS_KEY = 'AWS_SECRET_ACCESS_KEY';\n\nexport class Storage {\n  private readonly s3: AWS.S3 = new AWS.S3({\n    endpoint: MINIO_ENDPOINT,\n    accessKeyId: AWS_ACCESS_KEY_ID,\n    secretAccessKey: AWS_SECRET_ACCESS_KEY,\n    s3ForcePathStyle: true,\n    signatureVersion: 'v4',\n  });\n  private readonly uploadBucket: string = UPLOAD_BUCKET;\n\n  public async upload(localPath: string, s3Key: string, onProgress: (progress: number) => void): Promise<void> {\n    const request = this.s3.upload({ Bucket: this.uploadBucket, Key: s3Key, Body: '123' });\n  }\n}\n\nexport const storage = new Storage();\n
Run Code Online (Sandbox Code Playgroud)\n

storage.test.ts

\n
import AWS from 'aws-sdk';\nimport sinon from 'sinon';\n\ndescribe('68431461', () => {\n  let sandbox: sinon.SinonSandbox;\n\n  before(() => {\n    sandbox = sinon.createSandbox();\n  });\n\n  afterEach(() => {\n    sandbox.restore();\n  });\n\n  it('should pass', async () => {\n    const s3InstanceStub = { upload: sandbox.stub() };\n    const s3Stub = sandbox.stub(AWS, 'S3').callsFake(() => s3InstanceStub);\n    const { storage } = await import('./storage');\n    const onProgressStub = sandbox.stub();\n    await storage.upload('./package.json', 's3Key', onProgressStub);\n    sinon.assert.calledOnce(s3Stub);\n    sinon.assert.calledOnce(s3InstanceStub.upload);\n  });\n});\n
Run Code Online (Sandbox Code Playgroud)\n

测试结果:

\n
  68431461\n    \xe2\x9c\x93 should pass (796ms)\n\n\n  1 passing (810ms)\n\n------------|---------|----------|---------|---------|-------------------\nFile        | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s \n------------|---------|----------|---------|---------|-------------------\nAll files   |     100 |      100 |     100 |     100 |                   \n storage.ts |     100 |      100 |     100 |     100 |                   \n------------|---------|----------|---------|---------|-------------------\n
Run Code Online (Sandbox Code Playgroud)\n