What is the best way to simulate a dynamoDB call using jest?

In a simple Handler, I have calls to getData defined in a separate file

export const handler = async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
  let respData = await new DynamoDBClient().getData(123);
  return {
    statusCode: 200,
    body: JSON.stringify(respData),
  };
};

The DynamoDB class contains the following:

import { DynamoDB } from 'aws-sdk';
export default class DynamoDBClient {
private config: Config;
private client: DynamoDB.DocumentClient;

constructor() {
  this.config = getConfig();

  const dynamoDBClientConfig = this.config.mockDynamoDBEndpoint
  ? {
      endpoint: this.config.mockDynamoDBEndpoint,
      sslEnabled: false,
      region: 'local'
    }
  : undefined;

 this.client = new DynamoDB.DocumentClient(dynamoDBClientConfig);
}
// function
getData= async (id: string): Promise<any> => {
 const response = await this.client
  .query({
    TableName: tableName,
    IndexName: tableIndex,
    KeyConditionExpression: 'id= :id',
    ExpressionAttributeValues: {
      ':id': id
    }
  })
  .promise();
 return response;
}
}

Here is my test case:

describe('DynamoDB', () => {
test('should return no data', async () => {
    
    const spy = jest.spyOn(DynamoDBClient, 'getData').mockImplementation(() => jest.fn(() => {
      return Promise.resolve({});
  }));
    const actual = await handler(event);
    console.log(actual);

    expect(actual).toEqual({ statusCode: 400, body: JSON.stringify({ }) });
  });
 });

Answer №1

When defining the .getData() method using property initializer syntax, it becomes bound to the class instance. However, since the handler function relies on the DynamoDBClient class through an import statement, creating an instance in the test case and passing it to the handler during its invocation is not feasible.

To address this, you can mock both the aws-sdk module and the DynamoDB.DocumentClient class along with its instance.

DynamoDBClient.ts:

import { DynamoDB } from 'aws-sdk';

function getConfig() {
  return { mockDynamoDBEndpoint: '' };
}
interface Config {
  mockDynamoDBEndpoint: string;
}

export default class DynamoDBClient {
  private config: Config;
  private client: DynamoDB.DocumentClient;

  constructor() {
    this.config = getConfig();

    const dynamoDBClientConfig = this.config.mockDynamoDBEndpoint
      ? {
          endpoint: this.config.mockDynamoDBEndpoint,
          sslEnabled: false,
          region: 'local',
        }
      : undefined;

    this.client = new DynamoDB.DocumentClient(dynamoDBClientConfig);
  }

  getData = async (id: string): Promise<any> => {
    const response = await this.client
      .query({
        TableName: 'tableName',
        IndexName: 'tableIndex',
        KeyConditionExpression: 'id= :id',
        ExpressionAttributeValues: {
          ':id': id,
        },
      })
      .promise();
    return response;
  };
}

handler.ts:

import DynamoDBClient from './DynamoDBClient';

interface APIGatewayProxyEvent {}
interface APIGatewayProxyResult {}

export const handler = async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
  let respData = await new DynamoDBClient().getData('123');
  return { statusCode: 200, body: JSON.stringify(respData) };
};

handler.test.ts:

import { handler } from './handler';
import { DynamoDB } from 'aws-sdk';

const mDocumentClientInstance = {
  query: jest.fn().mockReturnThis(),
  promise: jest.fn(),
};
jest.mock('aws-sdk', () => {
  return {
    DynamoDB: {
      DocumentClient: jest.fn(() => mDocumentClientInstance),
    },
  };
});

describe('69475890', () => {
  afterEach(() => {
    jest.clearAllMocks();
  });
  test('should pass', async () => {
    mDocumentClientInstance.promise.mockResolvedValueOnce({});
    const event = {};
    const actual = await handler(event);
    expect(actual).toEqual({ statusCode: 200, body: JSON.stringify({}) });
    expect(DynamoDB.DocumentClient).toBeCalled();
    expect(mDocumentClientInstance.query).toBeCalledWith({
      TableName: 'tableName',
      IndexName: 'tableIndex',
      KeyConditionExpression: 'id= :id',
      ExpressionAttributeValues: {
        ':id': '123',
      },
    });
    expect(mDocumentClientInstance.promise).toBeCalled();
  });
});

test result:

 PASS  examples/69475890/handler.test.ts (8.642 s)
  69475890
    ✓ should pass (4 ms)

-------------------|---------|----------|---------|---------|-------------------
File               | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------------|---------|----------|---------|---------|-------------------
All files          |     100 |       50 |     100 |     100 |                   
 DynamoDBClient.ts |     100 |       50 |     100 |     100 | 18                
 handler.ts        |     100 |      100 |     100 |     100 |                   
-------------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        9.231 s

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Can you provide instructions on how to use RXJS Observables to conduct a service poll?

If I want the get request to "api/foobar" to repeat every 500 milliseconds, how can I modify the code provided below? import {Observable} from "RxJS/Rx"; import {Injectable} from "@angular/core"; import {Http} from "@angular/http"; @Injectable() export ...

"Creating a backend server using Node.js, TypeScript, and g

I am currently in the process of developing a nodejs project that will consist of 3 key services: Gateway Product Order The Product and Order services will perform functions related to their respective names, while the Gateway service will take JSON requ ...

Validate the button's status in Ionic

When I click on a button, I am trying to retrieve the Toggle state immediately. However, I consistently receive a value of true, even when my toggle is actually set to false. I believe the issue lies in how I am manipulating the DOM. Here is an example ...

How can I pass DOCUMENT in Angular?

In my directive, I use dependency injection to access the DOCUMENT and set up an event listener: constructor(@Inject(DOCUMENT) private document: Document) {} ngOnInit() { this.document.addEventListener('click', this.clicked, true); } @Bound ...

Creating a custom Angular pipe to convert milliseconds to a formatted hh:mm:ss in Angular

Struggling to develop an Angular pipe that accurately converts milliseconds to hh:mm:ss format. Despite researching several articles, none of the solutions seem to work. Here is a snippet of the current pipe.ts implementation: transform(value) { le ...

What are effective strategies for troubleshooting Dependency Injection problems in nest.js?

Can someone explain the process of importing a third-party library into NestJS using Dependency Injection? Let's say we have a class called AuthService: export class AuthService { constructor( @Inject(constants.JWT) private jsonWebToken: any, ...

Tips on changing the name of a property within an object using JavaScript

While this question may appear to be a duplicate, there is actually a distinction. I am attempting to provide a new key that does not contain any spaces. {order_id :"123" , order_name : "bags" , pkg_no : "00123#"} My goal is ...

React Native React Thunk activating upon the initial rendering of a component and every time a key is pressed in an input field

As a newcomer to react, react-native, react-redux, and react-thunk, I find myself facing a strange issue that is puzzling me. The sign-in component I have implemented uses a thunk for user authentication. I have mapDispatchToProps and applied it to the co ...

Encountering Duplicate Identifier Error while working on Angular 2 Typescript in Visual Studio Code

Currently attempting to configure a component in Angular 2 with Typescript using Visual Studio Code on Mac. Encounter the following errors when trying the code below: duplicate identifier 'Component'. and Duplicate identifier' DashboardCompo ...

Error: Issue with accessing the 'get' property of an undefined value (Resolved issue with incompatible imports not functioning)

Encountering an issue while attempting to execute the karma TS spec file. Despite all modules and imports functioning properly without conflicts, the error persists. I've tried incorporating component.ngOninit() into beforeEach() and it(), but to no a ...

Create an e-commerce platform using the Shopify/Hydrogen integration along with ExpressJS and DynamoDB, all hosted on AWS

Looking to launch a brand new e-commerce platform using Shopify/Hydrogen (React), but I'm still undecided on the best backend approach (possibly ExpressJS + DynamoDB). My main concern is hosting everything on AWS, as I am new to that platform and unsu ...

Using pytest for a basic Python class incorporating argparser

I am currently developing a PyTest for the class ModeHandler in my project. This is the basic structure of my test: @pytest.fixture(scope="module") def mode_handler_fixture(): return ModeHandler() @pytest.fixture def mode_handler(): mo ...

Building a dynamic hierarchical list in Angular 8 with recursive expansion and collapse functionality

I am attempting to construct a hierarchical expand/collapse list that illustrates a parent-child relationship. Initially, the parent nodes will be displayed. If they have children, a carat icon is shown; otherwise, a bullet icon appears. When the carat ico ...

The Typescript error message states that the type '{ onClick: () => void; }' cannot be assigned to the type 'IntrinsicAttributes'

I'm a beginner in Typescript and I'm encountering difficulties comprehending why my code isn't functioning properly. My goal is to create a carousel image gallery using React and Typescript. However, I'm facing issues when attempting t ...

The process of prioritizing specific elements at the top of an array when ordering

In my array, I want to prioritize specific items to always appear at the top. The API response looks like this: const itemInventorylocationTypes = [ { itemInventorylocationId: '00d3898b-c6f8-43eb-9470-70a11cecbbd7', itemInvent ...

Encountering a TypeScript error in Next.js: The 'Options' type does not align with the 'NavigateOptions' type

My code snippet: import { useRouter } from 'next/navigation'; interface Options { scroll: boolean; } const Component = () => { const router = useRouter(); const updateSearchParams = () => { const searchParams = new URLSearchPa ...

Using ngIf to validate an empty string in Angular 5

I need assistance with validating an empty string retrieved from a server Although it is usually straightforward, it's just not working as expected <div class="ui-g-2 info-txt" *ngIf="appointment.Notes !==null || appointment.Notes !== ...

Best practices for testing function and internal statement following form submission handler in jest and enzyme for react applications

Working on a React functional component with a form that includes a form handler triggering onSuccess and onFailure functions, where onSuccess function closes the modal. Next, I am in need of writing tests for the onFailure, onSuccess functions, as well a ...

Is there a way to delete a wrapper (parent element) in Angular without deleting the child element as well?

The solution provided in this answer on Stack Overflow addresses jQuery and not Angular or TypeScript. My inquiry bears resemblance to this question raised on a forum, but I am specifically looking for a resolution within the context of Angular. Is there ...

The ngx-datatable encountered a resolution issue with its dependency tree and was unable to resolve it

I've been trying to incorporate ngx-datatables into an Angular 12 project by running the command npm install @swimlane/ngx-datatable. However, after installation, I encountered the following Errors: npm ERR! code ERESOLVE npm ERR! ERESOLVE unable to r ...