Jest: mock require.resolve calls - node.js

I've seen this question but no one has answered it as directly as I'd like so here we go again:
How do I mock calls to require.resolve?
So if I have a module like so:
get-module.js
// just a simple example, don't get too caught up
export default getModuleLocation() {
return require.resolve('./my-module');
}
How can I make my require.resolve call return something else? Here's what I've tried:
get-module.test.js
import getModule from './get-module';
let originalRequireResolve;
beforeAll(() => {
originalRequireResolve = require.resolve;
require.resolve = jest.fn();
});
afterAll(() => {
require.resolve = originalRequireResolve;
});
it('gets the module', () => {
// 🔴 this does NOT work
require.resolve.mockReturnValueOnce('mock-module');
expect(getModule()).toBe('mock-module');
});
The above does not work but the example does a good job of communicating what I'm trying to do. It seems like require is some sort of reserved thing I can't mock.
Any ideas? No workarounds please.

Related

importing and calling a function in Typsescript which returns nothing

Hi I have a function in typescript which returns nothing. When I try to import and call this function in another part of my app, I am getting errors. I am fairly new to typescript and am struggling with how to fix this issue.
Here's my code. I am trying to set up a few scripts to do some simple tests (would prefer not to use any testing frameworks).
My helper functions for testing are here
//File: helper.ts
type validator = () => void;
export const it = (desc: string, fn: validator) => {
try {
let res = fn();
console.log("\x1b[32m%s\x1b[0m", `\u2714 ${desc}`);
} catch (error) {
console.log("\n");
console.log("\x1b[31m%s\x1b[0m", `\u2718 ${desc}`);
console.error(error);
}
};
My tests use the helpers and are defined like this;
// File: dummytests.ts
import { strict as assert } from 'node:assert';
import { it } from "src/spec/helper";
export const check_if15_eqls_15 = it("shoulld check if something is true", () => {
assert.strictEqual(15, 15);
});
and i finally am running the tests as such;
// File: testRUnner.ts
import {check_if15_eqls_15} from 'src/spec/frontend/dummyTests';
console.log ("This test harness is for the frontend tests.\n");
check_if15_eqls_15();
this throws the error;
error TS2349: This expression is not callable.
Type 'void' has no call signatures.
5 check_if15_eqls_15();
~~~~~~~~~~~~~~~~~~
The line
export const check_if15_eqls_15 = it("shoulld check if something is true", () => {
assert.strictEqual(15, 15);
});
already calls the method.
This means check_if15_eqls_15 is already the return value of the method and unless the return value is not another function (which it isn't in this case), you can't call it again.
The same thing would happen in pure JS, since TypeScript does not change how the code is run
Something that might work for your example would be this:
export const check_if15_eqls_15 = () => it("shoulld check if something is true", () => {
assert.strictEqual(15, 15);
});

How to mock useNavigation hook in react-navigation 5.0 for jest test?

i want to mock useNavigation hook used inside my functional component. Any workaround how to mock it using jest?
import React from 'react';
import { useNavigation } from '#react-navigation/native';
import { TouchableOpacity } from 'react-native';
const MyComponent = () => {
const { navigate } = useNavigation();
const navigateToScreen = () => {
navigate('myScreen', { param1: 'data1', param2: 'data2' })
}
return (<TouchableOpacity onPress={navigateToScreen}>
Go To Screen
</TouchableOpacity>)
}
how to test params being passed in navigate function ?
I know I'm late, but I had the issue where I needed to know know when the navigate function was called.
For it, I mocked the function the following way, so the mockedNavigate would be a jest function that i could use later if i needed to test if the function was actually called:
const mockedNavigate = jest.fn();
jest.mock('#react-navigation/native', () => {
const actualNav = jest.requireActual('#react-navigation/native');
return {
...actualNav,
useNavigation: () => ({
navigate: mockedNavigate,
}),
};
});
This allowed me to use this later and I would know if I could navigate properly in my application:
expect(mockedNavigate).toHaveBeenCalledTimes(1);
Hope this helps.
jest.mock("#react-navigation/native", () => {
const actualNav = jest.requireActual("#react-navigation/native")
return {
...actualNav,
useFocusEffect: () => jest.fn(),
useNavigation: () => ({
navigate: jest.fn(),
}),
}
})
Another quite late solution that is a bit cleaner than the previously suggested ones.
const mockedNavigate = jest.fn();
jest.mock('#react-navigation/native', () => (
{ useNavigation: () => ({ navigate: mockedNavigate }) }));
This does not include any other functionality of the navigation object but it will allow you to test in the following way:
expect(mockedNavigate).toHaveBeenCalledTimes(1);
Or with expect.toHaveBeenCalledWith() (here) if more applicable in your specific situation.
This might not be a solution for a mock of useNavigation, but I found this helpful to achieve my own intent (spying), and it looks like it would achieve the OP's intent (expecting on calls)
import { CommonActions } from '#react-navigation/routers';
jest.spyOn(CommonActions, 'navigate');
This seems to be where the actions that surface out of the useNavigation hook are defined, too. I was unsuccessful to create a mock on this, but the spy works excellently.
Caveat
I'm using react-navigation 6.
I am calling render with a NavigationContainer thus I am not mocking anything in react-navigation, hence I can spy. If you don't do that, and you're looking to truly mock navigation, this won't work.
FWIW, I would recommend trying this approach first, as you can then allow much more of your code to be tested properly, as it would get used.
e.g. For my CaptureLocationScreen which is in a Stack inside a modal.
render(
<NavigationContainer>
<RootStack.Navigator>
<RootStack.Group screenOptions={{ presentation: 'modal', }}>
<RootStack.Screen
name="CaptureLocation"
component={CaptureLocationScreen}
options={({ navigation }: RootStackScreenProps<'CaptureLocation'>) => ({
headerTitle: 'Add new location',
headerLeft: () => (<CloseButton onPressed={navigation.goBack} />),
headerRight: () => (<AcceptButton />)
})} />
</RootStack.Group>
</RootStack.Navigator>
</NavigationContainer>);
References
https://reactnavigation.org/docs/navigation-actions/

How do I go "async all the way down" with Node Express?

I have many async functions in my system, so I need to go "async all the way down", which is to the point where the http.Server and express.Application app are created.
(This is unavoidable in an async system - there will be many async routines which are needed in constructors, which cannot be done, and so we need to use async factory functions instead, which lead to async creep all the way down to the entry point.)
But I'm not sure of the Node/TypeScript syntax to use to bootstrap the app.
My main entry point is System.ts:
class default export System {
public constructor() {
// init Express.Application
// init http.Server
// init other parts of the system
}
public async start(): Promise<void> {
// start the system asynchronously
// start listening with http.Server
}
}
Then I have a bootstrapping module Main.ts:
import System from "./System"
const system = new System();
export default ???; // PROBLEM IS HERE
Which should be run:
node ./dist/Main.js
But I'm not sure what to use in the export line. I tried all these:
export default await system.start(); // doesn't compile (obviously)
export default system.start(); // doesn't seem right
export default system.start().then(); // this works *maybe*
The last line works based on a smoke test - but I'm not sure if that's the way to do it, and whether there's something down the line that may fail.
What is the canonical way to start an asynchronous node app?
UPDATE
Based on #JacobGillespie's answer, the Main.ts bootstrapping module is now:
import System from "./System"
new System().start().then();
//new System().start().catch(e => console.error(e)); // alternative
In my case, System.ts has handlers for errors and unhandled promises, and does logging (otherwise use the "alternative" line). So the bootstrapping module just bootstraps the system.
async / await here are operating on promises, so you essentially want to "start" the promise by calling .then or .catch.
My go-to snippet for this is creating an async run or main function, then attaching error handling to the process, something like this:
async function run() {
// run the app, you can await stuff in here
}
run().catch(err => {
console.error(err.stack)
process.exit(1)
})
In your case that would look like (Main.ts):
import System from "./System"
async function run() {
const system = new System()
await system.start()
}
run().catch(err => {
console.error(err.stack)
process.exit(1)
})
You don't need to export anything since this module file isn't being imported anywhere else (it's the entry file).
You can just call system.then() or system.catch(), but personally I like the async function run() pattern since you may need to coordinate more than one async thing in the future and this makes the code more explicit.
system.start().then() => {
value => export default value
}
In my opinion, a better way would be:
System.ts:
function System():Promise<string>{
//setup express and the server
return new Promise((res,rej) => {
//the server var is just the http server instance
server.listen(8000,() => resolve("server created"));
});
}
export {System}
And then in Main.ts:
import {System} from "yourpath"
And then:
System().then(() => {
//code runs when server is created
}).catch(err => console.error(err));

Assert arguments of stubbed methods with sinon

import ManagerDaoStub from '../salesforce/__test__/ManagerDaoStub';
import criticalMerchants from '../criticalMerchants';
describe('criticalMerchants Unit Tests', () => {
before(() => {
ManagerDaoStub.initStubs();
});
after(() => {
ManagerDaoStub.restoreStubs();
});
it('assert the arguments of stubbed method', (done)=>{
let load = criticalMerchants.createCases(MERCHANT, DEVICE_ID, KEY, {});
return done();
});
})
This is the test file written in node criticalMerchants.test.js. The method i want to test which is createCases uses a method in ManagerDao, which has been stubbed in ManagerDaoStub as below.
import ManagerDao from '../ManagerDao';
class ManagerDaoStub {
constructor() {
this.sandbox = sinon.sandbox.create();
}
initStubs(sandbox) {
this.sandbox = sandbox || this.sandbox;
this.restoreStubs();
this.initFindOpenCases();
}
restoreStubs() {
this.sandbox.restore();
}
initFindOpenCases() {
let findOpenCases = this.sandbox.stub(ManagerDao, "findOpenCases");
findOpenCases
.withArgs(DEVICE_ID, KEY, match.func)
.callsArgWith(2, new Error("Test error"));
}
}
I want to assert whether this stubbed method initFindOpenCases was called with the right arguments (DEVICE_ID,KEY,null). I used
sinon.assert.calledWith(ManagerDaoStub.initFindOpenCases, DEVICE_ID, KEY, null) and this gives the following error:
AssertError: initFindOpenCases() is not stubbed.
Can someone suggest a proper way to do this?
First off, if ManagerDao.initFindOpenCases is an instance method (I'm unsure since you haven't shared its definition), then you can't stub it on the constructor like you've done here:
let findOpenCases = this.sandbox.stub(ManagerDao, "findOpenCases")
You need to either create an instance first-- then stub it on that instance-- or stub it on the prototype itself like so:
let findOpenCases = this.sandbox.stub(ManagerDao.prototype, "findOpenCases");
Secondly, you're making the same mistake again in your assertion, combined with another:
sinon.assert.calledWith(ManagerDaoStub.initFindOpenCases, DEVICE_ID, KEY, null)
ManagerDaoStub is the constructor, and it does not have an initFindOpenCases property. Its prototype does, and thus its instances do as well. On top of that, ManagerDaoStub.prototype.initFindOpenCases is still not a stub. It's a method you're calling to create a stub, but it is not itself a stub. More plainly, you're getting ManagerDao mixed up with ManagerDaoStub.
Assuming you make example change above, you can make your assertion work like this:
sinon.assert.calledWith(ManagerDao.prototype.initFindOpenCases, DEVICE_ID, KEY, null)
However, this isn't all I would recommend changing. This latter mixup is arising largely because you're vastly over-complicating the setup code of your test. You don't need to make an entire class to stub one method of ManagerDao.
Instead, just replace your before and after calls with these:
beforeEach(() => {
// Create the stub on the prototype.
sinon.stub(ManagerDao.prototype, 'findOpenCases')
.withArgs(DEVICE_ID, KEY, sinon.match.func)
.callsArgWith(2, newError('Test Error'));
});
afterEach(() => {
// As of sinon 5.0.0, the sinon object *is* a sandbox, so you can
// easily restore every fake you've made like so:
sinon.restore();
});
Aside from that, I recommend looking deeply into the difference between properties on a constructor and properties on its prototype. That knowledge will make stuff like this much easier for you. Best place to start is probably here on MDN.

How can I build my test suite asynchronously?

I'm trying to create mocha tests for my controllers using a config that has to be loaded async. Below is my code. However, when the mocha test is run, it doesn't run any tests, displaying 0 passing. The console.logs are never even called. I tried doing before(next => config.build().then(next)) inside of the describe, but even though the tests run, before is never called. Is there a way to have the config be loaded one time before any tests are run?
'use strict';
const common = require('./common');
const config = require('../config');
config
.build()
.then(test);
function test() {
console.log(1);
describe('Unit Testing', () => {
console.log(2);
require('./auth');
});
}
You should run Mocha with the --delay option, and then use run() once you are done building your test suite. Here is an example derived from the code you show in the question:
'use strict';
function test() {
console.log(1);
describe('Unit Testing', () => {
console.log(2);
it("test", () => {
console.log(3);
});
});
// You must use --delay for `run()` to be available to you.
run();
}
setTimeout(test, 1000);
I'm using setTimeout to simulate an asynchronous operation. Using --delay and run() allows you to build a suite that is the result of an asynchronous computation. Note, however, that the suite must be built in one shot. (You cannot have an asynchronous process inside describe that will make calls to it. This won't work.)
One thing you should definitely not do is what rob3c suggests: calling describe or it (or both) from inside a hook. This is a mistake that every now and then people make so it is worth addressing in details. The problem is that it is just not supported by Mocha, and therefore there are no established semantics associated with calling describe or it from inside a hook. Oh, it is possible to write simple examples that work as one might expect but:
When the suite becomes more complex, the suite's behavior no longer corresponds to anything sensible.
Since there are no semantics associated with this approach, newer Mocha releases may handle the erroneous usage differently and break your suite.
Consider this simple example:
const assert = require("assert");
const p = Promise.resolve(["foo", "bar", "baz"]);
describe("top", () => {
let flag;
before(() => {
flag = true;
return p.then((names) => {
describe("embedded", () => {
for (const name of names) {
it(name, () => {
assert(flag);
});
}
});
});
});
after(() => {
flag = false;
});
it("regular test", () => {
assert(flag);
});
});
When we run it, we get:
top
✓ regular test
embedded
1) foo
2) bar
3) baz
1 passing (32ms)
3 failing
// [stack traces omitted for brevity]
What's going on here? Shouldn't all the tests pass? We set flag to true in the before hook for the top describe. All tests we create in it should see flag as true, no? The clue is in the output above: when we create tests inside a hook, Mocha will put the tests somewhere but it may not be in a location that reflects the structure of the describe blocks in the code. What happens in this case is that Mocha just appends the tests created in the hook the the very end of the suite, outside the top describe, so the after hook runs before the dynamically created tests, and we get a counter-intuitive result.
Using --delay and run(), we can write a suite that behaves in a way concordant with intuition:
const assert = require("assert");
const p = Promise.resolve(["foo", "bar", "baz"]).then((names) => {
describe("top", () => {
let flag;
before(() => {
flag = true;
});
after(() => {
flag = false;
});
describe("embedded", () => {
for (const name of names) {
it(name, () => {
assert(flag);
});
}
});
it("regular test", () => {
assert(flag);
});
});
run();
});
Output:
top
✓ regular test
embedded
✓ foo
✓ bar
✓ baz
4 passing (19ms)
In modern environments, you can use top-level await to fetch your data up front. This is a documented approach for mocha: https://mochajs.org/#dynamically-generating-tests
Slightly adapting the example from the mocha docs to show the general idea:
function fetchData() {
return new Promise((resolve) => setTimeout(resolve, 5000, [1, 2, 3]));
}
// top-level await: Node >= v14.8.0 with ESM test file
const data = await fetchData();
describe("dynamic tests", function () {
data.forEach((value) => {
it(`can use async data: ${value}`, function () {
// do something with data here
});
});
});
This is nice as it is on a per-file basis, and doesn't involve you taking on management responsibility of the test runner as you do with --delay.
The problem with using the --delay command line flag and run() callback that #Louis mentioned in his accepted answer, is that run() is a single global hook that delays the root test suite. Therefore, you have to build them all at once (as he mentioned), which can make organizing tests a hassle (to say the least).
However, I prefer to avoid magic flags whenever possible, and I certainly don't want to have to manage my entire test suite in a single global run() callback. Fortunately, there's a way to dynamically create the tests on a per-file basis, and it doesn't require any special flags, either :-)
To dynamically create It() tests in any test source file using data obtained asynchronously, you can (ab)use the before() hook with a placeholder It() test to ensure mocha waits until before() is run. Here's the example from my answer to a related question, for convenience:
before(function () {
console.log('Let the abuse begin...');
return promiseFn().
then(function (testSuite) {
describe('here are some dynamic It() tests', function () {
testSuite.specs.forEach(function (spec) {
it(spec.description, function () {
var actualResult = runMyTest(spec);
assert.equal(actualResult, spec.expectedResult);
});
});
});
});
});
it('This is a required placeholder to allow before() to work', function () {
console.log('Mocha should not require this hack IMHO');
});

Resources