How to mock curry function with Jest? - jestjs

add.js
export default a => b => a+b;
module.js
import add from './add';
export default {
add1: n => add(1)(n),
};
test/module.js
import add from '../add';
import module from '../module';
jest.mock('../add', () => () => jest.fn());
module.add1(6);
expect(add.mock.calls).toHaveLength(1);
this can be called, but add wouldn't be a mock function, instead add() is a mock function, but the call params were not recorded correctly.
jest.mock('../add', () => () => jest.fn(a => b => a+b));
has also tried this, which doesn't seem to work correctly as well.
jest.mock('../add', jest.fn(a => b => a+b));
this would throw the inline function error
Is there a correct way to mock curry function at the moment?

Simple version should look like this
jest.mock('../add', () => (a) => jest.fn(b => a+b));
So you mock add module with a function that return that when gets called it returns the spy, problem you can't test anything on the spy.
So we need to refactor it so you have the add1 think as a spy in scope of the test
import add from '../add'
jest.mock('../add', () => jest.fn)
const addC = jest.fn()
add.mockImplemetation((a) => {
addC.mockImplementation((b => a+b)
return addC
})

I know this is a bit old but with the following answer I would have saved so much time...
import module from "./module";
const mockOperation = {
add: jest.fn()
};
jest.mock("./add", () => () => {
return mockOperation.add;
});
describe("add ", () => {
it("is called at least once", () => {
module.add1(6);
expect(mockOperation.add.mock.calls).toHaveLength(1);
});
});
A very importante thing: the constant function that you want to mock must start with the word mock. Otherwise it will throw an error.
const mockOperation = {
add: jest.fn()
};
With the mockOperation constant assigned to the add file, by reference is calling to it's add method which is a jest.fn()

I've had to do this a lot recently so I wrote a helper function to help me out.
curryMock = (baseFn, maxCurries) => {
var callIndex = 0
var curries = []
let returnFn
baseFn.mockImplementation((arg) => {
curries[callIndex] = 0
const fn = returnFn(callIndex)
callIndex++
return fn
})
returnFn = (index: number) => {
if (curries[index] < maxCurries) {
curries[index]++
return (arg: any) => {
baseFn.mock.calls[index].push(arg)
return returnFn(index)
}
}
}
}
You pass the function a jest.fn() and curryMock modifies it's implementation so that all the arguments return functions are called with will be added to the original jest.fn().mock.calls array. The second argument specifies how many times to return a function. maxCurries = 0 is for a regular function () => {},
maxCurries = 1is for a regular function() => => {}`, etc.
In your case:
import add from '../add';
import module from '../module';
jest.mock('../add');
curryMock(add)
module.add1(6);
expect(add).toHaveBeenCalledWith(1, 6);
//equivalent to
//expect(add).toHaveBeenNthCalledWith(1, 1, 6);
//if you others to test..
module.add7(4)
expect(add).toHaveBeenNthCalledWith(2, 7, 4)
curryMock is a quick utility function that I wrote for personal use. I will likely clean it up and extend it later, but I hope you get the idea: testing curried functions does not have to laborious.

Related

Dynamic test data for test.each Jest

Is it possible to assign values at runtime for below Jest test.each example:
describe('jb-tests', () => {
jest.setTimeout(700000);
let table: Array<number[][]> = [[]];
beforeAll(() => {
//Below lines are just hardcoded values
table = [];
let test = [[1,2,3],[4,5,6],[7,8,9]]
table.push(test);
});
test.each(table)('.add(%i, %i)', (a, b, expected) => {
console.log("inside");
});
});
This test case is stuck and not showing any output. If I remove the jest.setTimeout then it fails with message "Exceeded timeout of 5000 ms for a test."
Building on the comment from #jonrsharpe, you can however specify values in the test.each() call.
describe('jb-tests', () => {
jest.setTimeout(700000);
test.each([
[1,2,3],
[4,5,6],
[7,8,9],
])('.add(%i, %i)', (a, b, expected) => {
expect(a + b).toBe(Expected);
});
});
However, if you really need data from an external source, and want to process it, you can load it in the test code, or a beforeAll(). Note that the beforeAll() is evaluated at the very start, so what is loaded there is done at the start of processing the file, not as each test is run.
import * as NodeJSFS from 'fs/promises';
describe('jb-tests', () => {
jest.setTimeout(700000);
let table: Array<number[][]> = [[]];
beforeAll(async () => {
const FileContent$: Buffer = await NodeJSFSPromises.readFile('testdata.txt');
// Loop through FileContent$ (map, etc.) and load your table (table.push)
...
});
describe('adding addition describe for this section', () => {
for (const data of table) {
... // Parse data (one element of table)
it(`should add ${a} + ${b} to be ${Expected}', () => {
expect(a + b).toBe(Expected);
});
}
});
Just found an alternate solution: Populate the test data dynamically and then use describe block to execute test cases using test.each.
let test_data: Array<number[]> = [];
for(let i=0;i<3;i++){
test_data.push([i+0,i+1,i+2]);
}
describe('jb-tests', () => {
test.each(test_data)('.add(%i, %i, %i)', (a, b, c) => {
expect(a + b + c).toBeGreaterThan(0);
});
});

Ramda.when always return true using fs

I am trying to use Ramda.when to execute only if a condition is true but it's always returning true:
const bla2 = path => () => R.when(fs.existsSync(path()), console.log(path()))
Is there anyway to use Ramda.when with fs.existsSync?
edit:
Even set false this is not working:
const bla3 => () => R.when(false, console.log('bla'))
This is the real code:
const moveEnvironmentVarsFile = (oldPath, newPath) => () => R.when(fs.existsSync(oldPath()), fs.renameSync(oldPath(), newPath()))
I don't think R.when is appropriate for what you're trying to do. R.when's purpose is to transform a value, but only if that value matches a condition. It expects you to pass in three things:
A function which checks the condition
A function which does the transformation
A value that you want to send through this process
fs.existsSync can conceivably be used as argument 1, such as the following contrived example which appends "exists" to a string if the file exists:
const result = R.when(
fs.existsSync,
(val) => val + "exists",
"some/file"
);
// result is either some/file or some/fileexists
In real word, I am trying to rename a file using fs. How can I do it using ramda or any functional way in JS?
Honestly, i would just use an if/else and not use Ramda:
const myFunc = (filename) => {
if (fs.existsSync(filename) {
// do something
} else {
// do something else
}
}
If you really want to use ramda to create that code for you, you could use R.ifElse:
const myFunc = R.ifElse(
fs.existsSync,
(filename) => { /* do something */ },
(filename) => { /* do someething else */ }
);

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);
});

Ensure an async method is never executed in parallel

There's an async method in some external library, which is not thread-safe (corrupts internal state when being called concurrently) and is very unlikely to be ever fixed in the near future. Yet I want the rest of my codebase to freely use it in any possible parallel vs. sequential combinations (in other words, I want my methods that use that method to still be thread-safe).
So I need to wrap that problematic method with some Active Object implementation, so that its invocations are always aligned (executed one after another).
My take on this is to use a chain of Promises:
var workQueue: Promise<any> = Promise.resolve();
function executeSequentially<T>(action: () => Promise<T>): Promise<T> {
return new Promise((resolve, reject) => {
workQueue = workQueue.then(() => {
return action().then(resolve, reject);
});
});
}
And then instead of directly calling that method:
const myPromise = asyncThreadUnsafeMethod(123);
I'll be calling it like this:
const myPromise = executeSequentially(() => asyncThreadUnsafeMethod(123));
But am I missing something? Any potential problems with this approach? Any better ideas?
There are no problems with this approach, using a promise queue is a standard pattern that will solve your problem.
While your code does work fine, I would recommend to avoid the Promise constructor antipattern:
let workQueue: Promise<void> = Promise.resolve();
const ignore = _ => {};
function executeSequentially<T>(action: () => Promise<T>): Promise<T> {
const result = workQueue.then(action);
workQueue = result.then(ignore, ignore);
return result;
}
Also you say that you'd be "calling the method" like executeSequentially(() => asyncThreadUnsafeMethod(123));, but this does leave the potential for mistakes where you forget the executeSequentially wrapper and still call the unsafe method directly. Instead I'd recommend to introduce an indirection where you wrap the entire safe call in a function and then export only that, having your codebase only interact with that facade and never with the library itself. To help with the creation of such a facade, I'd curry the executeSequentially function:
const ignore = _ => {};
function makeSequential<T, A extends unknown[]>(fn: (...args: A) => Promise<T>) => (...args: A) => Promise<T> {
let workQueue: Promise<void> = Promise.resolve();
return (...args) => {
const result = workQueue.then(() => fn(...args));
workQueue = result.then(ignore, ignore);
return result;
};
}
export const asyncSafeMethod = makeSequential(asyncThreadUnsafeMethod);
or just directly implement it for that one case
const ignore = _ => {};
let workQueue: Promise<void> = Promise.resolve();
export function asyncSafeMethod(x: number) {
const result = workQueue.then(() => asyncThreadUnsafeMethod(x));
workQueue = result.then(ignore, ignore);
return result;
}
import { asyncSafeMethod } from …;
asyncSafeMethod(123);

Mock function without callback as parameter

I have dh.js
const checkDExistsCallback = (err, dResp) => {
if (err)
cbResp.error('failed');
if (dResp.length > 0)
checkDCollectionExists();
else
cbResp.error('Not found.');
};
const checkDCollectionExists = () =>
{
let query = `select sid from tablename where sid = '${objRequestData.dName}' limit 1;`;
genericQueryCall(query, checkDCollCallback);
}
module.exports = {checkDExistsCallback , checkDCollectionExists }
In my dh.test.ts
const dhExport = require("./DensityHookReceive");
dhExport.checkDCollectionExists = jest.fn().mockImplementation(() => {});
test('check req dh is exists', () => {
dhExport.checkDExistsCallback(false, '[{}]');
expect(dhExport.checkDCollectionExists).toBeCalled();
});
In dh.js checkDExistsCallback function is invoked the checkDCollectionExists after satisfied the 'if' condition. When you look into the dh.test.ts file I mocked the checkDCollectionExists function in the beginning, but while running the test it did not invoke the mocked function it invokes the actual function. Can you help me to figure it out?
A function that is used in the same module it was defined cannot be mocked, unless it's consistently used as a method on an object that could be mocked, e.g.
if (dResp.length > 0)
module.exports.checkDCollectionExists();
instead of
if (dResp.length > 0)
checkDCollectionExists();
checkDCollectionExists needs to be either moved to another module, or two functions need to be tested as a single unit. It's database call that needs to be mocked.

Resources