0

I have written a test case that successfully load files into virtual FS, and at the same time mounted a virtual volume as below

describe("should work", () => {
  const { vol } = require("memfs");
  afterEach(() => vol.reset());
  beforeEach(() => {
    vol.mkdirSync(process.cwd(), { recursive: true });
    jest.resetModules();
    jest.resetAllMocks();
  });

it("should be able to mock fs that being called in actual code", async () => {
    jest.mock("fs", () => {
      return ufs //
        .use(jest.requireActual("fs"))
        .use(createFsFromVolume(vol) as any);
    });
    jest.mock("fs/promises", () => {
      return ufs //
        .use(jest.requireActual("fs/promises"))
        .use(createFsFromVolume(vol) as any);
    });
    const { createFsFromVolume } = require("memfs");
    const { ufs } = require("unionfs");
    const { countFile } = require("../src/ops/fs");

    vol.fromJSON(
      {
        "./some/README.md": "1",
        "./some/index.js": "2",
        "./destination": null,
      },
      "/app"
    );

    const result = ufs.readdirSync(process.cwd());
    const result2 = ufs.readdirSync("/app");
    const result3 = await countFile("/app");

    console.log({ result, result2, result3 });
  });
});

By using ufs.readdirSync, I can access to virtual FS and indeed result giving me files that loaded from disc into virtual FS, result2 representing /app which is a new volume created from vol.fromJSON.

Now my problem is I am unable to get the result for result3, which is calling countFile method as below

import fsPromises from "fs/promises";

export const countFile = async (path: string) => {
  const result = await fsPromises.readdir(path);
  return result.length;
};

I'm getting error

Error: ENOENT: no such file or directory, scandir '/app'

which I think it's because countFile is accessing the actual FS instead of the virtual despite I've had jest.mock('fs/promises')?

Please if anyone can provide some lead?

Isaac
  • 12,042
  • 16
  • 52
  • 116
  • Confused as to what your objective is. You want to: >write a unit test that involves both virtual FS and actual FS. But I don't see any cases where you would need to mock anything. The first 2 results, you're using `unionfs` to read the directory files. Third result, you're using `fs/promises` to read the directory files. Neither of which needs to be mocked if, the test is to simply use `fs/promises` to read files from a virtual file system. And, `ufs` isn't mocked when using `.readdir();` but `fs/promises` does? Needs clarification. – Tony Jan 31 '22 at 02:36
  • @Tony: `countFile` is a function that uses `fsPromises`. In order to write a unit test against it, i would have to mock `fsPromises` with `unionfs` right? Because `fsPromises` doesn't access virtual FS. If it's clearer now? – Isaac Jan 31 '22 at 03:57
  • A bit but, I don't understand the need to go through all this. I'll explain in an answer. – Tony Jan 31 '22 at 14:07

1 Answers1

0

This is the function you want to unit test.

//CommonJS version
const fsPromises = require('fs/promises');

const countFile = async (path) => {
  const result = await fsPromises.readdir(path);
  return result.length;
};

module.exports = {
    countFile
}

Now, how you would normally go about this, is to mock fsPromises. In this example specifically readdir() since that is the function being used in countFile.

This is what we call: a stub.

A skeletal or special-purpose implementation of a software component, used to develop or test a component that calls or is otherwise dependent on it. It replaces a called component.

const {countFile} = require('./index');
const {readdir} = require("fs/promises");

jest.mock('fs/promises');

beforeEach(() => {
    readdir.mockReset();
});

it("When testing countFile, given string, then return files", async () => {
    const path = "/path/to/dir";
// vvvvvvv STUB HERE
    readdir.mockResolvedValueOnce(["src", "node_modules", "package-lock.json" ,"package.json"]);
    const res = await countFile(path);
    expect(res).toBe(4);
})

You do this because you're unit testing. You don't want to be dependent on other functions because that fails to be a unit test and more integration test. Secondly, it's a third-party library, which is maintained/tested by someone else.

Here is where your scenario applies. From my perspective, your objective isn't to test countFile() rather, to test fsPromises and maybe test functionality to read virtual file-systems: unionfs. If so then, fsPromises doesn't need to really be mocked.

Tony
  • 436
  • 1
  • 9
  • 17