1

I have this exported function and I'd like to test that sts.assumeRole gets called:

const AWS = require('aws-sdk')
const sts = new AWS.STS();

module.exports.assumeRole = async (roleArn) => {
  let params = {
    RoleArn: roleArn,
    RoleSessionName: 'MessagingSession',
  }

  return await sts.assumeRole(params).promise()
}

This is what I have tried:

const chai = require('chai')
const expect = chai.expect
const sinon = require('sinon')
chai.use(require('sinon-chai'))
const AWS = require('aws-sdk')

describe('assumeRole', () => {
  it('throws an exception if roleArn is not present', async () => {
    const authenticationService = require('../services/authenticationService.js')
    await expect(authenticationService.assumeRole('')).to.eventually.be.rejectedWith('no ARN present')
  })

  it('calls sts assumeRole', () => {
    const stsStub = sinon.stub({ assumeRole: () => {} });
    const awsStub = sinon.stub(AWS, 'STS');
    awsStub.returns(stsStub);

    const authenticationService = require('../services/authenticationService.js')

    authenticationService.assumeRole('a-role-arn')

    expect(stsStub).to.have.been.calledOnce
  })
})

This results in TypeError: { assumeRole: [Function: assumeRole] } is not a spy or a call to a spy!

I'm new to spies and stubs and javascript testing, any help is appreciated in getting this test working!

Note: I want to be using the expect assertion syntax and don't want to use any other external packages

EDIT: now using slideshowp2 's solution I have the following:

const chai = require('chai')
chai.use(require('chai-as-promised'))
const expect = chai.expect
const sinon = require('sinon')
chai.use(require('sinon-chai'))
const AWS = require('aws-sdk')

describe('assumeRole', () => {
  it('throws an exception if roleArn is not present', async () => {
    const authenticationService = require('../services/authenticationService.js')
    await expect(authenticationService.assumeRole('')).to.eventually.be.rejectedWith('no ARN present')
  })

  it('calls sts.assumeRole', async () => {
    const stsFake = {
      assumeRole: sinon.stub().returnsThis(),
      promise: sinon.stub(),
    };
    const STSStub = sinon.stub(AWS, 'STS').callsFake(() => stsFake);
    const authenticationService = require('../services/authenticationService.js')

    await authenticationService.assumeRole('a-role-arn')

    sinon.assert.calledOnce(STSStub);
    sinon.assert.calledWith(stsFake.assumeRole, { RoleArn: 'a-role-arn', RoleSessionName: 'MessagingSession' });
    
    STSStub.restore();
  })
})

These two tests now work and pass independently but run all together the second one fails. I get the error ValidationError: 1 validation error detected: Value 'a-role-arn' at 'roleArn' failed to satisfy constraint: Member must have length greater than or equal to 20 It looks like the STS library is not being stubbed anymore

user3895395
  • 471
  • 4
  • 16

1 Answers1

0

Here is the unit test solution:

index.js:

const AWS = require('aws-sdk');
const sts = new AWS.STS();

module.exports.assumeRole = async (roleArn) => {
  let params = {
    RoleArn: roleArn,
    RoleSessionName: 'MessagingSession',
  };

  return await sts.assumeRole(params).promise();
};

index.test.js:

const sinon = require('sinon');
const AWS = require('aws-sdk');

describe('62918753', () => {
  it('should pass', async () => {
    const stsFake = {
      assumeRole: sinon.stub().returnsThis(),
      promise: sinon.stub(),
    };
    const STSStub = sinon.stub(AWS, 'STS').callsFake(() => stsFake);
    const authenticationService = require('./');
    await authenticationService.assumeRole('a-role-arn');
    sinon.assert.calledOnce(STSStub);
    sinon.assert.calledWith(stsFake.assumeRole, { RoleArn: 'a-role-arn', RoleSessionName: 'MessagingSession' });
    STSStub.restore();
  });
});

unit test result with coverage report:

  62918753
    ✓ should pass (919ms)


  1 passing (926ms)

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------|---------|----------|---------|---------|-------------------
All files |     100 |      100 |     100 |     100 |                   
 index.js |     100 |      100 |     100 |     100 |                   
----------|---------|----------|---------|---------|-------------------
Lin Du
  • 88,126
  • 95
  • 281
  • 483
  • thank you for this - in isolation this works! there is an issue, however, since I have other tests in the same file that have already required authenticationService, when run all together the test fails because the non-stubbed version of STS is used – user3895395 Jul 16 '20 at 07:59
  • is there some pattern for unit tests requiring the same module when parts of it are stubbed? – user3895395 Jul 16 '20 at 08:01
  • @user3895395 can you update your question, provide two test cases for reproducing the issue? – Lin Du Jul 16 '20 at 08:03
  • I just updated it. I'm also not sure if it is an anti-pattern to require the module being tested in every single test scenario like I have been – user3895395 Jul 16 '20 at 08:09
  • @user3895395 I don't think it's an anti-pattern, we `require` module in test cases is for asserting that the constructor `new AWS.STS();` is called or not after we make stub for it. Because you did initialization work in the module scope. The code in the module scope will be executed when you `require`/`import` the module. – Lin Du Jul 16 '20 at 08:14
  • @user3895395 You should update your question with my answer. In the first test case, you didn't stub anything. I don't understand. – Lin Du Jul 16 '20 at 08:19
  • apologies - I updated the question with the new solution and problem – user3895395 Jul 16 '20 at 08:56