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

Encountering a TypeError while working with Next.js 14 and MongoDB: The error "res.status is not a function"

Currently working on a Next.js project that involves MongoDB integration. I am using the app router to test API calls with the code below, and surprisingly, I am receiving a response from the database. import { NextApiRequest, NextApiResponse, NextApiHandl ...

Challenges encountered when sending an HTTP post request in Ionic 2 with Angular 2

Whenever I try to make a post request in my ionic2 application, I encounter an error without any specific message. It seems like there is something wrong with my Angular2 post request. Below is the function I am using for the request: load(username, pass ...

The specified type 'MutableRefObject<HTMLInputElement | undefined>' cannot be assigned to type 'LegacyRef<HTMLInputElement> | undefined'

Consider the following simplified component : const InputElement => React.forwardRef((props:any, ref) => { const handleRef = React.useRef<HTMLInputElement|undefined>() React.useImperativeHandle(ref, () => ({ setChecked(checke ...

Issue with the scoring algorithm using Angular and Spring Boot

Hello, I have created a scoring algorithm to calculate scores, but I encountered an error in "salaireNet". ERROR TypeError: Cannot read properties of null (reading 'salaireNet') at ScoringComponent.calculateScore (scoring.component.ts:33:55) ...

Can Selenium be used for testing driven by Excel spreadsheets?

With Sahi, I was able to input the required functions into a spreadsheet and have them executed. It allowed for various combinations which led to different outcomes. Is it possible to achieve something similar using selenium? I am interested in selecting a ...

Exploring the intricacies of defining Vue component props in TypeScript using Vue.extend()

Here is a simple example to illustrate my question When I directly define my props inside the component, everything works fine <script lang="ts"> import Vue, { PropType } from "vue"; export default Vue.extend({ props: { col ...

Sanity.io's selection of schema field types for efficient and convenient

Hey there, guys! I recently started using Sanity.io and I'm curious whether there's a way to enhance my code efficiency and reuse certain fields across different schemas. I had an idea that goes something like this: cars.ts: export default { ...

An uncommon token was found by Jest - import (Angular CLI 6)

Struggling to get jest installed and running in an angular/cli (v6) app on Mac. Despite trying various methods to set up jest, I keep encountering the following error: Test suite failed to run Jest encountered an unexpected token This usually me ...

When using AngularJS 2, the class identity is lost when resolving a Promise during fetching

SUMMARY: I'm encountering an issue where I am fetching Object instances instead of Org instances from my data in Angular 2. Is there a way to retrieve Org objects directly or is this the expected behavior? DETAILS: In my Angular 2 project, I have mod ...

I'm encountering a ModuleNotFoundError that says: "Module not found: Error: Can't resolve". What could be causing this

I have encountered an issue while trying to create a blog post page on my NextJS website. The page displays correctly on my local machine, but when I deploy it to production, I am facing the following error and I am unsure of how to resolve it: Here is the ...

Failed to import due to an error from the downloaded dependency

I'm encountering an import error in the @react-three module of my downloaded package. ./node_modules/@react-three/drei/node_modules/troika-three-text/dist/troika-three-text.esm.js Attempted import error: 'webgl-sdf-generator' does not cont ...

Add a npm module without type definitions

I am currently utilizing Typescript version 2.1 and facing an issue with installing an npm package called 'reactable' that lacks typings. When attempting to import the package using import * as Reactable from 'reactable', Typescript di ...

Exploring ways to retrieve a function-scoped variable from within an Angular subscribe function

Here's the scenario: I have a simple question regarding an Angular component. Inside this component, there is a function structured like this: somethingCollection: TypeSomething[] ... public deleteSomething(something: TypeSomething): void { // so ...

Implementing typing for a module that exports an instance of a particular class

Attempting to create a .d.ts file for the foo-foo-mq module has presented some challenges. Following the documentation, a 'foo-foo-mq' folder was created within the 'typings' directory at the project's root. An index.d.ts file was ...

Avoiding the use of destructuring for undefined values in JavaScript can be achieved by implementing

Upon receiving the response registryReportSettings from the server: this.getRegistrySettings(registry.Id).subscribe((registryReportSettings: { extended: ReportPropertiesRequest }) => { const { objectProperties, reportProperties, textProperties } = reg ...

Guide to leveraging clsx within nested components in React

I am currently using clsx within a React application and encountering an issue with how to utilize it when dealing with mappings and nested components. For instance: return ( <div> <button onClick={doSomething}>{isOpened ? <Component ...

Looking to display parent and child elements from a JSON object using search functionality in JavaScript or Angular

I am trying to display both parent and child from a Nested JSON data structure. Below is a sample of the JSON data: [ { "name": "India", "children": [ { "name": "D ...

Update the function's argument type signature if the current argument is a function with properties

Looking for input on a potential title change, but for now, here are the details of my specific use case: I'm currently developing a library that facilitates executing methods remotely and accessing properties across serialized boundaries like those ...

Can Angular components be used to replace a segment of a string?

Looking to integrate a tag system in Angular similar to Instagram or Twitter. Unsure of the correct approach for this task. Consider a string like: Hello #xyz how are you doing?. I aim to replace #xyz with <tag-component [input]="xyz">&l ...

Navigating in express

Here is the structure I am working with: server.ts routes/ index.ts homeRoute.ts In server.ts: let app = Express(); app.use(router); In routes/index.ts: const routes = Router(); export default function router() { routes.use('/home' ...