【问题标题】:Mocking aws-sdk S3#putObject instance method using jest使用 jest 模拟 aws-sdk S3#putObject 实例方法
【发布时间】:2018-08-17 15:41:14
【问题描述】:

对于包含以下内容的源代码:

 const S3 = require('aws-sdk/s3/clients')
 const s3 = new S3()
 s3.putObject(params, callback)

我基于this article添加了以下模拟:

 jest.mock('aws-sdk/s3/clients')
 const S3 = require('aws-sdk/s3/clients')

 it('has to mock S3#putObject', () => { 
    S3.prototype.putObject.mockImplementation(() => cb()) 
 })

但是我找不到带有 / 没有模拟的 S3.prototype.putObject,因为在 apiLoader 模式 here 期间,api 的构建方式似乎有所不同。 但是,定义似乎完全不同here

我也试过了:

  const AWS = require('aws-sdk')
  console.log(AWS.S3.prototype.putObject) // undefined

如果在原型上找不到方法,我该如何模拟?

包装在 Promise 中会破坏结果

我已将源代码包装在这样的 Promise 中:

new Promise((resolve, reject) => {
    s3.putObject(params, (err, data) => {
        if (err) {
            reject(err)
        } else {
           resolve(data)
       }
    })
})

并使用如下所示的测试:

const mockedPutObject = jest.fn();
jest.mock('aws-sdk/s3/clients', () => {
  return class S3 {
    putObject(params, cb) {
      mockedPutObject(params, cb);
    }
  }
});

it('should call aws S3.putObject method', async () => {
  const data = {
    Bucket: 'aaa',
    Key: 'bbb',
    Content: 'this can be anything',
    ACL: 'public-read'
  }
  await putObject(data)
  console.log(mockFn.calls)
  expect(mockFn).toBeCalledWith(data)
})

这会导致带有适当参数的错误 putObject › 应调用 aws S3.putObject 方法

Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.

我认为将调用代码包装在手动承诺中或使用这样的库:

const {promisify} = require('es6-promisify')
const putS3Object = promisify(s3.putObject.bind(s3))
return putS3Object(data)

同样失败。

【问题讨论】:

    标签: node.js amazon-s3 mocking jestjs aws-sdk-nodejs


    【解决方案1】:

    您可以使用jest.fn().mockImplementation

    // index.js
    const AWS = require('aws-sdk')
    const s3 = new AWS.S3()
    
    s3.putObject({},()=> {
      return 2;
    });
    
    // your test under test folder
    let AWS = require('aws-sdk');
    describe('test', () => {
      let result;
      beforeEach(()=>{
        AWS.S3 = jest.fn().mockImplementation( ()=> {
          return {
            putObject (params, cb) {
              result = cb();
            }
          };
        });
        require('../index');
      });
      test('call s3', () => {
        expect(result).toBe(2);
      });
    });
    

    【讨论】:

    • 所以我得到了Cannot assign to 'S3' because it is a read-only property.ts(2540) 我猜我适合使用打字稿。
    【解决方案2】:

    源代码

    //src.js
    const S3 = require('aws-sdk/clients/s3');
    const s3 = new S3();
    
    const putFunction = () => {
      s3.putObject(params, callback);
    }
    export default putFunction;
    

    下面的方法可以用来模拟S3客户端的putObject方法。

    const mockedPutObject = jest.fn();
    jest.mock('aws-sdk/clients/s3', () => {
      return class S3 {
        putObject(params, cb) {
          mockedPutObject(params, cb);
        }
      }
    });
    
    it('has to mock S3#putObject', () => {
        const putFunc = require('./src).default.putFunc;
        putFunc();
        expect(mockedPutObject).toHaveBeenCalledWith(params, callback);
     })
    

    【讨论】:

    • 我没想到将源代码包装在 Promise 中会导致失败,但是,看起来这是在取消调用 (???)。我是否需要改用global.S3。我已经用信息更新了问题。我们是否也需要为回调使用模拟。
    • 从测试中删除 async 让它工作,我不明白那里发生了什么。你可以解释吗?感谢您的回答。
    • 所以我在 const S3 = new AWS.S3(); 上得到了 TypeError: AWS.S3 is not a constructor,这个模拟就位。
    【解决方案3】:

    如果您不想仅出于测试目的使用 AWS.S3 中的原型方法重新创建对象,我可以通过简单地添加以下内容来模拟实现:

    // Arrange
    const spy = jest.fn();
    S3.prototype.getObject = spy;
    // Act
    ...
    //Assert
    expect(spy).toHaveBeenCalledWith(data);
    

    让我知道进展如何(即使这可能来晚了)

    【讨论】:

      猜你喜欢
      • 2016-10-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-21
      • 2012-11-10
      • 1970-01-01
      • 2021-06-01
      相关资源
      最近更新 更多