How can I resolve AWS mock test with singleton classes

Viewed 281

when I execute Jest in Javascript test with AWS mock via npm, it will be Failure. because I use singleton class.

The difference like here.

「module.exports = Users;」 or 「module.exports = new Users();」

I guess AWS mock doesn't work with singleton class.

in that cause, how should I do to solve this problem?

'use strick';

var aws = require('aws-sdk')
aws.config.update({region:'ap-northeast-1'})

class Users {

    constructor() {
        this.table = 'Users'
        this.dynamodb = new aws.DynamoDB()
    }

    getData(email) {
        let params = {
            TableName: this.table,
            Key      : { 'email': {'S':email} }
        }

        return this.dynamodb.getItem(params).promise()
    }
}
// module.exports = Users // ← this will be success.
module.exports = new Users(); // ← this will be failure.
'use strict';

var aws = require('aws-sdk-mock'),
    users = require('./user'),
    chai = require('chai'),
    path = require('path'),
    should = chai.should(),
    input = 'test@gmail.com',
    usersObj;

aws.setSDK(path.resolve('node_modules/aws-sdk'));

describe('All Tests', function () {
    // this.timeout(0);
    beforeEach(function () {
        aws.mock('DynamoDB', 'getItem', function (params, callback) {
            callback(null, {Item: {email: params.Key.email.S}});
        });

        // usersObj = new users(); ← this will be success.
        usersObj = users; // ← this will be failure.
    });

    it('getData', function (done) {
        usersObj.getData(input).then(function (res) {

            console.log(res);

            res.Item.email.should.equal(input);
            done();
        });
    });
});

3 Answers

This line:

module.exports = new Users();

...means that a Users object will get created as soon as the code runs...and it runs as soon as user.js is required.

This line:

users = require('./user')

...is at the top of your test file and this line:

aws.mock('DynamoDB', 'getItem', function (params, callback) {
  callback(null, {Item: {email: params.Key.email.S}});
});

...is in a beforeEach...

...which means that user.js is required and runs before the mock has been created...which causes the test to fail.


If you are going to export an instance of Users then you just need to make sure you don't require the user.js file in your test until after you have set up your mock:

var aws = require('aws-sdk-mock'),
  chai = require('chai'),
  path = require('path'),
  input = 'test@gmail.com',
  usersObj;
chai.should()

aws.setSDK(path.resolve('node_modules/aws-sdk'));

describe('All Tests', function () {
  beforeEach(function () {
    aws.mock('DynamoDB', 'getItem', function (params, callback) {
      callback(null, { Item: { email: params.Key.email.S } });
    });  // <= set up the mock first...

    usersObj = require('./user');  // <= ...then require user.js
  });

  it('getData', function (done) {
    usersObj.getData(input).then(function (res) {
      res.Item.email.should.equal(input);  // Success!
      done();
    });
  });
});

I could resolve this pattern too.

'use strict';

var aws = require('aws-sdk-mock'),
    users = require('./user'),
    chai = require('chai'),
    path = require('path'),
    should = chai.should(),
    input = 'test@gmail.com',
    usersObj;

const awsObject = require('aws-sdk');

aws.setSDK(path.resolve('node_modules/aws-sdk'));

describe('All Tests', function () {
    // this.timeout(0);
    beforeEach(function () {
        aws.mock('DynamoDB', 'getItem', function (params, callback) {
            callback(null, {Item: {email: params.Key.email.S}});
        });

        // it will be resolve problem by creating new AWS instance.
        users.dynamodb = new awsObject.DynamoDB();
    });

    it('getData', function (done) {
        users.getData(input).then(function (res) {

            console.log(res);

            res.Item.email.should.equal(input);
            done();
        });
    });
});

You must call the aws client inside the class constructor

class MyClass {
  constructor(){
    this.dynamodb = new DynamoDB.DocumentClient({ region: "us-west-2" });
  }
...

In the test file you must create a new instance of your class just after call de AWSMock. Example:

it('Should save on dinamoDB with param atributes void()', async () => {
  AWSMock.mock('DynamoDB.DocumentClient', 'update', function (params, callback){
    callback(null, { Attributes: { currentValue: 1 } } );
  });
  AWSMock.mock('DynamoDB.DocumentClient', 'put', function (params, callback){
    callback(null, true);
  });

  const myClass = new MyClass();
...
Related