Mocking fs and glob modules with Jest
to test scripts that read and write files to disk.
The whole code can be found in this GitHub repository: https://github.com/marekrozmus/blog-mocking-fs-and-glob-modules-with-jest
When I had to introduce one more “small change” to one of the post build scripts in the project I had a lot of those thoughts like: “I hope this won’t break anything”. When you get that kind of thoughts — it is the time to write some unit tests ;)
The unit test should be if course written as soon as possible but sometimes there is no time as we need that feature now and it is already Friday afternoon (leave a clap if you have ever needed to make a Friday afternoon deployment ;)).
So this time (it was not Friday yet) I have decided to take some time to write the tests while I still understood what this script supposed to do.
The script was just searching for some HTML files in specific folder and was replacing some texts in those files — simple as that.
Find the files, find in those files specific texts (enclosed with some start and end markers), replace those with some new values and save the files.
What I wanted to test if the texts are found correctly and if those are replaced with expected data.
The code simplified
You can download the repo and run npm start
to see the script in action: https://github.com/marekrozmus/blog-mocking-fs-and-glob-modules-with-jest
Mocking
To stop the fs
and glob
access the disk during the tests, they need to be mocked. This can be done by using following Jest method — Mocking Node modules.
So we need to create the mocks of those libraries in the __mocks__
folder.
In my case for the glob
I needed only globSync
method that was returning the “found” files. This is how whole glob
mock look like in my case:
So just two functions — one for setting the files that should be returned (__setMockFiles
) and another one that I used in the post build script (globSync
).
The mock for the fs
module looks almost the same:
It got the __setMockFiles
function also but in this case we initialize that with the file path and its content.
For glob
we take only files’ paths but for the fs
module mock also the content of the files that will be returned by the readFileSync
function.
And here is the test:
What is happening here:
- spyOn the
writeFileSync
method to see if it is called with proper params - load and run the script (it will use the mocked
glob
andfs
modules - check if the proper replacement is done
Remove console logs from test results
Because the script had some console.log
calls, this is what I could see when tests were run:
I have tried something that worked in other projects: jest.spyOn(global.console, “log”).mockImplementationOnce(() => undefined);
But unfortunately still console logs were there. The solution was to show the logs only if script was run with specific param:
verbose && console.log(‘ Writing file…’);
Check the post-build.js
file to see how it is used.
Disable automatic script run
The post build script had following structure:
That caused it to be run every time it was required. In the test the following line was already running the script const PostBuild = require(“./post-build”);
Then another line was triggering the script again: PostBuild.main();
One might say: just remove the second invocation. Well, this brings two issues:
- we cannot see directly in the test that we actually have run something
- if the
main
method would beasync
then it gets complicated to actuallyawait
until it is done
So I have updated the script to only run automatically if specific argument is used:
And then update the package.json
script to pass that param:
And that’s it — now you we can run the script and test it.