Tips for handling a jest mock function of a module and TypeScript types

When it comes to writing my testing files in TypeScript, I rely on ts-jest and jest.

I'm facing some confusion regarding how to type the mock function of a module.

Let's take a look at the code:

./module.ts:

import {IObj} from '../interfaces';

const obj: IObj = {
  getMessage() {
    return `Her name is ${this.genName()}, age is ${this.getAge()}`;
  },

  genName() {
    return 'novaline';
  },

  getAge() {
    return 26;
  }
};

export default obj;

./module.test.ts:

import * as m from './module';

describe('mock function test suites', () => {

  it('t-1', () => {
    // Unsure about the correct jest.Mock<string> type here.
    m.genName: jest.Mock<string> = jest.fn(() => 'emilie'); 
    expect(jest.isMockFunction(m.genName)).toBeTruthy();
    expect(m.genName()).toBe('emilie');
    expect(m.getMessage()).toEqual('Her name is emilie, age is 26');
    expect(m.genName).toHaveBeenCalled(); 

  });

});

How can I properly type the mock function genName of module m?

TypeScript throws an error in this line:

Error:(8, 7) TS2540:Cannot assign to 'genName' because it is a constant or a read-only property.

Answer №1

Here is my approach to tackling the same issue and how I handle mocking and spying in my current workflow.

import * as m from './module';

describe('testing scenario', () => {
  let mockGenName;

  beforeEach(() => {
    mockGenName = jest.spyOn(m, 
      'genName').mockImplementation(() => 'franc');
  })

  afterEach(() => {
    mockGenName.mockRestore();
  })


  test('description of the test case', () => {
    // perform an action that triggers the genName function
    expect(mockGenName).toHaveBeenCalledTimes(1);
  })

})

By setting it up this way, you have the flexibility to modify the mock's implementation based on different tests, validate the function calls and parameters, and reset the mock between tests and at the conclusion of all tests.

Answer №2

Check out this helpful resource - https://jestjs.io/docs/mock-function-api#typescript

To simplify, there are only three possible strategies to consider:

  1. Completely mock the imported module and access the mocked function for manipulation (jest.Mock(), jest.MockedFunction)
  2. Partially mock the imported module and access the mocked function for manipulation using a factory method (jest.Mock() with factory, jest.MockedFunction)
  3. Import the module as is and then spy on the function that needs to be mocked (jest.spy())

Answer №3

If you wish to create a mock for the module and modify the function that is exported from it, you can achieve this by using the following code snippet. This will effectively override your initial import statement.

jest.mock('./module', () => ({
    genName: jest.fn().mockImplementation(() => 'emilie')
}))

Answer №4

The reason for encountering the error:

It seems that the properties of a module object foo (import * as foo from 'foo') behave similarly to those of a frozen object.

For further details, refer to In ES6, imports are live read-only views on exported values

Solving the issue involved changing from import * as m from './module' to import m from './module';.

Package configurations:

"typescript": "^3.6.4"
"ts-jest": "^24.1.0"
"jest": "^24.9.0",

jest.config.js:

module.exports = {
  preset: 'ts-jest/presets/js-with-ts',
  //...
}

tsconfig.json:

"compilerOptions": {
    "target": "es6",
    "module": "commonjs",
    //...
}

Answer №5

import * as m from './module'

jest.mock('./module', () => ({
    genName: jest.fn().mockImplementation(() => 'emilie')
    // This will ensure that "emilie" is returned for all tests
}))

it('returns franc', () => {
  m.genName.mockImplementationOnce(() => 'franc')
  // Only for this test, "franc" will be returned 
})

Answer №6

I'm imitating a certain process like this:

jest.mock('./module')
const {genName} = require('./module')

and during my test:

 genName.mockImplementationOnce(() => 'franc')

This approach works perfectly for me without any TypeScript errors.

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

iterating over a list of files using JavaScript

I am currently working on setting up individual ajax requests for each file being uploaded. I have a functioning ajax call that creates a record, returns some html, and provides the tempID of the record which is then used in another ajax call that triggers ...

How can you identify English words along with non-English characters and special characters using JavaScript regex?

Dealing with a text that consists of English and Chinese characters can be tricky. I need to separate out the English words, foreign characters like French or Chinese, numbers, and special characters such as "@#$%^&>?", so that I can manipulate them ...

How might I structure a server reply to appear distinct?

My server query can receive two types of responses: interface Response { [id: string]: Data } or interface Response { error: string } However, I am encountering an error message that says: Property 'error' of type 'string' is no ...

Top method for retrieving CSS variable in a customized Angular directive

I have a question regarding the process of converting the statement below to Angular's renderer2: this.elementRef.nativeElement.style.setProperty( '--primary-color ' , '#455363' ) The above statement modifies a CSS variable in the ...

Steps for creating a TypeScript project with React Native

Hey there, I'm just starting out with react-native and I want to work on a project using VS Code. I'm familiar with initializing a project using the command "react-native init ProjectName", but it seems to generate files with a .js extension inst ...

My application built with React and Flask successfully processes JSON data on one route, but encounters issues on another route

The code I have in place is working quite well, with the frontend being the next area of focus. This code effectively registers a user and updates the database: export default class APIService { static RegisterUser(username, email, password, base_city, ...

Incorporate Angular frontend into current website or project

I've successfully built a website using bootstrap, but now I'm looking to integrate Angular in order to transform it into a single page application. The goal is that when a user clicks on a link, only the necessary content will be loaded from the ...

Using Wordpress and JavaScript to dynamically hide a button if a product in the online store does not have an SKU

I'm encountering an issue on my Wordpress site where products with variations are not displaying the inner text on a certain element, despite the fact that the text is present when I inspect the element. Here's the code: const makerBtn = document ...

The reason Typescript is able to accurately determine the value when providing a Generics that extends specific types

Exploring Different Generics in TypeScript: When using generics in TypeScript, the type of data you receive can vary based on how you define your functions. // Example with string generic type function getResult<T>(...v: T[]) { return v } const s ...

JavaScript event listener on the "change" event only triggers when changed manually [CodePen]

Check out this jsFiddle I created with all the data and information related to the issue. It should make it easier to understand what's happening: Take a look here: http://jsfiddle.net/lukinhasb/GuZq2/ $("#estado").val(unescape(resultadoCEP["uf"])); ...

What is the best way to test chained function calls using sinon?

Here is the code I am currently testing: obj.getTimeSent().getTime(); In this snippet, obj.getTimeSent() returns a Date object, followed by calling the getTime() method on that Date. My attempt to stub this functionality looked like this: const timeStu ...

Guide on setting up Express.js to log errors during asynchronous operations

I encountered an issue with my code that goes like this: exports.listSavedThreads = function (req, res) { SavedThread.find({}).exec().then(function (data) { wat.map(); res.render('home/listSavedThreads'); }); }; It seems that the va ...

PHP and MySQL collaborate for live countdowns in real-time

Looking for a code solution to implement a real-time countdown feature, similar to deal expiration timers on shopping websites. I have an expiry time stored in MySQL which needs to be displayed whenever the page is accessed. Additionally, it should calcul ...

Step-by-step guide on implementing a see-more/read-more feature using only anchors in the HTML

I am currently developing a website that will be managed by individuals who are not very tech-savvy. I want to empower them with the ability to add "see-more" anchors that utilize jQuery slide up/down functionality to reveal content. While my code works w ...

Exploring the methods to update axios request configuration

Whenever a request is made to the backend, an access token is sent along with it. If the token fails verification, the original request configuration is saved and a new request is made to update the tokens. If the verification is successful, the original ...

What is the best way to align a <div> element below another without being on the same line?

I'm currently working on developing a snake game. My focus right now is figuring out how to make the body parts of the snake follow the head and be positioned after it. <!--Player--> <div class="snake"></div> So here we have the sn ...

embedding js files into html with node.js

When it comes to messaging via websockets between a HTML5 client and server running on node.js, I decided to use JSON as the message format. To streamline this process, I created common javascript code that defines different message content types and trans ...

"Failure to update the $scope object within an AngularJS service results in no changes being reflected in

I am currently working on integrating Google maps into an Angular / IonicFramework project. The implementation involves a directive, a service, and a controller. Within the scope ($scope), I have objects for the map, marker, and geolocation. While the map ...

transmitting error messages from a service to a controller in AngularJS

Controller.js var vm = this; vm.admin = {}; vm.add = function () { API.addAdmin(token, vm.admin) .then(function (resp) { vm.hideForm = true; vm.showButton = true; Notify.green(resp); }, function (re ...

New to JSON: Why is my variable returning as undefined?

Embarking on my first JSON project has brought some challenges that I am struggling to overcome. Here's the task at hand. I have created a basic scraper using Apify.com to extract data from a specific website. The data is presented in JSON format, an ...