How to mock not installed npm package in jest? - javascript

How to mock not installed npm package in jest?
I'm writing a library and I need to test some cases when optional dependencies are not installed.
Update
My library has an optional dependency. The end-user of my library can optionally to install styled-components.
In my tests (jest) I covered the case when styled-components is installed.
Now I need to cover the case when the package is not installed.
test(`When styled-components is not installed`, () => {
process.env.SC_NOT_INSTALLED = true
const fn = () => {
const styled = require(`./styled`)
}
expect(fn).toThrow(Error)
})
let styled
try {
require.resolve(`styled-components`)
styled = require(`styled-components`)
if (process.env.NODE_ENV === `test` && process.env.SC_NOT_INSTALLED) {
throw new Error(`Imitation styled-components is not installed`)
}
}
catch {
styled = () => {
throw new Error(`Module not found: styled-components`)
}
}
export default styled
process.env.SC_NOT_INSTALLED -> will not work because as I guess the test are running in different process.

When an exception is thrown in your try you are exporting a function.
Calling the exported function is what throws the Error.
Change your test to this:
test(`When styled-components is not installed`, () => {
process.env.SC_NOT_INSTALLED = true;
const styled = require(`./styled`).default;
expect(() => styled()).toThrow('Module not found: styled-components'); // Success!
});
...and it should work.
Update
If you are calling require('./styled') multiple times in the same test file, then you will want to add an afterEach that calls jest.resetModules, otherwise Jest will cache the module and just keep returning the same module for each require:
afterEach(() => {
jest.resetModules();
})
test(`When styled-components is installed`, () => {
const styled = require(`./styled`).default;
// ...
});
test(`When styled-components is not installed`, () => {
process.env.SC_NOT_INSTALLED = true;
const styled = require(`./styled`).default;
expect(() => styled()).toThrow('Module not found: styled-components'); // Success!
});

Related

How to mock a worker for a Jest test?

I have a test:
import { convertHeicToPng } from './heicUtils';
class Worker {
url: string;
onmessage: (m?: any) => void;
constructor(stringUrl: string) {
this.url = stringUrl;
this.onmessage = () => {};
}
postMessage(msg: any) {
this.onmessage(msg);
}
}
(window.Worker as any) = Worker;
describe('test heicUtils', () => {
test('should convert HEIC to PNG', async () => {
const file = new File([''], 'test.heic', { type: 'image/heic' });
const base64 = await convertHeicToPng(file);
expect(base64).toContain('data:image/png;base64');
});
});
and in heicUtils, I'm using heic2any, which uses WebWorkers. How can I properly mock a Worker for a Jest test?
Since you are testing your heicUtils module, you should mock the heic2any lib, otherwise, you will be testing the 3rd party library instead of your own code.
In the mock, you should define the functions/methods of heic2any that your heicUtils use and what they should return for each test case you intend to write.
Examples of how to mock modules can be found here: https://jestjs.io/docs/manual-mocks
tsc && mocha --reporter spec -t 5000 --exit
.npm install mocha
.then do this cmd
.Examples my github : https://github.com/www778878net/koa78-base78

NodeJS Jest : Testing function that imports module using require.main.require

I am trying to write unit testing using Jest for a Node JS project. It was importing all the modules using require.main.require
Below are the simulation of the issue. Code can be found here: https://stackblitz.com/edit/node-jest-demo?file=index.js
I have the following test file present in my root directory in which I am importing index.js
./sample.pass.test.js
const { checkUser } = require('./index');
console.log(checkUser); // This is purely to check If i can access checkUser from this file or not
describe('Testing...', () => {
it('Should pass', () => {
expect(0).toBe(0);
});
});
In my index.js I am importing another function using require.main.require
const { getUserById } = require.main.require('./models/UserModel');
function checkUser(id) {
const user = getUserById(id);
return user ? 'Found' : 'Not Found';
}
module.exports.checkUser = checkUser;
The above test case is passing. But If I were to place the same test file in some other directly (like _test_ ) then it fails.
E.g.: ./__test__/sample.fail.test.js
Notice here I adjusted require statement of index since it is now one level up
const { checkUser } = require('../index');
console.log(checkUser);
describe('Testing...', () => {
it('Should pass', () => {
expect(0).toBe(0);
});
});
The result shows it is unable to access UserModel.
Cannot find module './models/UserModel' from '__tests__/sample.fail.test.js'
Require stack:
index.js
__tests__/sample.fail.test.js
> 1 | const { getUserById } = require.main.require('./models/UserModel');
| ^
2 |
3 | function checkUser(id) {
4 | const user = getUserById(id);
What could be the solution in this case?
Thank in advance!

Use jest to compare content of two files

I'm trying to switch from Mocha and Chai to Jest. In my current setup I'm also using chai-files to compare the contents of two files:
import chai, { expect } from 'chai';
import chaiFiles, { file } from 'chai-files';
import fs from 'fs-extra';
import { exec } from 'child-process-promise';
chai.use(chaiFiles);
describe('cli', () => {
before(() => {
process.chdir(__dirname);
});
it('should run', async () => {
// make a copy of entry file
fs.copySync('./configs/entry/config.version-and-build.xml', './config.xml');
// executes code that changes temp files
await exec('../dist/cli.js -v 2.4.9 -b 86');
// checks if target file and change temp file are equal
expect(file('./config.xml')).to.equal(file('./configs/expected/config.version-and-build.to.version-and-build.xml'));
});
afterEach(() => {
if (fs.existsSync(tempConfigFile)) {
fs.removeSync(tempConfigFile);
}
});
});
How should this be done in Jest? Will I need to load both files and compare the content?
Yes, simply load the contents of each like so:
expect(fs.readFileSync(actualPath)).toEqual(fs.readFileSync(expectedPath));

node - using jest with esm package

I was wondering how I would incorporate the esm package https://www.npmjs.com/package/esm with jest on a node backend.
I tried setting up a setup file with require("esm") and require("esm")(module) at the very top of the file, but it's still giving me the SyntaxError: Unexpected token error.
I would have previously used node -r esm but jest doesn't support this.
When you perform require("esm")(module), think of it as you are creating an esm-transformer function that is pending a file to be transformed into an ES module.
Here's my attempt with node v8+ with:
default jest configuration
default esm configuration
utils-1.js:
export const add = (a, b) => a + b;
utils-2.js:
export const multiAdd = array => array.reduce((sum, next) => sum + next, 0)
_test_/utils-1.assert.js
import { add } from '../utils-1';
describe('add(a,b)', () => {
it('should return the addtion of its two inputs', () => {
expect(add(1,2)).toBe(3);
});
});
_test_/utils-2.assert.js
import { multiAdd } from '../utils-2';
describe('multiAdd(<Number[]>)', () => {
it('should return a summation of all array elements', () => {
expect(multiAdd([1,2,3,4])).toBe(10);
})
});
_test_/utils.test.js
const esmImport = require('esm')(module);
const utils_1 = esmImport('./utils-1.assert')
const utils_2 = esmImport('./utils-2.assert')
Hope this helps!

Jest - Testing Module Multiple Times in One Test Suite

I have a TypeScript module (should be irrelevant, as I think this affect JS too) and I'm trying to test a module I have. The module imports lots of data from external files and chooses which data should be returned based on the a variable.
I'm attempting to run some tests where I update that variable, re-require the module and run further tests in one file. But my issue is that the require of the file only runs once. I guess it's being cached. Is it possible to tell Jest's require function not to cache or to clear the cache between tests?
Here's some stripped back code of what I'm trying to achieve:
module.ts
import { getLanguage } from "utils/functions";
import * as messagesEn from "resources/translations/en";
import * as messagesFr from "resources/translations/fr";
// Determine the user's default language.
const language: string = getLanguage();
// Set messages based on the language.
let messages: LocaleMessages = messagesEn.default;
if (languageWithoutRegionCode === "fr") {
messages = messagesFr.default;
}
export { messages, language };
test.ts
import "jest";
// Mock the modules
const messagesEn = { "translation1": "English", "translation2": "Words" };
const messagesFr = { "translation1": "Francais", "translation2": "Mots" };
const getLangTest = jest.fn(() => "te-ST");
const getLangEn = jest.fn(() => "en-GB");
const getLangFr = jest.fn(() => "fr-FR");
jest.mock("resources/translations/en", () => ({"default": messagesEn}));
jest.mock("resources/translations/fr", () => ({"default": messagesFr}));
jest.mock("utils/functions", () => ({
getLanguage: getLangTest
})
);
describe("Localisation initialisation", () => {
it("Sets language", () => {
const localisation = require("./localisation");
expect(getLangTest).toHaveBeenCalled();
expect(localisation.language).toEqual("te-ST");
expect(localisation.messages).toEqual(messagesEn);
});
it("Sets english messages", () => {
// THIS GETS THE MODULE FROM THE CACHE
const localisation = require("./localisation");
expect(getLangEn).toHaveBeenCalled();
expect(localisation.language).toEqual("en-GB");
expect(localisation.messages).toEqual(messagesEn);
});
it("Sets french messages", () => {
// THIS GETS THE MODULE FROM THE CACHE
const localisation = require("./localisation");
expect(getLangFr).toHaveBeenCalled();
expect(localisation.language).toEqual("fr-FR");
expect(localisation.messages).toEqual(messagesFr);
});
});
I'm aware the second and third tests won't work anyway as I'd need to update the "utils/functions" mock. The issue is that the code in module.ts only runs once.
So, many thanks to the Jest folks on Discord. It's possible to actually clear the modules from the cache with the jest.resetModules() function.
So my test.ts file will look as follows:
describe("Localisation initialisation", () => {
beforeEach(() => {
jest.resetModules();
});
it("Sets language", () => {
const localisation = require("./localisation");
// Perform the tests
});
it("Sets english messages", () => {
const localisation = require("./localisation");
// Perform the tests
});
it("Sets french messages", () => {
const localisation = require("./localisation");
// Perform the tests
});
});
The beforeEach() call to jest.resetModules() ensures we're re-running the code in the module.

Categories