Exporting keys of objects one by one

I am trying to mock the `fs` module in vitest using [memfs](https://github.com/streamich/memfs). To do this, I have created a mock file located at `./__mocks__/fs.ts` where I have set up the mocked volume and fs.

However, I am facing an issue with the mocked export not working correctly. Every time I try to use it, I receive a `TypeError: readdirSync is not a function` message.

Can someone guide me on the correct way to export this properly?

`__mocks_/fs.ts`:

// fs.ts
import { createFsFromVolume, Volume } from "memfs"

const musicFolder = "./musicFolder"
const files = {
  "./1.mp3": "1",
  "./2.mp3": "2",
}

const volume = Volume.fromJSON(files, musicFolder)

const fs = createFsFromVolume(volume)

export { fs } // This does not work

`Test file`:

// test.spec.ts
import { describe, expect, it, vi } from "vitest"
import * as fs from "fs"

vi.mock("fs")

it("testing fs", async () => {
  const files = fs.readdirSync("./musicFolder") //! TypeError: readdirSync is not a function

  expect(files).toBeTruthy()
})

Answer №1

If you've encountered the issue of importing an entire module's contents under a namespace and the need to explicitly define each export by name, using ES modules provides no workaround.

In my opinion, the challenge arises from the mix of implicit behavior from 'vitest' and explicit behavior in your code. Personally, I find it easier to understand code when it is explicit. Consider this alternative approach to the 'vi.mock' method:

Let's assume the module you are testing is located at './src/example.ts'.

Mocking the Module

Create a mock module alongside your test module with the following structure:

Following the convention of '[name].test.mock.[ext]'

// ./src/example.test.mock.ts

import { createFsFromVolume, type IFs, Volume } from 'memfs';

// Mock factory to produce the simulated fs
export function createFs (): IFs {
  const files = {
    './1.mp3': '1',
    './2.mp3': '2',
  };

  const dir = './musicFolder';
  const volume = Volume.fromJSON(files, dir);
  return createFsFromVolume(volume);
}

// Alternatively, directly export an instance if it's the only one needed
export const fs = createFs();

Testing Module

Adhering to the naming convention of '[name].test.[ext]'

Here are two ways to implement it:

First approach

// ./src/example.test.ts

import { expect, it } from 'vitest';

// If the actual `fs` from Node is required elsewhere in your test,
// maintain this import.
import * as fs from 'node:fs';

import { createFs } from './example.test.mock.js';

it('testing fs', async () => {
  const fs = createFs();
  const files = fs.readdirSync('./musicFolder');
  expect(files).toBeTruthy();
});

// Utilize the real `fs` in other test scenarios...

Second approach

If the actual fs is not needed in your test, utilize the mocked version directly:

// ./src/example.test.ts

import { expect, it } from 'vitest';
import { fs } from './example.test.mock.js';

it('testing fs', async () => {
  const files = fs.readdirSync('./musicFolder');
  expect(files).toBeTruthy();
});

Conclusion

Both approaches to the test run smoothly without errors and pass successfully:

so-72860426 % node --version
v16.15.1

so-72860426 % npm run test

> <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="295a46041e1b111f191d1b1f691907180719">[email protected]</a> test
> vitest


 DEV  v0.17.0 /stack-overflow/so-72860426

 ✓ src/example.test.ts (1)

Test Files  1 passed (1)
     Tests  1 passed (1)
      Time  906ms (in thread 34ms, 2666.13%)


 PASS  Waiting for file changes...
       press h to show help, press q to quit

Here are the repository configuration files for replicating this setup:

./package.json:

{
  "name": "so-72860426",
  "version": "0.1.0",
  "description": "",
  "type": "module",
  "scripts": {
    "test": "vitest"
  },
  "keywords": [],
  "author": "",
  "license": "MIT",
  "devDependencies": {
    "@types/node": "^18.0.1",
    "typescript": "^4.7.4",
    "vitest": "^0.17.0"
  },
  "dependencies": {
    "memfs": "^3.4.7"
  }
}

./tsconfig.json:

{
  "compilerOptions": {
    "esModuleInterop": true,
    "exactOptionalPropertyTypes": true,
    "isolatedModules": true,
    "lib": [
      "esnext"
    ],
    // "jsx": "react-jsx",
    "module": "esnext",
    "moduleResolution": "nodenext",
    "noUncheckedIndexedAccess": true,
    "strict": true,
    "target": "esnext",
    "useUnknownInCatchVariables": true
  },
  "include": [
    "src/**/*"
  ]
}

Answer №2

Make sure to have a default export as specified in .

__mocks_/fs.ts:

import { createFsFromVolume, Volume } from "memfs"

const musicFolder = "./musicFolder"
const files = {
  "./1.mp3": "1",
  "./2.mp3": "2",
}

const volume = Volume.fromJSON(files, musicFolder)

const fs = createFsFromVolume(volume)

export default fs

It is important to take caution when running multiple tests that require a new volume each time.

An effective approach I have discovered is to utilize a dynamic import and reset the module before each test as explained in :

import { describe, expect, it, vi } from "vitest"

vi.mock("fs")

beforeEach(() => {
  vi.resetModules()
})

it("testing fs", async () => {
  const fs = (await import("fs")).default

  const files = fs.readdirSync("./musicFolder")

  expect(files).toBeTruthy()
})

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

How can I tally the frequency of characters in a given string using Javascript and output them as numerical values?

I am in the process of tallying the frequency of each individual character within a given string and representing them as numbers. For example, let's consider the string "HelloWorld". HELLOWORLD There is one H - so 1 should be displayed with H remov ...

"Experiencing sluggish performance with VSCode while using TypeScript and Styled Components

My experience with vscode's type-checking is frustratingly slow, especially when I am using styled components. I have tried searching for a solution multiple times, but have only come across similar issues on GitHub. I attempted to read and understa ...

The $http service factory in Angular is causing a duplication of calls

After creating an angular factory that utilizes the $http service, I encountered a problem where the HTTP request is being made twice when checking the network tab in the browser. Factory: app.factory('myService', function ($http, $q) { var de ...

Is there a benefit to using middlewares instead of the standard built-in functions in Express.js?

Express.js offers a wide range of middlewares that replace built-in functions. One example is body-parser, which parses HTTP request bodies, replacing the built-in function express.bodyParser. body-parser replaces the built-in function express.bodyParse ...

Is it possible to create a d3 gauge chart showcasing data with both labels and

We've been on the hunt for a radial chart that resembles the one shown below. What makes this chart stand out is the clear display of percentage values. Despite our search efforts over three days, we have yet to come across anything using the d3.js li ...

Top method for preventing an HTTP 403 error when receiving the Set-Cookie header in order to establish the CSRF Cookie

As I interact with a REST API that includes CSRF protection measures, I am facing a common hurdle. Successfully obtaining the token and sending it back to the server seems to work smoothly. However, encountering an HTTP 403 error arises when initiating t ...

Is it possible to delete an element from an HTML form?

I'm facing an issue with removing an element from my HTML form during the loading process. The specific element in question is as follows: <input type="hidden" name="testToken" value="1000ad"></input> Here's what I've tried so ...

Exploring the power of regular expressions in Javascript when used between

Consider the scenario outlined in the text below I desire [this]. I also desire [this]. I do not desire \[this] I am interested in extracting the content enclosed within [], but not including \[]. How should I approach this? Currently, I have ...

Injecting services differently in specific scenarios in AngularJS

I have a unique angular service called $superService that I utilize across many of my directives and controllers. However, there is one specific directive where I want to implement the following behavior: If another directive utilizes $superService in its ...

When a URL is entered, information is not retrieved from the database

I have a simple app setup where the data (iPhones from the database) is fetched in the AppComponent itself. ngOnInit(): void { this.iphoneservice.fetchIphones(); } The fetchIphones method is located in my iPhoneService file and consists of 3 functio ...

Manipulate the default option in a Select field with JavaScript

I'm currently working on a WordPress post editing page and I need to set up a target link: By default, the option is set to "None", but I want to change it to "Custom Link..." with the following details: <select name="imt_team_href" onchange="imt ...

The process of parsing HashMap failed due to an unexpected encounter with an Array, when an Object

Currently, I am in the experimental phase of creating an action at Hasura using a Node.js code snippet hosted on glitch.com. The code snippet is as follows: const execute = async (gql_query, variables) => { const fetchResponse = await fetch( "http ...

Having trouble utilizing a function with an async onload method within a service in Angular - why does the same function work flawlessly in a component?

I successfully created a component in Angular that can import an Excel file, convert it into an array, and display its content as a table on the page. The current implementation within the component looks like this: data-import.compoent.ts import { Compo ...

Having trouble sending a POST request to an Endpoint with Formidable and Request

I am encountering an issue while attempting a basic file upload to a REST endpoint using Node. The error that keeps appearing is: TypeError: Cannot read property 'hasOwnProperty' of null Below is my form setup: <form action="/upload4" me ...

As I enlarge the font size, the border around the div also expands

Can someone explain why the size of the div border increases along with the font size? If you'd like to see an example, check out this link to a jsFiddle: LINK TO JS FIDDLE: http://jsfiddle.net/krishna22211/m14qms52 ...

Injecting Services Error in Angular

I'm in the process of developing a web App and recently put together a new service: import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class ModuleService { constructor(private startTime: ...

Customize your Wordpress site with a JQuery load more button specifically designed for custom post types

I'm currently working on adding a "load more" button to my WordPress website in order to load my custom post types. I've managed to successfully make it load the posts, but I'm facing an issue where each time I load more posts, it replaces t ...

Guide on fetching live data from mysql database in angularjs

How can I dynamically load database users into a select box using Angular? <div ng-app="myapp" ng-controller="myctrl" class="centered"> <label>Select User</label> <select ng-model="selectedItem" ng-options="item.name for item in ...

Moving the starting directory of a NodeJS application on Azure

My NodeJS app on Azure was initially written in Javascript with the app.js file located in the root directory. This file was automatically detected during deployment via Git. Recently, I converted the app to Typescript and now have a build directory, with ...

The state of dynamically created Angular components is not being preserved

My current task involves dynamically creating multiple components to be placed in a table. The code successfully achieves this objective, but the state seems to be getting jumbled up at the level of the dynamically generated components. When a component is ...