Is it possible to customize webpack 4 modules in order to enable Jasmine to spy on their properties?

I've been facing issues trying to run my Jasmine test suite with webpack 4. Ever since I upgraded webpack, I keep getting this error message in almost every test:

Error: <spyOn> : getField is not declared writable or has no setter 

The problem seems to stem from a common pattern we follow for creating spies on simple functions, like so:

import * as mod from 'my/module';
//...
const funcSpy = spyOn(mod, 'myFunc');

I have experimented with module.rules[].type, but none of the available options seem to solve the issue.

According to this webpack GitHub issue, ECMA modules are designed to be non-writable, which makes sense for web usage. But is there really no workaround for running tests?

Here are the relevant package versions:

"jasmine-core": "2.6.4",
"typescript": "2.5.3",
"webpack": "4.1.1",
"webpack-cli": "^2.0.12",
"karma": "^0.13.22",
"karma-jasmine": "^1.1.0",
"karma-webpack": "^2.0.13",

Answer №1

If you want to make a property read-only using the spyOnProperty method, simply set the accessType argument to 'get'.

To implement this, your code would resemble the following:

import * as example from 'my/example';
//...
const mockFunc = jasmine.createSpy('mockFunction').and.returnValue('mockValue');
spyOnProperty(example, 'myFunc', 'get').and.returnValue(mockFunc);

Answer №2

There's a discussion on this GitHub thread where the consensus is that immutable exports are the desired outcome. However, user lavelle shared a interesting workaround (in this specific comment) where they opted to create separate webpack configurations for testing and production code. In their test config, they utilized "commonjs" modules which seemed to prevent the creation of getters.

Answer №3

Expanding on @Anton Poznyakovskiy's response:

In order to streamline my testing process, I introduced this TypeScript function to my shared testing module:

export const mockFunction = <T>(obj: T, func: keyof T) => {
  const spy = jasmine.createSpy(func as string);
  spyOnProperty(obj, func, 'get').and.returnValue(spy);

  return spy;
};

Here is an example of how to use it:

import * as myModule from 'my/module';
//...
mockFunction(myModule, 'myFunc').and.returnValue('myMockReturnValue');

Answer №4

One way to solve this issue is by encapsulating the methods in a custom class and then mocking it.

See an example below:

//Custom Class

import * as library from './myLibrary'

export class MethodWrapper{
    static _function = library.function;
}

//Main File
import { MethodWrapper } from './util/moduleWrapper';

const result = MethodWrapper._function(argument1, argument2);

//Testing File

import { MethodWrapper } from './util/moduleWrapper';

spyOn(MethodWrapper, '_function').and.returnValue('mockedOutcome');

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

Breaking down large reducer into smaller reducers

I am currently working on a feature reducer (slice reducer) called animals. My goal is to separate these reducers into categories such as mammals, birds, fishes, and more. Initially, I thought this would be a smooth process using the ActionReducerMap. How ...

Aurelia's navigation feature adds "?id=5" to the URL instead of "/5"

I have set up my Aurelia Router in app.ts using the configureRouter function like this: configureRouter(config, router: Router) { config.map([ { route: ['users', 'users/:userId?'], na ...

I am facing an issue with my useFetch hook causing excessive re-renders

I'm currently working on abstracting my fetch function into a custom hook for my Expo React Native application. The goal is to enable the fetch function to handle POST requests. Initially, I attempted to utilize and modify the useHook() effect availab ...

The error message "TypeError: 'results.length' is not an object" occurred within the Search Component during evaluation

Having trouble with a search feature that is supposed to display results from /api/nextSearch.tsx. However, whenever I input a query into the search box, I keep getting the error message TypeError: undefined is not an object (evaluating 'results.lengt ...

Using sl-vue-tree with vue-cli3.1 on internet explorer 11

Hello, I am a Japanese individual and my proficiency in English is lacking, so please bear with me. Currently, I am using vue-cli3.1 and I am looking to incorporate the sl-vue-tree module into my project for compatibility with ie11. The documentation menti ...

What is the significance of the colon before the params list in Typescript?

Consider the following code snippet: import React, { FC } from "react"; type GreetingProps = { name: string; } const Greeting:FC<GreetingProps> = ({ name }) => { // name is string! return <h1>Hello {name}</h1> }; Wha ...

How to pass props to customize styles in MUI5 using TypeScript

Currently, I'm in the process of migrating my MUI4 code to MUI5. In my MUI4 implementation, I have: import { createStyles, makeStyles } from '@material-ui/core'; import { Theme } from '@material-ui/core/styles/createMuiTheme'; ty ...

Error TS2403: All variable declarations following the initial declaration must be of the same type in a React project

While developing my application using Reactjs, I encountered an error upon running it. The error message states: Subsequent variable declarations must have the same type. Variable 'WebGL2RenderingContext' must be of type '{ new (): WebGL2 ...

Removing the path from import filenames in React during NPM build: A step-by-step guide

In order to maintain organization in my project, I prefer to store my files in structured folders. For example, having an Auth folder for authentication-related components, a Components folder for reusable components, and a Profile folder for user profile- ...

What is the significance of var-less variables in TypeScript class definitions?

Why is it that when creating a component class in Angular2, we don't need to use var when declaring a new variable? For example: @Component({ selector: 'my-app', template: ` <h1>{{title}}</h1> ` }) export class AppCo ...

What is the reason behind TypeScript indicating that `'string' cannot be assigned to the type 'RequestMode'`?

I'm attempting to utilize the Fetch API in TypeScript, but I keep encountering an issue The error message reads: Type 'string' is not assignable to type 'RequestMode'. Below is the code snippet causing the problem export class ...

Images in the Ionic app are failing to display certain asset files

Currently, I am working on an Ionic 4 app for both Android and iOS platforms. The issue I am facing is that only SVG format images are displaying in the slide menu, even though I have images in both SVG and PNG formats. public appPages = [ { ...

Learn the method for triggering events with a strongly-typed payload in Vue 3 Composition API and TypeScript

I'm currently exploring Vue 3 Composition API along with TypeScript, particularly focusing on emitting events with a strictly typed payload. There's an example provided below, but I'm unsure if it's the most effective way to achieve t ...

I'm trying to figure out which one is the correct term on Ubuntu - is it "node" or "nodejs"? And

Trying to install webpack-dev-server but it requires the latest version of nodejs. I am using Ubuntu 20.04 and attempted to update with nvm, which did not work. Following this Q&A answer here, I then tried to install nodejs using sudo apt-get install ...

Creating a backup link for a video player component in NextJs

My aim is to make sure that two video player components in a NextJS application can still access and play videos even when the application is running locally using npm run dev without an internet connection. Currently, these two components.. <HoverVi ...

The Ionic project compilation was unsuccessful

Issue: This module is defined using 'export =', and can only be utilized with a default import if the 'allowSyntheticDefaultImports' flag is enabled. Error found in the file: 1 import FormData from "form-data"; ~~~~~~~~ node ...

The useForm function from react-hook-form is triggered each time a page is routed in Nextjs

Hey there, I'm just starting out with Next.js (v14) and I'm trying to create a multi-page form using react-hook-form. But I'm encountering an issue where the useForm function is being executed every time, and the defaultValues are being set ...

The type 'true | CallableFunction' does not have any callable constituents

The error message in full: This expression is not callable. No constituent of type 'true | CallableFunction' is callable Here is the portion of code causing the error: public static base( text, callB: boolean | CallableFunction = false ...

JavaScript - Modifying several object properties within an array of objects

I am attempting to update the values of multiple objects within an array of objects. // Using a for..of loop with variable i to access the second array and retrieve values const AntraegeListe = new Array(); for (let i = 0; i < MESRForm.MitarbeiterL ...

Increase the size of the NativeScript switch component

Here is the code I am working with: .HTML <Switch style="margin-top: 10" (checkedChange)="onFirstChecked1($event)" row="0" col="1" horizontalAlignment="center" class="m-15 firstSwitchStyle"></Switch> .CSS .firstSwitchStyle{ width: 30%; ...