Creating a spy object in Jest just got easier with the `

If you want to create a spy object with Chai, you can do so by using the following syntax:

chai.spy.object([ 'push', 'pop' ]);

For jasmine users, the equivalent code would be:

jasmine.createSpyObj('tape', ['play', 'pause', 'stop', 'rewind']);

What is the Jest alternative for this functionality?

Situation: I am currently in the process of transitioning my TypeScript Jasmine tests to TypeScript Jest. However, the migration guide provided seems to be of little help in this particular case. As with many new technologies, it is challenging to find detailed information in the documentation.

Answer №1

I have developed a new utility function called createSpyObj specifically for jest testing in an older project. This function is inspired by Jasmine's implementation and aims to provide similar functionality.

const createSpyObj = (baseName, methodNames): { [key: string]: Mock<any> } => {
    let obj: any = {};

    for (let i = 0; i < methodNames.length; i++) {
        obj[methodNames[i]] = jest.fn();
    }

    return obj;
};

Answer №2

const player = {
  start() {
    return true;
  },
};

module.exports = player;

Here is the testing code:

const player = require('./player');

test('starts player', () => {
  const spy = jest.spyOn(player, 'start');
  const isStarting = player.start();

  expect(spy).toHaveBeenCalled();
  expect(isStarting).toBe(true);

  spy.mockReset();
  spy.mockRestore();
});

For more information, check out: https://example.com/jest/docs/en/player-object.html#jestspyonobject-methodname

You can also use jest.fn()

const mockFunction = jest.fn();
  mockFunction();
  expect(mockFunction).toHaveBeenCalled();

  // With a custom implementation:
  const returnsTrue = jest.fn(() => true);
  console.log(returnsTrue()); // true;

https://example.com/jest/docs/en/player-object.html#jestfnimplementation

Answer №3

After taking inspiration from @Max Millington's response, I was able to devise a solution utilizing the jest.fn() method:

  1. To begin, create a mocked object:

const mockObject: any = {};

  1. Add the necessary method(s) to the mocked object:

mockObject['run'] = jest.fn();

  1. You have the option to spyOn the mocked methods, but first you must assign them to a real object. In my case, I am using the component instance:

    // GIVEN
    comp.actualMock = mockObject
    
    // WHEN
    spyOn(comp.actualMock, 'run')
    comp.procedure()
    
    // THEN
    expect(comp.actualMock.run).toHaveBeenCalledTimes(1)
    

Answer №4

Shoutout to @David for the helpful response!

Even in this day and age, finding a ready-made solution for this issue can be quite challenging. I made some tweaks to your example as the basename wasn't utilized. When incorporating this object into my tests, I found myself constantly having to cast it as unknown, which wasn't ideal.

export const createSpyObj = <T>(methodNames): { [key: string]:jest.Mock<any> } & { spyObj: T } => {

  const obj: { [key: string]: jest.Mock<any> } & { spyObj: T } = { 
spyObj: null } as any;

  for (let i = 0; i < methodNames.length; i++) {
     obj[methodNames[i]] = jest.fn();
  }

  obj.spyObj= obj as T;
  return obj;
};

By implementing this code, I am able to seamlessly pass the spyObj into the constructor of the Service under test.

const injectedService = createSpyObj<InjectedService>(['something']);

injectedService.something.mockReturnValue(true);

const serviceToBeTested = new ServiceToBeTested(injectedService.spyObj);
expect(injectedService.something).toBeCalled();

A potential drawback is that intellisense no longer functions for methodNames.

Answer №5

Emily's guidance steered me in the right direction. I adapted it to suit the needs of my Angular4/Ionic3 project by incorporating ionic-mocks (https://github.com/user/ionic-mocks).

Within my testing "helper" class, I implemented the following logic:

export function createCustomSpy(baseName: string, methodNames: string[]): { [key: string]: jasmine.Spy } {
  const obj: any = {}
  for (let i: number = 0; i < methodNames.length; i++) {
    obj[methodNames[i]] = jasmine.createSpy(baseName, () => {})
  }
  return obj
}

Subsequently, I utilized it within my test/spec file by injecting the relevant provider like so:

{ provide: AlertController, useFactory: () => AlertControllerMock.instance() },

Due to Jest's lack of compatibility with ionic-mocks, I had to manually transfer the mocks (utilizing createCustomSpy):

class ToastMock {
  public static initiate(): any {
    const instance: any = createCustomSpy('Toast', ['present', 'dismiss'])
    instance.present.and.returnValue(Promise.resolve())
    instance.dismiss.and.returnValue(Promise.resolve())

    return instance
  }
}

class ToastControllerMock {
  public static initiate(toastMock?: ToastMock): any {

    const instance: any = createCustomSpy('ToastController', ['create'])
    instance.create.and.returnValue(toastMock || ToastMock.initiate())

    return instance
  }
}

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

Troubleshooting issue with jest expect.any() failing to work with a custom class following migration from JavaScript to TypeScript

I recently made the switch to TypeScript in my project, and now some of my Jest tests are failing. It appears that the next function below is no longer being called with an AppError object, but with an Error object instead. Previously, the assertion expec ...

Trouble with Displaying Events on React Big Calendar with Typescript

Struggling to implement React Big Calendar with TypeScript. Managed to get the calendar to display correctly after adjusting the height, but unable to show any events. The array of events is populating as expected, and I modified the code for TypeScript co ...

Typescript: How can we determine the data type of React Router Link?

Trying to pass Link from react-router-dom as props and needing to specify it in the interface. While hovering over the Link element, the type displayed is: React.ForwardRefExoticComponent<LinkProps & React.RefAttributes<HTMLAnchorElement>> ...

Can Angular 4 experience race conditions?

Here is a snippet of my Angular 4 Service code: @Injectable() export class MyService { private myArray: string[] = []; constructor() { } private calculate(result): void { myArray.length = 0; // Perform calculations and add results to myAr ...

Running 'npm test' is successful, however the 'jest --coverage' command fails to execute

In my MonoRepo project (using Lerna), I have multiple packages, with one being a React application. For unit testing in the React project package, I rely on Jest. However, when I try to execute the jest --coverage command in the WebStorm console, it shows ...

Data retrieved from API not displaying in Angular Material table

I've hit a roadblock trying to understand why my mat-table isn't displaying the data for me. I'm working with Angular 15 and using Angular Material 15. Below is my HTML component code: <mat-divider></mat-divider> <table mat-t ...

Angular 2 Custom Pipe Magic

I'm brand new to using the Angular 2 framework and I'm attempting to create a custom filter. app.component.ts import {Component} from 'angular2/core'; import {HTTP_PROVIDERS} from 'angular2/http'; @Component({ selector: ...

What is the method to have VIM recognize backticks as quotes?

Currently working in TypeScript, I am hoping to utilize commands such as ciq for modifying the inner content of a template literal. However, it appears that the q component of the command only recognizes single and double quotation marks as acceptable ch ...

How to use sinon to create a mock for an independently imported function

Is there a way to successfully mock the axios import using sinon and then set expectations? Here is my attempted code: import axios from 'axios'; axiosMock = sinon.mock(axios); However, the expectation does not pass: describe('Custom test ...

Tips for accessing a specific ListItem within the Menu Component using MUI for React

Within my code, I am working with a List that contains multiple ListItems pulled from an array called myCollection. Each ListItem has a MenuIcon element which triggers a menu to appear, providing the option to delete the specific item. Here is a simplified ...

The aesthetic of the material tree design is not being reflected as expected

I am attempting to recreate the material tree example showcased here. This is the desired outcome: https://i.sstatic.net/dnkm2.png However, my result appears like this: https://i.sstatic.net/JXdbo.png Below is the HTML code I am utilizing: <mat-tr ...

After updating from angular4 to angular 5, the npm test is failing with the error message "TypeScript compilation cannot find test.ts file"

After upgrading my app from Angular4 to Angular 5 using the steps provided on https://update.angular.io/, I encountered an issue. While I can successfully run ng-serve and ng build without any problems, the npm test command for ng test is failing with the ...

Is there a way for me to implement a "view more posts" button on

I need help figuring out how to hide the "Show More" button when there are no posts. I have created a showLoad function and an isLoad state variable, but I'm unsure of how to implement this. The button display logic is dependent on the isLoad state. ...

Determine if all resources on a page in Angular 4 have finished loading by keeping a loader spinning until everything is fully loaded

As part of my work on an Angular app, I am developing a loader to enhance user experience. While the typical approach involves utilizing a boolean parameter for subscribing to HTTP requests, in my case, the service's response consists of multiple ima ...

Testing the retrieval of a file using Jest in an express controller

I recently implemented a basic Express endpoint for users to download a csv file. Now, I'm looking to write a test using Jest specifically for this file download endpoint. However, I'm a bit uncertain about which function or mock I should utiliz ...

What's the best way to refactor the `await nextEvent(element, 'mousemove')` pattern in my code once it is no longer necessary?

Within my React component, the code includes the following: class MyComponent extends React.Component { // ... trackStats = false componentDidMount() { this.monitorActivity() } componentWillUnmount() { this.trackStat ...

"What is the best way to determine the data type of an object retrieved from an API in TypeScript

Hey there, I'm currently developing a web application using Angular 2 and I'm focusing on implementing an exception handling mechanism. To achieve this, I've created a model that mirrors the object structure I will receive from the server (E ...

Debug a Typescript-enabled Koa application remotely from a Docker container using Webstorm

Issue: Currently facing challenges while setting up a new NodeJS Project using KoaJS, Typescript, and Docker. The initial setup went as planned, but encountering some difficulties with remote debugging - or at least it seems so from my perspective. When s ...

Global Inertia Headers

How can I ensure that a custom header (Accept-Content-Language) is sent with every request, including Inertia manual visits? Below is the code snippet where I define and set the header: import axios from 'axios'; const lang = localStorage.getIt ...

Struggling with the TypeScript generic syntax for the GroupBy function

Struggling to figure out where I'm going wrong with this TypeScript signature after spending some time on it. I've been working on a group by function: const group = <T>(items: T[], fn: (item: T) => T[keyof T]) => { return items.re ...