Search code examples
node.jsunit-testingjestjsaws-sdkaws-sdk-nodejs

Jest Mock an an error thrown and get the actual error from the catch


In code below I'm trying to test method getParameter for failure. Module A contains the method to test.

Module A.spec contains the test. Problem is that the test always passes, meaning it never hiuts the catch. I am mocking AWS.SSM to fail. What am I missing here?

Module A:

const AWS = require ('aws-sdk')
exports.getParameter = async function (parameterName) {
    const params = {
        Name: parameterName,
        WithDecryption: true
    };
    const ssm = new AWS.SSM();
    try {
        const paramValue = await ssm.getParameter(params).promise();
        return paramValue.Parameter.Value;
    }
    catch (e) {
        console.log(e);
        throw new Error('Error while retrieving parameter value from pstore.\n' + e);
    }
};

exports.AWS = AWS

Module A.spec.js

  const prm = require('A')

  test('should handle error', () => {
      prm.AWS.SSM = jest.fn().mockImplementation(() => {
         throw new Error()
      })
    prm.getParameter = jest.fn()
    try{
      prm.getParameter("abc")
    }
   catch(e)
   {
     console.log(e)
   }
 });

Solution

  • You can use jest.spyOn(prm.AWS, 'SSM').mockReturnValue() to mock the SSM constructor and its instance.

    E.g.

    a.js:

    const AWS = require('aws-sdk');
    
    exports.getParameter = async function (parameterName) {
      const params = {
        Name: parameterName,
        WithDecryption: true,
      };
      const ssm = new AWS.SSM();
      try {
        const paramValue = await ssm.getParameter(params).promise();
        return paramValue.Parameter.Value;
      } catch (e) {
        console.log(e);
        throw new Error('Error while retrieving parameter value from pstore.\n' + e);
      }
    };
    
    exports.AWS = AWS;
    

    a.spec.js:

    const prm = require('./a');
    
    describe('71027434', () => {
      afterEach(() => {
        jest.restoreAllMocks();
      });
      test('should get parameter value', async () => {
        const ssm = {
          getParameter: jest.fn().mockReturnThis(),
          promise: jest.fn().mockResolvedValueOnce({ Parameter: { Value: 'fake value' } }),
        };
        const SSMSpy = jest.spyOn(prm.AWS, 'SSM').mockReturnValue(ssm);
        const actual = await prm.getParameter('abc');
        expect(actual).toEqual('fake value');
        expect(SSMSpy).toBeCalledTimes(1);
        expect(ssm.getParameter).toBeCalledWith({ Name: 'abc', WithDecryption: true });
      });
    
      test('should handle error', async () => {
        const mError = new Error('network');
        const ssm = {
          getParameter: jest.fn().mockReturnThis(),
          promise: jest.fn().mockRejectedValueOnce(mError),
        };
        const SSMSpy = jest.spyOn(prm.AWS, 'SSM').mockReturnValue(ssm);
        await expect(prm.getParameter('abc')).rejects.toThrowError(
          'Error while retrieving parameter value from pstore.\n' + mError
        );
        expect(SSMSpy).toBeCalledTimes(1);
        expect(ssm.getParameter).toBeCalledWith({ Name: 'abc', WithDecryption: true });
      });
    });
    

    Test result:

     PASS  stackoverflow/71027434/a.spec.js (8.108 s)
      71027434
        ✓ should get parameter value (4 ms)
        ✓ should handle error (23 ms)
    
      console.log
        Error: network
            at /Users/dulin/workspace/github.com/mrdulin/jest-v26-codelab/stackoverflow/71027434/a.spec.js:20:20
            at Generator.next (<anonymous>)
            at /Users/dulin/workspace/github.com/mrdulin/jest-v26-codelab/stackoverflow/71027434/a.spec.js:8:71
            at new Promise (<anonymous>)
            at Object.<anonymous>.__awaiter (/Users/dulin/workspace/github.com/mrdulin/jest-v26-codelab/stackoverflow/71027434/a.spec.js:4:12)
            at Object.<anonymous> (/Users/dulin/workspace/github.com/mrdulin/jest-v26-codelab/stackoverflow/71027434/a.spec.js:19:42)
            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 Object.<anonymous> (stackoverflow/71027434/a.js:13:13)
              at Generator.throw (<anonymous>)
    
    ----------|---------|----------|---------|---------|-------------------
    File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
    ----------|---------|----------|---------|---------|-------------------
    All files |     100 |      100 |     100 |     100 |                   
     a.js     |     100 |      100 |     100 |     100 |                   
    ----------|---------|----------|---------|---------|-------------------
    Test Suites: 1 passed, 1 total
    Tests:       2 passed, 2 total
    Snapshots:   0 total
    Time:        8.623 s, estimated 10 s
    

    package version:

    "aws-sdk": "^2.875.0",
    "jest": "^26.6.3",