I have a function that converts an array of 8 bit integers into a x-bit integer. This function has extensive unit tests in Jest. This function is called convertArrayIntoInt().
I have another functions that relies on convertArrayIntoInt() called getSignature().
/**
* #param {Uint8Array} fileContents The file contents
* #param {number} position The position in the file where the signature is.
* #return {number} the 32 bit signature
*/
function getSignature(fileContents, position) {
let signature = fileContents.slice(position, position + 4);
signature = convertArrayIntoInt(signature);
for(let s in Enums.Signatures) {
if(signature === parseInt(s)) {
return signature;
}
}
return Enums.Signatures.BAD_SIGNATURE;
}
I want to write a test for this function that tests whether Uint8Array.of(0x04, 0x03, 0x4b, 0x50), 0) returns 0x04034b50 or Enums.BAD_SIGNATURE. I heard that with unit testing, you don't want to test a function that relies on the output of another function. How do I go about doing this in Jest?
Update: Looks like I had some issue in my original response when I merged different files in to a single files I expected it to work as before but it didn't so make sure to put function(s) you want to inject in to separate file(s).
As mentioned earlier, unit test should just test functionality of function it self while mocking dependencies.
I moved functions in to separate files, in one file all functions are defined and in another test.
enums.js
export const Enums = {
Signatures: {
SIGNATURE_1: 1,
SIGNATURE_2: 2,
SIGNATURE_3: 3,
BAD_SIGNATURE: -1
}
};
convertArrayIntoInt.js
export function convertArrayIntoInt(array) {
// processing...
return array.length;
}
getSignature.js
import { convertArrayIntoInt } from "./convertArrayIntoInt";
import { Enums } from "./enums";
export const getSignature = (fileContents, position) => {
let signature = convertArrayIntoInt(
fileContents.slice(position, position + 4)
);
for (let s in Enums.Signatures) {
if (signature === parseInt(s, 10)) {
return signature;
}
}
return signature;
};
tests.spec.js
import { getSignature } from "./getSignature";
import { Enums } from "./enums";
import * as convertArrayIntoIntModule from "./convertArrayIntoInt";
it("Function check", () => {
// arrange
const spy = jest.spyOn(convertArrayIntoIntModule, "convertArrayIntoInt");
spy.mockReturnValue(Enums.Signatures.SIGNATURE_1);
// act
const expected = Enums.Signatures.SIGNATURE_1;
const actual = getSignature([1, 2, 3, 4, 5, 6], 1);
// assert
expect(spy).toBeCalledTimes(1);
expect(actual).toEqual(expected);
// clean up
spy.mockRestore();
});
You can take a look at this sandbox that I created to test dummy function similar to yours.
Yes, you have heard it right. A unit test should only test a single unit/function. What you can do is, you can make another unit test to check convertArrayIntoInt() function, and for this function, you can set dummy return values for convertArrayIntoInt() in order to verify other logic is working correctly or not.
You can spyOn convertArrayIntoInt function and mockReturnValue.
https://jestjs.io/docs/en/jest-object#jestspyonobject-methodname
Related
I am aware of import and export, but this doesn't work here because each time you call imported function it starts a new instance of the original file from which it was exported.
I have a JavaScript Code A running in node.js continously without restarting. As it runs, it creates its own data and stores it in arrays.
Please how do I call a function within code A from another file which will run in the same instance Code A is currently running in and that it will respect the stored data in arrays Code A has?
If I use import/export, it calls a function from within Code A, but it doesn't have any stored data in arrays that I gathered whilst running Code A.
Thank you very much.
EDIT: Adding sample code
MAIN FILE
let calculationArray = []
function add(x, y) {
if ( calculationArray.includes("abcde") ) {
console.log("Calculation is on timeout", calculationArray)
}
else {
calculationArray.push("abcde")
console.log(x + y)
return x + y;
}
}
module.exports = { add }
setInterval(add, 3000, 5, 5)
SECOND FILE WHICH CALLS FUNCTION FROM MAIN FILE and doesn't respect the fact that calculationArray in the original file already has "abcde". It simply starts it own fresh instance with empty array
const f = require('./calculation.js');
f.add(10, 5);
I'm not sure how you exactly load and call your scripts... so I tried to make something I could reason about of — out of your code.
Using ES6 modules (which use export and import statements), say you have:
calculation.js — which adds every second 5+5 to a file-internal array
other.js — which imports calculation.js and returns the result of add(100, 100) 200
a main index.js which calls:
calculations.js the first time after 3500ms
other.js after 6500ms
Here are the files and than some explanation on what happens using ES6 dynamic import():
calculation.js
// calculation.js
const calculationArray = [];
const add = (x, y) => {
const result = x + y;
calculationArray.push(result);
console.log(calculationArray.reduce((a, v) => a + v), 0);
return result;
};
// add 5+5 every 1sec
setInterval(add, 1000, 5, 5);
export { add }
other.js
// other.js
import { add } from "./calculation.js";
// export the result of 100 + 100
export default add(100, 100);
and finally:
index.js
// index.js
const delayedImport_calculation = async () => {
const { add } = await import("./calculation.js");
add(10, 5);
};
const delayedImport_other = async () => {
const { default: otherResult } = await import("./other.js");
console.log(otherResult);
};
setTimeout(delayedImport_calculation, 3500);
setTimeout(delayedImport_other, 6500);
If you call the main script like: $ node ./index.js from the terminal, you should expect the following console logs:
(Nothing for 3500ms)
15 after 3500ms since that's when calculation.js was first imported into index.js and called the function add(10, 5);
25 (after 1sec)
35 (also after 1sec since the interval inside calculation.js)
235 since other.js was dynamically imported into index.js and added 100 to the array
other: 200 200 since other.js exports just the result of add(100 + 100)
245, 255, 265, etc... on intervals of 1sec — all proving the point that the array values are updated as expected.
as you can see above, the .reduce() on the calculationArray returns the expected added values in console.log
Another simpler example (which might be closer to what you have?) with only two files:
calculation.js that:
every 1sec adds 5+5 to the array
exports a default add() function and a getAddResult() method
index.js which imports both the default and the helper methods
logs every second the sun of the array values using getAddResult()
calls add(1000, 1000) after 5sec
calculation.js
const calculationArray = [];
const getAddResult = () => calculationArray.reduce((a, v) => a + v, 0);
const add = (x, y) => {
const result = x + y;
calculationArray.push(result);
return result;
};
// add 5+5 every 100ms
setInterval(add, 100, 5, 5);
export default add
export { getAddResult }
index.js
import add, { getAddResult } from "./calculation.js";
setInterval(() => {
console.log(getAddResult())
}, 1000);
setTimeout(() => {
add(1000, 1000);
}, 5000);
the log this time would be approximately like:
90, 180, 280, 370 (in intervals of 1sec)
2470, (after ~5000ms) proving the point that the values in array are updated
2560, 2660, etc... (also in intervals of 1sec)
PS:
for the above to work you either need to use "type": "module" in package.json, or just name all your files extensions as .mjs
The problem is rather simple. We need to imbue a function with a parameter, and then simply extract that parameter from the body of the function. I'll present the outline in typescript...
abstract class Puzzle {
abstract assign(param, fn): any;
abstract getAssignedValue(): any;
async test() {
const wrapped = this.assign(222, async () => {
return 555 + this.getAssignedValue();
});
console.log("Expecting", await wrapped(), "to be", 777);
}
}
Let's set the scene:
Assume strict mode, no arguments or callee. Should work reasonably well on the recent-ish version of v8.
The function passed to assign() must be an anonymous arrow function that doesn't take any parameters.
... and it's alsoasync. The assigned value could just be stored somewhere for the duration of the invocation, but because the function is async and can have awaits, you can't rely on the value keeping through multiple interleaved invocations.
this.getAssignedValue() takes no parameters, returning whatever we assigned with the assign() method.
Would be great to find a more elegant solution that those I've presented below.
Edit
Okay, we seem to have found a good solid solution inspired by zone.js. The same type of problem is solved there, and the solution is to override the meaning of some system-level primitives, such as SetTimeout and Promise. The only headache above was the async statement, which meant that the body of the function could be effectively reordered. Asyncs are ultimately triggered by promises, so you'll have to override your Promise with something that is context aware. It's quite involved, and because my use case is outside of browser or even node, I won't bore you with details. For most people hitting this kind of problem - just use zone.js.
Hacky Solution 2
class HackySolution2 extends Puzzle {
assign(param: any, fn: AnyFunction): AnyFunction {
const sub = Object(this);
sub["getAssignedValue"] = () => param;
return function () { return eval(fn.toString()); }.call(sub);
}
getAssignedValue() {
return undefined;
}
}
In this solution, I'm making an object that overrides the getAssignedValue() method, and re-evaluates the source code of the passed function, effectively changing the meaning of this. Still not quite production grade...
Edit.
Oops, this breaks closures.
I don't know typescript so possibly this isn't useful, but what about something like:
const build_assign_hooks = () => {
let assignment;
const get_value = () => assignment;
const assign = (param, fn) => {
assignment = param;
return fn;
}
return [assign, get_value];
};
class Puzzle {
constructor() {
const [assign, getAssignedValue] = build_assign_hooks();
this.assign = assign;
this.getAssignedValue = getAssignedValue;
}
async test() {
const wrapped = this.assign(222, async () => {
return 555 + this.getAssignedValue();
});
console.log("Expecting", await wrapped(), "to be", 777);
}
}
const puzzle = new Puzzle();
puzzle.test();
Hacky Solution 1
We actually have a working implementation. It's such a painful hack, but proves that this should be possible. Somehow. Maybe there's even a super simple solution that I'm missing just because I've been staring at this for too long.
class HackySolution extends Puzzle {
private readonly repo = {};
assign(param: any, fn) {
// code is a random field for repo. It must also be a valid JS fn name.
const code = 'd' + Math.floor(Math.random() * 1000001);
// Store the parameter with this code.
this.repo[code] = param;
// Create a function that has code as part of the name.
const name = `FN_TOKEN_${code}_END_TOKEN`;
const wrapper = new Function(`return function ${name}(){ return this(); }`)();
// Proceed with normal invocation, sending fn as the this argument.
return () => wrapper.call(fn);
}
getAssignedValue() {
// Comb through the stack trace for our FN_TOKEN / END_TOKEN pair, and extract the code.
const regex = /FN_TOKEN_(.*)_END_TOKEN/gm;
const code = regexGetFirstGroup(regex, new Error().stack);
return this.repo[code];
}
}
So the idea in our solution is to examine the stack trace of the new Error().stack, and wrap something we can extract as a token, which in turn we'll put into a repo. Hacky? Very hacky.
Notes
Testing shows that this is actually quite workable, but requires a more modern execution environment than we have - i.e. ES2017+.
Say I have some code like this:
function a(...numbers) {
return numbers.map(n => b(n));
}
function b(n) {
return n+1;
}
I've been looking at ways I would test like code like this, specifically to test the functionality of a without actually calling b.
One option is to use dependency injection, and to pass function b as a parameter.
ie.
function a(...numbers, _b=b) {
return numbers.map(n => _b(n));
}
But of course, the rest operator won't allow me to tack an argument on the end.
And I don't want to put the function argument first - because then the developer is having to have to pass function b in every time, or whatever, or pass a null value or similar.
Is there a way you could achieve this functionality?
rest parameters can only work as the last argument accepted by a function, it captures all argumets that were not declared in the function parameter. You can actually let go of the rest parameter and pass in an array
function a(numbers, _b = b) {
return numbers.map(n => _b(n));
}
function b(n) {
return n+1;
}
console.log(a([1,2,3,4], f => f * 1));
Function.prototype.bind() (kinda) solves this!
//The convention we will use here is that developers shouldn't use the
// _underscore methods in production.
export const _a = function(_b, ...numbers) {
return numbers.map(n => _b(n));
};
export const b = function(n) {
return n+1;
}
export const a = _a.bind(null, b);
console.log(a(1,2,3)) //[2,3,4]
This also has the advantage of that you're hiding the injected function from the developer.
Now how would you test this?
You have to test the _underscore method, so something like:
import { _a } from "../functions";
describe("_a", () => {
it("_a(1,2,3) calls _b three times.", () => {
const mockFn = jest.fn();
const a = _a.bind(null, mockFn);
a(1, 2, 3);
expect(mockFn.mock.calls).toHaveLength(3);
})
});
If you're interested - I've started a Github repo with a more fleshed out example of this approach here.
If anyone has a tidier way of doing this - I'm all ears.
I have a react function returning a promise from axios, and I need to encode an equation type string is being passed to it.
const { equation } = this.state;
axios.post(`${this.state.rootUrl}/integrate`, { equation }).then(some other code)
I want to encode the string equation before passing it to the API for a query.
I have tried the following but it didn't work.
axios.post(`${this.state.rootUrl}/integrate`, { encodeURIComponent(equation) })
I also tried this:
const { equation } = this.state;
const { test } = encodeURIComponent(equation);
axios.post(`${this.state.rootUrl}/integrate`, { test }).then(some other code)
This also didn't work.
Here's the full code of the function where I'm trying use this:
handleSubmit = (e) => {
e.preventDefault();
const { equation } = this.state;
// const { test } = encodeURIComponent(equation);
axios.post(`${this.state.rootUrl}/integrate`, { equation })
.then((response) => {
const data = response.data;
this.setState({ data: data });
console.log(equation);
if (data != null) {
this.setState({input: data.response[0]});
}
}
}
In your original example, you're using the shorthand syntax for setting object properties - the following two lines of code are equivalent:
{ equation }
{ equation: equation }
Your second two examples don't do the same thing! In example two, you're trying to use the shorthand with a method call, which won't work. In example three, you're trying to destructure the return value of encodeURIComponent(equation), which also won't work (it returns a string).
Fawaz's first example almost works, but there's a subtle difference in behavior - because they've named the variable test, the key of the object being passed to Axios will also be test. Remember, these are equivalent:
{ test }
{ test: test }
Presumably, your API is expecting something called equation, not test, so this won't work.
To get the right behavior, you should make sure the object you're passing has the right key:
const test = encodeURIComponent(equation);
axios.post(`${this.state.rootUrl}/integrate`, { equation: test })
// or
axios.post(`${this.state.rootUrl}/integrate`, { equation: encodeURIComponent(equation) })
There seems an issue in using the shorthand. Try like this :
const test = encodeURIComponent(equation); // no braces here
axios.post(`${this.state.rootUrl}/integrate`, { test }).then(some other code)
or
axios.post(`${this.state.rootUrl}/integrate`, { test: encodeURIComponent(equation) })
I am using moment and need to stub current time to always return 1/1/18 for example. So when I call moment() or moment.now() I should receive a moment object with the time set to 1/1/18.
Now I could override moment.now but then anything else that uses moment will have this stub due to module caching, which is not what I want.
So I have wrote a momentWrapper that copies moment's protoypes and defines a new constructor. The problem is when calling moment() it doesn't use the new defined prototype for now, as I'm simply calling moment's constructor. I need to copy moment and change it's now protoype in my new consturctor I'm just not sure how to do that.
This is what I have so far:
/**
* Wraps moment so that alternate time can be set as the 'now' time.
* This idea was grabbed from fs-extra https://github.com/jprichardson/node-fs-extra
* #module lib/momentWrapper
*/
var moment = require('moment');
function momentWrapper() {
return moment.apply(this, arguments);
}
assign(momentWrapper, moment);
momentWrapper.prototype = Object.create(moment.prototype);
momentWrapper.prototype.now = newNow;
momentWrapper.prototype.constructor = momentWrapper;
function newNow() {
return +new Date('1995-12-17T03:24:00');
}
/**
* Loops through given arguments and assigns them to a object
* Credit: fs-extra https://github.com/jprichardson/node-fs-extra/blob/fab376ec2bd4d4420806644c1b37a7ac487d17ec/lib/util/assign.js
* #function assign
* #return {Object} Mutated object with copied keys
*/
function assign () {
const args = [].slice.call(arguments).filter(i => i);
const dest = args.shift();
args.forEach(src => {
Object.keys(src).forEach(key => {
if (key === 'now') {
dest[key] = newNow;
}
else {
dest[key] = src[key];
}
});
});
return dest;
}
module.exports = momentWrapper;
Testing file:
var momentWrapper = require('./momentWrapper');
var moment = require('moment');
console.log(momentWrapper()); // Not working
console.log(momentWrapper('12/25/18', 'MM/DD/YY')); // Working
console.log(momentWrapper(momentWrapper.now()).format()) // Working
console.log('----------------------------------------');
console.log(moment()); // Working not being overridden
console.log(moment(moment.now()).format()) // // Working not being overridden
Output:
moment("2018-01-27T15:56:50.928")
moment("2018-12-25T00:00:00.000")
1995-12-17T03:24:00-05:00
----------------------------------------
moment("2018-01-27T15:56:50.938")
2018-01-27T15:56:50-05:00
Depending on your use case, rather than using a wrapper; you could temporarily replace the now function with a custom stub and when you're finished using it, just put the original one back.
var moment = require("moment");
// prepare before testing
moment.originalNow = moment.now;
moment.now = () => +new Date('1995-12-17T03:24:00');
console.log([
moment().format(),
moment(moment.now()).format(),
moment('12/25/18', 'MM/DD/YY').format()
]);
// clean after testing
moment.now = moment.originalNow;
moment.originalNow = undefined;
console.log([
moment().format(),
moment(moment.now()).format()
]);
Its so obvious that i havent seen it. You dont even need to play with inheritance, just clone the moment object and rewrite the now property:
const orig = require("moment");
const moment = (...args) => args.length === 0 ? moment.now() : orig(...args);
Object.assign(moment, orig);
moment.now = () => orig('1995-12-17T03:24:00');
export moment;