Monitor the behavior of a function that is provided as an argument to a simulated node package

Currently, I am in the process of creating a custom client that integrates the robust SignalR library:

export class RealTimeCommunicator {

  /** SignalR hub connection */
  private hubConnection: HubConnection | null = null;

  private buildConnection(url: string): HubConnection {
    return new HubConnectionBuilder()
    .withUrl(url)
    .build();
  }

  public subscribe(onStart: () => void, success: (data: IVehicleInfo) => void, fail?: (err: Error) => void): () => void {
    this.hubConnection = this.buildConnection("/someurl");
    this.hubConnection.on("send", data => {
      success(data);
    });

    this.hubConnection.start()
      .then(onStart)
      .catch(err => fail && fail(err));

    return this.unsubscribe;
  }

  public subscribeToVehicleDetails(vehicleId: number, success: (data: IVehicleInfo) => void, fail?: (err: Error) => void): () => void {
    return this.subscribe(() => this.hubConnection!.invoke("EnrolInVehicleGroupAsync", vehicleId), success, fail);
  }

To test this functionality, I need to mock out SignalR, so I have introduced __mocks__/@aspnet/signalr.ts into the codebase:

let SignalR = jest.genMockFromModule("@aspnet/signalr");

class HubConnection {

  public on(methodName: string, callback: () => string): Promise<void> {
    this.successCallback = callback;
    return Promise.resolve()
  }

  public start(): Promise<void> { return Promise.resolve() }

  public successCallback: () => string = () => "failure";
}

let hubConnection = new HubConnection();

let HubConnectionBuilder = () => ({
  withUrl: (url) => ({
    build: () => hubConnection
  })
});

SignalR = {
  HubConnectionBuilder: HubConnectionBuilder,
  HubConnection: hubConnection
};

module.exports = SignalR;

In an effort to validate the functionality, I am trying to spy on the on method to ensure its invocation, and ideally confirm the correct registration of my success handler:

import { RealTimeCommunicator } from "./RealTimeCommunicator";
jest.mock('SignalR');

describe("RTC", () => {
  test("foo", () => {
    const ConnectionBuilder = require("SignalR").HubConnectionBuilder;

    const connection = new ConnectionBuilder().withUrl("").build();

    const spy = jest.spyOn(connection, 'on');
    expect(spy).toHaveBeenCalled();  //Expectation fails
    const successText = "success!";
    const successCallBack: () => string = () => successText;
    const rtc = new RealTimeCommunicator();

    rtc.subscribeToVehicleDetails(1, successCallBack, _ => _);
    expect(connection.successCallback()).toBe(successText); // connection.successCallback is undefined
  })
});

While debugging the SignalR mock implementation, I can confirm that the on method is called; however, the spy mechanism seems unable to detect it. Furthermore, the successCallback addition to my mock appears as undefined in the test scenario.

Answer ā„–1

Could you share a working example (maybe a repository) to replicate the issue you're facing? From my observation, there seems to be some errors in the code snippets provided.

Firstly, why does jest.mock('SignalR'); work as intended? It seems like we should be mocking the module being imported instead: jest.mock('@aspnet/signalr');. Also, the initial part of your test appears to be testing a third-party library - is there a specific reason for this other than verifying the mock functionality?

Secondly, you're verifying the call to the on method immediately after creating the spy. However, the spy only captures calls made after its creation. Perhaps you should consider setting the on method as a spy by default:

// in __mocks__/@aspnet/signalr.ts

class HubConnection {

  public on = jest.fn().mockImplementation((methodName: string, callback: () => string) => {
    this.successCallback = callback;
    return Promise.resolve()
  });


  // ...

}

Lastly, the connection instance created in the test is distinct from the one initialized later with subscribeToVehicleDetails, resulting in the successCallback still returning "failure" if it were functional.

Answer ā„–2

Almost there, just two tweaks needed.


Retrieve the outcome of success(data):

public subscribe(onStart: () => void, success: (data: IVehicleInfo) => void, fail?: (err: Error) => void): () => void {
  this.hubConnection = this.buildConnection("/someurl");
  this.hubConnection.on("send", data => {
    return success(data);   // get the result
  });

  this.hubConnection.start()
    .then(onStart)
    .catch(err => fail && fail(err));

  return this.unsubscribe;
}

Confirm that spy is invoked only after calling rtc.subscribeToVehicleDetails:

const spy = jest.spyOn(connection, 'on');
const successText = "success!";
const successCallBack: () => string = () => successText;
const rtc = new RealTimeCommunicator();

rtc.subscribeToVehicleDetails(1, successCallBack, _ => _);
expect(spy).toHaveBeenCalled();   // SUCCESS
expect(connection.successCallback()).toBe(successText);   // SUCCESS

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

Oops! Issue encountered while trying to read the file "src/core/database/config.ts"

Need help with migrating a database in a Node Nest.JS application. When running the npx sequelize-cli db:migrate shell command, I encountered the following exception: Error details: Error: TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".t ...

Angular2 app fails to update after emitting an event

I need help with a child component responsible for updating phone numbers on a webpage. The goal is for the application to automatically display the changed phone number once the user hits the 'save' button. Here's a visual of how the appli ...

Transforming an ordinary JavaScript object into a class instance

As I was delving into Angular's documentation on "Interacting with backend services using HTTP", I came across the following statement in the "Requesting a typed response" section: ...because the response is a plain object that cannot be automatical ...

Tips for Successfully Transmitting Information via Mat-Dialog

Having trouble passing data from a dialog back to the parent component. Specifically, I'm struggling with updating the value of an array in the `afterClosed` function. I've tried using `patchValue` and `setValue`, but it doesn't seem to be w ...

Tips for effectively managing 404 errors in Angular 10 with modular routing

I'm facing challenges with handling 404 pages within an Angular 10 application that utilizes modular routing architecture. Here is the structure of my code: |> app |-- app.module.ts |-- app-routing.module.ts |-- app.component{ts, spec.ts, scss, ht ...

Tips for running Typescript files on a server in the background

I am currently working on a NodeJS application that consists solely of TypeScript files, without any JavaScript files. I'd like to run it on my server in the background. Any suggestions on how I can achieve this? I experimented with using the npm pa ...

Returning a 'never' type from a function in React using Typescript

Basically, I have a function that initiates the OAuth flow (redirecting to Google OAuth login page, for example): async function signIn() { // start OAuth flow } And let's say I want to use it in a useEffect hook like this: ... useEffect(() => { ...

Exploring Angular Testing: Unraveling Chained HTTP Requests with the Power of rxjs

I've been attempting to write unit tests for a function in my service that carries out a POST request followed by a GET request. I'm using switchMap to handle this, but running into an issue where the HttpTestingController match function isn&apos ...

Establish HTTP headers for accessing the Oxford API in an Angular 6 application

public performAutocomplete(wordInput):any { let headersOptions = { headers:{ 'Accept': 'application/json', 'app_id': 'myid', "app_key": "mykey" } as any } this.wordTyped = wordInp ...

The value of type 'number' cannot be assigned to type 'string | undefined'

Having an issue with the src attribute. I am trying to display an image on my website using the id number from an API, but when I attempt to do so, it gives me an error in the terminal saying 'Type 'number' is not assignable to type 'st ...

Transforming the timestamp to a date object using Angular and Typescript

As a newcomer to Angular 2.0, I've been delving into new concepts in order to grasp it better. However, despite encountering this common issue multiple times and reading through various solutions, I haven't been able to find the answer to my prob ...

TypeError: describe is not a function in the Mocha testing framework

Encountering an issue with mocha-typescript throwing an error indicating that describe is not defined. TypeError: mocha_typescript_1.describe is not a function at DatabaseTest.WrongPath (test/database_test.ts:21:9) at Context.<anonymous> ...

What is the most efficient method for line wrapping in the react className attribute while utilizing Tailwind CSS with minimal impact on performance?

Is there a more efficient way to structure the className attribute when utilizing tailwind css? Which of the examples provided would have the least impact on performance? If I were to use an array for the classes and then join them together as shown in e ...

The compiler error TS2304 is indicating that it cannot locate the declaration for the term 'OnInit'

I successfully completed the Angular superhero tutorial and everything was functioning properly. However, when I close the CMD window running NPM and then reopen a new CMD window to reissue the NPM START command, I encounter two errors: src/app/DashBoard ...

What are the appropriate Typescript typings for React Components that have the ability to return a string or their child components directly?

What are the suitable types for a React Component that can also output a string or directly its children, in addition to a JSX.Element? For example: type PropsStringExample = Readonly<{ returnString: boolean; }>; type PropsChildrenExample = Readon ...

Refreshing the page does not trigger Angular callHooks

Encountering an issue with the proper invocation of F5 button or directive call URL from the address bar resulting in ngOnInit not being triggered correctly. Using a debugger to analyze the situation, it was noticed that callHook is not initiated after ngO ...

Error: It is not possible to modify the paths property of the compilerOptions object in the tsconfig file because it is read-only

**Attempting to configure tsconfig.json to set the path for the compiler to recognize src as the baseUrl in a React TypeScript project** { "compilerOptions": { "target": "es5", "baseUrl": "src" ...

The slice() method in arrays provides a reference to the elements rather than copying

In my file, I am exporting an object in the following manner: export const LINECHART2_DATA = { series: [{ data: [], name: 'HR', }, { etc... }] } The way I import it is like this: import { LINECHART2_DAT ...

Utilizing asynchronous methods within setup() in @vue-composition

<script lang="ts"> import { createComponent } from "@vue/composition-api"; import { SplashPage } from "../../lib/vue-viewmodels"; export default createComponent({ async setup(props, context) { await SplashPage.init(2000, context.root.$router, ...

Encountering a tslint issue: "Parsing error: Expression expected"

Could someone provide some insight on this issue? Iā€™m encountering an error message that says Failed to compile. Parsing error: Expression expected with this specific line of code - isLogViewVisible: dashboard?.logView !== null where the variable isLog ...