gab
gab

Reputation: 63

How to mock overloading signatures with Jest using typescript?

I am trying to mock a function of a singleton class fetching a value from the aws ssm parameter store.

The type definitions for getParameter() are as follows:

  getParameter(params: SSM.Types.GetParameterRequest, callback?: (err: AWSError, data: SSM.Types.GetParameterResult) => void): Request<SSM.Types.GetParameterResult, AWSError>;
  
  getParameter(callback?: (err: AWSError, data: SSM.Types.GetParameterResult) => void): Request<SSM.Types.GetParameterResult, AWSError>;

My test looks like this:

import {SSM} from 'aws-sdk';
import {SSMParameter} from './src/module/ssm/ssm-cache-parameter';
import {mocked} from 'jest-mock';
import { AWSError } from 'aws-sdk';

jest.mock('aws-sdk', () => {
  const mSSMInstance = {
    getParameter: jest.fn(),
  };
  const mSSM = jest.fn(() => mSSMInstance);

  return { SSM: mSSM };
});


describe('amazon service mock', () => {
  let ssmParameter: SSMParameter;
  it('should get Parameter from Parameter Store', async () => {
    ssmParameter = SSMParameter.getInstance();
    expect(SSM).toBeCalled();
    console.log(ssmParameter);
    const mSSMInstance = new SSM();
    const mData = {
      Parameter: {
        ARN: 'arn:aws:ssm:eu-test-1:123:NAME',
        LastModifiedDate: new Date(2020, 09, 1),
        Name: 'NAME',
        Type: 'String',
        Value: 'VALUE',
        Version: 1,
      }
    };
    const error: AWSError = {
        code: 'ERROR_CODE',
        message: 'ERROR_MESSAGE',
        name: 'ERROR_NAME',
        time: new Date(2020, 09, 1),
    }
    mocked(mSSMInstance.getParameter).mockImplementationOnce(
      (params: SSM.GetParameterRequest, callback?: (err: AWSError, data: SSM.GetParameterResult) => void): any => {
        if (callback) {
          callback(error, mData);
        }
      },
    );
    const actual = ssmParameter.cache({Name: 'NAME', WithDecryption: false}, 30000);
    expect(actual).toBe('VALUE'); 
  });
});

I am getting the following error, which I believe is because TypeSCript doesn't know for certain which definition to use:

Argument of type '(params: SSM.GetParameterRequest, callback?: ((err: AWSError, data: SSM.GetParameterResult) => void) | undefined) => any' is not assignable to parameter of type '{ (params: GetParameterRequest, callback?: ((err: AWSError, data: GetParameterResult) => void) | undefined): Request<GetParameterResult, AWSError>; (callback?: ((err: AWSError, data: GetParameterResult) => void) | undefined): Request<...>; }'. Types of parameters 'params' and 'callback' are incompatible. Type '((err: AWSError, data: GetParameterResult) => void) | undefined' is not assignable to type 'GetParameterRequest'. Type 'undefined' is not assignable to type 'GetParameterRequest'.

I am stuck. Any help on how to resolve this would be much appreciated.

Upvotes: 0

Views: 902

Answers (1)

gab
gab

Reputation: 63

The solution was to cast the signature I wanted to use for my test. I solved it as follows:

import {SSM, AWSError, Request} from 'aws-sdk';
import {cache} from './src/module/ssm/ssm-cache-parameter-wrapper';

jest.mock('aws-sdk', () => {
  const mRequestInstance = {
    promise: jest.fn(),
  };
  const mRequest = jest.fn(() => mRequestInstance);
  const mSSMInstance = {
    getParameter: jest.fn(() => Request),
  };
  const mSSM = jest.fn(() => mSSMInstance);

  return {Request: mRequest, SSM: mSSM};
});
type GetParameter = (
  params: SSM.Types.GetParameterRequest,
  callback?: (err: AWSError, data: SSM.Types.GetParameterResult) => void
) => any;

describe('AWS System Manager Cache Mock', () => {
  it('should get Parameter from Parameter Store', async () => {
    const mSSMInstance = new SSM();
    const mRequestInstance = new Request(mSSMInstance, 'getParameter', {Name: 'NAME', WithDecryption: false});

    jest
      .mocked(mSSMInstance.getParameter as GetParameter)
      .mockImplementationOnce(
        (
          _params: SSM.Types.GetParameterRequest,
          _callback?: (err: AWSError, data: SSM.Types.GetParameterResult) => void
        ) => mRequestInstance
      );
    jest.mocked(mRequestInstance.promise as any).mockResolvedValueOnce({
      Parameter: {
        Value: 'VALUE',
      },
    });
    const actual = await cache({
      cacheTimeInMilliSeconds: 30000,
      decrypt: false,
      name: 'NAME',
    });
    expect(SSM).toBeCalled();
    expect(Request).toBeCalled();
    expect(actual).toBe('VALUE');
  });
});

Upvotes: 0

Related Questions