Mock 'fs' module in jest - node.js

I'm trying to mock the fs module like so:
jest.mock('fs');
And as seen in this post - Mock fs function with jest
I should be able to treat the functions inside the module as jest.fn() and use - fs.existsSync.mockReturnValue(false); for instance.
That however does not seem to work and typescript gives me a bunch of errors.
All I want to do is assert a couple of functions like mkdirSync to have been called times or with some parameters and I seem to be running into this error -
'The "path" argument must be one of type string, Buffer, or URL. Received type undefined'
I tried to add fs.ts to the __mocks__ folder and mock it there - but that was no luck.
The file I am trying to test is a class and it imports fs. I am creating a new class instance in the beforeEach jest method.
So generally speaking, I don't really care to create a file or see if it exists, I wish to have a mocked return value or an implementation and just check with what parameters the fs module functions have been called.

It appears that running jest and mocking the file system in any way results in a conflict as jest is also using the fs module to handle the run.
The only solution i have found to overcome this issue:
export class SomeClass {
fs: typeof fs;
constructor() { this.fs = fs }
///code
}
Mock the fs methods like so in a test:
someClass.fs = {
mkdirSync: jest.fn()
} as any;
Assertion:
expect(someClass.fs.mkdirSync).toBeCalledWith('parameters');

Related

sinonjs - stub a library referenced internally as a function using node modules (no require)

I have an external library that is exported as a function, in the Stub documentation it only accepts an input with the first parameter as object and the second parameter as method , so how could I stub a library that is exported as a function in a Node ES Modules environment (without Commonjs)?
(In my specific case, I had used a library that use the internet to work, and I wanted to test derivated functions without accessing the internet, so I want to stub the external function library)
Attempts:
I couldn't use solutions like proxyquire as it is a solution based on require and module cache deletion, which are not supported within Node's ES modules.
I don't want to use proxyquire-universal because it simulates the operation of require in normal ES, and it's just a function in the whole project that I wanted to test, I was looking for a simpler solution
Changing the import mode doesn't work as it's not recompiled like in babel, so even if I import as import * as obj from 'lib' only the function name is changed
I had this error environment with 3 files:
An external library called "sum" for example, which I don't want to change, exported as follows:
External Library: externalSum.js
module.exports = function sum(a, b){
console.log(">>>>> running without stub <<<<<")
return a + b
}
This library used in the middle of a project file called mathProblems
Internal File: mathProblems.js
import sum from 'externalSum'
export function sumMore1(a) {
return sum(a, 1);
}
And I have a test file
Internal File: spec.js
import sinon from 'sinon'
import assert from 'assert'
import sumObj from 'externalSum'
import { sumMore1 } from '../mathProblems.js'
describe('sumMore1 is working', () => {
it('sumMore1 test', () => {
const sum_stub = sinon.stub(sumObj) // NOT STUBBING
sum_stub.withArgs(1, 1).returns(2) // NOT STUBBING
const result = sumMore1(1)
assert.strictEqual(result, 2)
});
});
I didn't find this solution anywhere on the internet, i found some solutions that work for node with request or babilon, but not for my case using ES Modules:
https://github.com/sinonjs/sinon/issues/562
https://minaluke.medium.com/how-to-stub-spy-a-default-exported-function-a2dc1b580a6b
So I wanted to register the solution in case anyone needs it.
To solve this, create a new file, which can be allocated anywhere in the project, in this case I will call it sumImport.js:
Internal File: sumImport.js
import sum from 'externalSum';
// export as object
export default {
sum
}
The object needs to be called inside the created function I want to test, and changed the import way:
Internal File: mathProblems.js
import sumObj from './sumImport.js';
export function sumMore1(a) {
const { sum } = sumObj;
return sum(a, 1);
}
And I finally managed to import as an object in the test:
Internal File: spec.js
import sinon from 'sinon'
import assert from 'assert'
import sumObj from '../sumImport.js'
import { sumMore1 } from '../mathProblems.js'
describe('sumMore1 is working', () => {
it('sumMore1 test', () => {
const sum_stub = sinon.stub(sumObj, "sum") // STUBBED
sum_stub.withArgs(1, 1).returns(2) // STUBBED
const result = sumMore1(1)
assert.strictEqual(result, 2)
});
});
I hope it helps someone and if someone else has some better solutions I would also be grateful!

Jasmine spy on exported function on NodeJS

I've had trouble spying on exported function in a NodeJS (v9.6.1) app using Jasmine.
The app is written in typescript, transpiled with tsc in a dist folder to be executed as javascript.
App
I have a Foo utils file (foo.utils.ts) which exports functions:
import {readFile} from "fs";
export function getData(filePath: string){
const data = readFile(filePath)
// various checks happens here.
return data;
}
And a Bar class in a bar.ts file:
import {getData} from "../utils/foo.utils
export class Bar {
public do(){
// ... does stuff
const data = getData(filePath);
// etc.
}
}
Test
Now I'm trying to spyOn the exported getData method to check if it've been called and to return a mocked value. I don't want to read file in my unit test (and even to use the real getData method at all)
The bar.spec.ts file for testing:
describe("Bar", ()=>{
let bar: Bar;
beforeEach(function() {
bar = new Bar();
})
it(" should do stuff", ()=>{
const spy = spyOn(???, "getData").and.returnValue([]);
bar.do();
expect(spy).toHaveBeenCalled();
})
});
Problems
As it is a NodeJS app, I've been trying to use global as the object to spy on, but I get the error:
Error: : getAttachementData() method does not exist
I've also tried to add
import * as fooutils from ".../src/utils/foo.utils
And spy on fooutils but I still goes through the exported function (and crashes when it tries to read the file...)
Now I'm kinda lost. From what I've found it's not really possible to mock an exported function (even though they should be added to the global object).
I thought about refactoring the utils file to create an utils class which exports static method and spying on them.
Questions
Am I doing something wrong ?
Is it possible to spy (and replace) exported function ?
Would using static method in class (rather than export function) work ?
Any other way to that ?
You can wrap getData in an object { getData } and write your unit test as follows.
import { getData } from "../utils/foo.utils";
...
it(" should do stuff", () => {
const spy = spyOn({ getData }, "getData").and.returnValue([]);
bar.do();
expect(spy).toHaveBeenCalled();
});

How do I find the names of classes in modules for TypeScript

So in this code:
import * as fs from "fs"
class FsAsyncFactory {
private static fsSync: any
}
export default FsAsyncFactory
I've put the type of this prop private static fsSync: any as any but it's going to be the fs variable imported at the top - how do I figure out what the class is called?
I guessed something like FileSystem but it didn't work. I don't have a deep enough understanding of TypeScript to figure it out.
I have "#types/node": "^8.0.50", in my dev dependencies and I've gone into node_modules/#types/node/index.d.ts but I can't see anything that quite makes sense? Thanks!
You can use the "type of" command of typescript.
import * as fs from "fs"
class FsAsyncFactory {
private static fsSync: typeof fs
}
export default FsAsyncFactory
And then inside your class
constructor(){
//...//
FsAsyncFactory.fsSync. //ide recognizes fsSync is of type "fs" and gives you full prediction of fs functions
//...//
}
Question: What is this typeof and why cant I use just class names?
Basically as I understand by reading the node/index.d.ts the fs is just a module that is being exported. Basically an object with some typed functions with their own documentation. In that case we don't have a class name or an interface to declare our other variables which are equal to fs. The typeof command of typescript is a type query, basically if no class or interface is implemented on the source variable it will just expect the same properties of the source to be presented in the target.
Another approach with your issue could be the use of Type Aliases
import * as fs from "fs"
type FileSystem = typeof fs
class FsAsyncFactory {
private static fsSync: FileSystem
}
export default FsAsyncFactory
This will create a new type called FileSystem which will expect every object which is declared as FileSystem type, to implement every function of the fs module.
Question: How can I use Bluebird's promisifyAll with typescript?
import * as fs from "fs"
import * as Bluebird from "bluebird"
const fsProm : FileSystem = Bluebird.promisifyAll(fs)
fsProm.writeFile('filename','some data') // Typescript error function expects at least 3 parameters
.then(console.log)
Unfortunately, from my point of view promisifyAll will change a strict typed function to something else without leaving any definitions of what changed, that is very bad for typescript. After some search I couldn't find any solid solution that will work on all cases, check this issue.
Maybe your best bet is to declare your promisidied variable to type any and continue working without intellisense.

Jasmine spy on function exported with no parent object and imported using ES6 in NODE

Having done a lot of research I cannot find a way to mock functions that are exported with no parent object. For example I'm trying to mock functions exported the following way:
module.exports = function thing(event) {};
OR in ES6
export function thing(event) {};
When importing these into a test file I try importing like this:
import {thing} from 'emvio-util-responses;
//call some function that calls thing()
spyOn(???, 'thing').and.returnValue({});
expect(???.thing).toHaveBeenCalled();
I have tried many ways of accomplishing this but the mock is not called.
Some suggest importing * and providing an alias as a parent object. like this:
import * as SomeObj from 'emvio-util-responses;
//call some function that calls thing()
spyOn(SomeObj , 'thing').and.returnValue({});
expect(SomeObj .thing).toHaveBeenCalled();
This doesn't work.
Others suggest using the window object
spyOn(window, 'thing').and.returnValue({});
But I'm in node :(.
When you use es6 modules instead CommonJS all exports are named so you may use:
export default (event) => {}
and then import & spy as
import * as SomeObj from 'emvio-util-responses'
...
beforeEach(() => {
spyOn(someObj, 'default')
});

Can I use a custom module resolution function (like "proxyquire") in place of require() with TypeScript?

I have a TypeScript file config.ts that will be run with node:
import myDependency = require('my-dependency');
export = {
doSomething = () => {
...
}
}
In other TypeScript file, I can import this file with full type safety:
import config = require('./config');
config.doSomething();
config.doSomethingElse(); // compiler error, this method doesn't exist
Now I want to unit test this script. In order to mock out the dependencies that this script require()s I'm using proxyquire, which lets me provide the values that my script will get when it makes calls to require(). Here's what my test might look like:
import proxyquire = require('proxyquire');
const config = proxyquire('./config', {
'my-dependency': {} // this mocked object will be provided when config.ts asks for `my-dependency`
});
expect(config.doSomething()).to.do.something();
This works fine, except that my config variable is of type any because I'm using proxyquire() in place of require(). TypeScript must give the require() function special treatment to allow it to perform module resolution. Is there a way to tell the TypeScript compiler that proxyquire() should also do module resolution, similar to require()?
I could rewrite config.ts as a class or make it use an interface. Then I would be able to explicitly type the variables in my tests by importing the class/interface definition. But allowing proxyquire() to implicitly type things for me would be far be the easier solution.
There is a workaround - you can get the type of config.ts module by importing actual module and using typeof in a type cast:
import proxyquire = require('proxyquire');
import configType = require('./config');
const config = <typeof configType> proxyquire('./config', {
'my-dependency': {} // this mocked object will be provided when config.ts asks for `my-dependency`
});
config.doSomething();
// config.noSuchMethod(); // does not compile
This is not ideal because you have to import the same module in your test twice - the real one just to get at the type of it and "proxiquired" one to actually use in your tests, and you have to be careful not to mix up the two. But it's pretty simple compared to the task of implementing another variant of module resolution for typescript. Also, when configType is used in this way - for typing only - its import will not even appear in generated javacsript code.

Resources