Learn how to set expectations on the object returned from a spied method, Jasmine

I am currently faced with setting an expectation regarding the number of times a specific function called "upsertDocument" is executed. This function is part of a DocumentClient object within a getClient method in production. Here's how it looks:

client.ts file:

getClient() {
    return new DocumentClient(...);
}

In the method I aim to unit test:

import * as clientGetter from '../clients/client'
...
methodA(){
  ...
  client = clientGetter.getClient(); 
  for (...) {
    client.upsertDocument();
  }
}

The issue arises from the fact that the DocumentClient constructor establishes a live connection with the database, which obviously isn't suitable for unit testing. My options are either to spyOn the DocumentClient constructor or the getClient method, both of which present challenges.

If I spy on the DocumentClient constructor:

unit test:

it('performs specific actions', (done: DoneFn) => {
  spyOn(DocumentClient, 'prototype').and.returnValue({
    upsertDocument: () => {}
  })

  ...
})

This results in the following error:

  Message:
    Error: <spyOn> : prototype is not declared writable or has no setter
    Usage: spyOn(<object>, <methodName>)
  Stack:
    Error: <spyOn> : prototype is not declared writable or has no setter
    Usage: spyOn(<object>, <methodName>)
        at <Jasmine>
        at UserContext.fit (C:\Users\andalal\workspace\azure-iots-saas\service-cache-management\src\test\routes\managementRoute.spec.ts:99:35)
        at <Jasmine>
        at runCallback (timers.js:810:20)
        at tryOnImmediate (timers.js:768:5)
        at processImmediate [as _immediateCallback] (timers.js:745:5)

If I spy on the getClient method and provide my own object with an upsertDocument method, I face difficulty in setting an expectation on that mock object:

it('performs specific actions', (done: DoneFn) => {
  spyOn(clientGetter, 'getClient').and.returnValue({
    upsertDocument: () => {}
  })

  methodA().then(() => {
    expect().toHaveBeenCalledTimes(3); // what do I put in expect() ??
  })
})

Answer №1

After identifying that the issue had already been tackled through an existing unit test, I decided to provide a comprehensive solution for others facing similar challenges:

Essentially, the getClient method is responsible for returning a DocumentClient with specific methods. The goal here is to avoid invoking the actual DocumentClient constructor. To achieve this, I developed a mock object that mirrored the DocumentClient interface and utilized jasmine.createSpy on each method:

export class TestableDocumentClient implements IDocumentClient {
  upsertDocument = jasmine.createSpy('upsertDocument')
  ...
}

In my unit test, I intercepted the getClient method and provided an instance of the aforementioned mock type:

let mockDocClient = new TestableDocumentClient()
spyOn(clientGetter, getClient).and.returnValue(mockDocClient);

...

expect(mockDocClient.upsertDocument).toHaveBeenCalledTimes(3);

To ensure everything was functioning as expected, I created a basic object containing only the desired method and verified its behavior through expectations, which yielded successful outcomes.

let myObj = {
  upsertDocument: jasmine.createSpy('upsertDocument')
}

spyOn(clientGetter, 'getClient').and.returnValue(myObj);

...
expect(myObj.upsertDocument).toHaveBeenCalledTimes(3);

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

The collaboration between an object literal declaration and an object instantiated through a constructor function

If I declare let bar: Bar; and set it to initialFooConfig;, is bar still considered as type Bar and an object, or does it become an object in literal notation? If the assignment can be done both ways (assuming initialFooConfig is not a constant), what set ...

Implementing Styled API in TypeScript with props: A Comprehensive Guide

I'm currently working on styling a component using the new styled API, not to be confused with StyleComponents. const FixedWidthCell = styled(TableCell)((props: { width: number }) => ({ width: props.width || 20, textAlign: "center", })) The i ...

What is the best way to prevent double clicks when using an external onClick function and an internal Link simultaneously

Encountering an issue with nextjs 13, let me explain the situation: Within a card component, there is an external div containing an internal link to navigate to a single product page. Using onClick on the external div enables it to gain focus (necessary f ...

Enforce directory organization and file naming conventions within a git repository by leveraging eslint

How can I enforce a specific naming structure for folders and subfolders? I not only want to control the styling of the names (kebab, camel), but also the actual names of the folders and files themselves. For example, consider the following paths: ./src/ ...

What steps should be followed to effectively incorporate Google Fonts into a Material UI custom theme for typography in a React/TypeScript project

Hey there, I'm currently working on incorporating Google fonts into a custom Material UI theme as the global font. However, I'm facing an issue where the font-weight setting is not being applied. It seems to only display the normal weight of 400. ...

Encountering an error in TypeScript: Attempting to define type files for a third-party library triggers the TS2709 error when attempting to use the 'Optional' namespace as a type

Currently, I'm in the process of creating type files for a third-party library called optional-js. The structure is as follows: index.js var Optional = require('./lib/optional.js'); module.exports = { empty: function empty() { ...

Using Jest in Typescript to simulate fetch responses

I am currently facing an issue with mocking the global fetch function using Jest and dealing with the Response type. Specifically, I only require the response.ok and response.json properties, but I am struggling to set the return data of fetch without spec ...

Steps for making a "confirm" button within a modal that includes a redirect URL

I have developed a modal that, upon clicking on the confirm button, should redirect the user to the page titled securities-in-portfolio. modal <div class="modal-footer justify-content-center"> <button type="button" class ...

The property '.....' is missing an initializer and has not been explicitly assigned in the constructor

I want to address an issue with a similar question title that was asked 5 years ago on Stack Overflow. The problem is related to declaring a variable as an array of a specific object type in an Angular component using TypeScript 4.9. Even though I tried t ...

What is the best way to adjust the Material Drawer width in Reactjs to match the width of its children?

Currently, I am utilizing the Material-ui Drawer component to toggle the display of content on the right side of the screen. The functionality I am aiming for is that when the Drawer opens, it will shrink the existing content on the right without any overl ...

Angular: Granting an external module access to a provider

One of the modules I imported provides a service with an optional dependency. Although it being optional didn't affect my application, as it just prevented any errors from occurring when not present. Here's an example: import { FooModule } from ...

Next.js focuses solely on rendering markup and does not run any custom code

I am in the process of creating a website, where currently the only functionality available is to change themes by selecting an option from a dropdown menu (the theme is an attribute that uses CSS variables). Everything was functioning properly, however t ...

Creating a dynamic union return type in Typescript based on input parameters

Here is a function that I've been working on: function findFirstValid(...values: any) { for (let value of values) { if (!!value) { return value; } } return undefined; } This function aims to retrieve the first ...

"What is the best way to calculate the total value of an array in TypeScript, taking into account the property

I'm currently working on a small Angular project that involves managing an array of receipt items such as Coke, Fanta, Pepsi, Juice, etc. Each receipt item has its own price and quantity listed. receiptItems: Array<ReceiptItem>; Here is the st ...

What is the best way to update this payload object?

Currently, I'm developing a route and aiming to establish a generic normalizer that can be utilized before storing user data in the database. This is the function for normalization: import { INormalizer, IPayloadIndexer } from "../../interfaces/ ...

Updating nested interface values using React hooks

I am looking to develop an application that can seamlessly update a nested configuration file after it has been imported (similar to swagger). To achieve this, I first created a JSON configuration file and then generated corresponding interfaces using the ...

webdriverIO encountered an unhandled promise rejection, resulting in a NoSuchSessionError with the message "invalid session id

I am currently learning how to conduct UI testing using Jasmine and WebdriverIO in conjunction with NodeJS. Below is a snippet of my test code: const projectsPage = require('../../lib/pages/projects.page'); const by = require('selenium-we ...

Join our mailing list for exclusive updates on Angular 6

ingredients : Array additionalIngredients : Array In my code, I have two different methods for subscribing: this.ingredients.valueChanges.subscribe(val=> { console.log(val); } this.additionalIngredients.valueChanges.subscribe(val2=> { console.lo ...

Exploring the integration of React.Components with apollo-client and TypeScript

I am in the process of creating a basic React component using apollo-client alongside TypeScript. This particular component is responsible for fetching a list of articles and displaying them. Here's the code: import * as React from 'react' ...

Clicking a button in React requires two clicks to update a boolean state by triggering the onClick event

I have a React functional component with input fields, a button, and a tooltip. The tooltip is initially disabled and should only be enabled and visible when the button is clicked and the input fields contain invalid values. The issue I'm facing is t ...