Jest test functions that define variables in it - javascript

I want to write some Jest tests for JavaScript functions in a .mjs file. Within these functions, variables are defined by calling other functions. For example:
export function getCurrentVideo() {
var currentVideo = VideoList.GetCurrentVideo()
console.log(currentVideo);
return "This is the video:" + currentVideo
}
In this case I will receive undefined, right? Because VideoList.GetCurrentVideo can't be reached.
Test will be something like:
const getCurrentVideo = import('../').getCurrentVideo;
describe('getCurrentVideo', () => {
test('if getCurrentVideo will return "This is the video:" + currentVideo', () => {
expect(getCurrentVideo).toBe('This is the video:" + currentVideo');
});
});
I know you can add parameters to the function, but that will mean that I have to re-write the functions just for test purposes. It isn't my code, it's a huge project where the owner wants some tests for it.

You can mock function, assuming they work correctly (and test them separately):
const getCurrentVideo = import('../').getCurrentVideo;
describe('getCurrentVideo', () => {
it('if getCurrentVideo will return "This is the video:" + currentVideo', () => {
const currentVideo = 'testCurrentVideo';
window.VideoList = {
GetCurrentVideo: jest.fn(() => currentVideo)
}
expect(getCurrentVideo).toBe('This is the video:' + currentVideo);
});
});
Or you can provide a full context of VideoList, so that the two function are tested together.

Related

Javascript New Function on string script - Error accessing nested function

In angular project and learning to build a feature to run a custom script using the new function method.
I am able to access a custom function within the string. However is there a way to access the nested functions? The reasoning is I intend to have quite a number of functions accessible so it makes sense to pass it through a parent function.
Here is the code + stackblitz. Current I get an error 'nestFunc' undefined. Where nestFunc is the nested function.
https://stackblitz.com/edit/angular-bx5kia?file=src/main.ts
test() {
const script = `
var value = 1 +4;\n
console.log('value',value);\n
testFunc('Success');\n
testFunc().nestFunc2();\n
console.log("End Script");\n
`;
var testFunc = (msg?: string) => {
console.log('Test Function', msg);
var nestFunc1 = () => {
alert('hi');
};
var nestFunc2 = () => {
alert('hi');
};
};
const userFunction = new Function('testFunc', script);
userFunction(testFunc);
}

Attempting to mock child method, fails if parent module imports it using object destructuring

I was doing some basic jest unit testing in attempt to learn it more.
I have this issue I do not know how to explain
This file has the child function add
// FileB.js
const add = (a, b) => {
return a + b;
}
module.exports = {
add,
};
This file has the parent function addTen
// FileA.js
const { add } = require('./FileB');
const addTen = num => {
return add(10, num);
}
module.exports = {
addTen,
};
this is my test file, where I am trying to either check <mockedFunction/spy>.mock.calls or do toHaveBeenCalledWith to see if the inner child method, add, is being passed in 10,10 when i call addTen(10);
This file is not used in any real env, its simply me trying to learn jest + unit testing more.
// randomTest.js
const { addTen } = require('../src/FileA');
const fileB = require('../src/FileB');
describe('temp', () => {
it('temp', () => {
const addSpy = jest.spyOn(fileB, 'add');
addTen(10);
console.log(addSpy.mock.calls);
expect(addSpy).toHaveBeenCalledWith(10,10)
});
});
Now for the issue, the test fails, saying add was never called, or nothing passed in. I logged the add function within FileB
However, if I modify FileA in this way, by importing entore module instead of destructuring, the test passes and I cna log out the functions and everything and see it works
This is what works
// FileA.js
const fileB = require('./FileB');
const addTen = num => {
return fileB.add(10, num);
}
module.exports = {
addTen,
};
Why does this slight change work?

Cucumber JS: How to export/update global variable outside Given/When/Then step?

I try to update/export global variable (firstString) to use and validate it in 'Then' step.
How do I export it correctly? When I'm doing it this way, the firstString is undefined.
It works only when I export/import it inside steps. How can I update it globally and use it in 'Then' file?
helpers.js:
let firstString;
given.js:
let { firstString } = require('./helpers')
Given('I have first {string}', function (stringValue) {
return stringRequest(stringValue).then(response => {
firstString = response
});
});
module.exports = { firstString }
then.js:
firstString = require('./helpers').firstString
Then('blablabla {string}', function (stringType) {
console.log(firstString)
});
If I am correct in understanding what you want to do you are wanting to store data across steps. Todo that you will want to use the world instance that cucumber provides for you. You can access the world instance in steps via the this keyword.
So what you would do is
Given('I have first {string}', function (stringValue) {
return stringRequest(stringValue).then(response => {
this.firstString = response
});
});
Then('blablabla {string}', function (stringType) {
console.log(this.firstString)
});
For more information on the world instance check out https://github.com/cucumber/cucumber-js/blob/master/docs/support_files/world.md

Get function arguments from outside without executing this function in javascript

Is it possible to receive the arguments list of the function without executing it somehow? I can't find a normal solution unfortunately.
I'm working on some Dependency Injection system and I'd like to know arguments naming in the function (service) before I create the instance of it. Because I want to create the Dependencies first and then pass them into the function (service).
for ex:
function MyServiceA(myServiceB) {
say() {
...code.........
myServiceB.beep();
}
}
function MyServiceB() {
beep() {
...code.........
}
}
function Creator() {
// Is it possible to receive it somehow? this is the question
const argumentsList = MyServiceA
// iterate list and instantiate the ServiceB dependency and inject in into the MyServiceA
argumentsList.forEach(dependency => {
const dependencyInstance = new dependency()
new MyServiceA(dependencyInstance )
})
}
Thanks for any help!
You could turn the function into a string, and then parse that string to find the names of the arguments. AngularJs did something like this for its dependency injection. Be aware that if the code gets minified, the names will change and this can break your code.
function needsA(a) {
console.log("in needsA");
a();
}
function needsB(b) {
console.log("in needsB");
b();
}
function needsAandB(a, b) {
console.log("in needsAandB");
a();
b();
}
const dependencyLookup = {
a: () => console.log('in A'),
b: () => console.log('in B'),
}
function injector(fxn) {
const argNames = fxn.toString()
.match(/^function\s*[^\(]*\(([^\)]*)\)/)[1]
.split(',');
const dependencies = argNames
.map(str => dependencyLookup[str.trim()]);
fxn.apply(null, dependencies);
}
injector(needsA);
injector(needsB);
injector(needsAandB);
A useful article on angularjs's dependency injection: https://teropa.info/blog/2014/06/04/angularjs-dependency-injection-from-the-inside-out.html

'TypeError: is not a function' in Node.js

I'm getting the error while running the following code in Node.js
var assert = require('assert');
var request = require('request');
var index = require('./index');
it('verify javascript function', function(done) {
var v2 = index.AddNumbers(5, 6);
assert.equal(11, v2);
done();
});
The index.js file contain the following code:
function AddNumbers(a,b){
return a+b;
}
What am I doing wrong?
This happened to me many times because of circular dependency, check if you have 2 classes that are requiring each other, remove one of them from requiring the other and the issue should be solved
With NodeJS modules, to make something public, you have to export it. Add this to the end of index.js:
module.exports.AddNumbers = AddNumbers;
(That's using the old CommonJS modules. For ESM, it would be export AddNumbers;)
Here it is running on my machine:
$ cat index.js
function AddNumbers(a,b){
return a+b;
}
module.exports.AddNumbers = AddNumbers;
$ cat example.js
var index = require('./index');
var v2 = index.AddNumbers(5,6);
console.log(v2);
$ node example.js
11
I'm fairly a beginner at Node JS so I managed to get this error by importing a function like so:
const { functionName } = require('./function')
instead of like so:
const functionName = require('./function')
Editing my post to add an explanation since I've learned more node since I wrote it. If a module exports an object containing multiple functions functions like so:
module.exports = { functionName, otherFunction }
Then the function has to be deconstructed out of the object during the import, as in the first code snippet. If the module exports a single function or a default function, like so:
module.exports = functionName
Then tt must be imported directly, as in the second code snippet.
If you need to expose a specific component, function or a variable to public. You have to exports those components using JavaScript modules.
let add = (a,b)=>{
return ( a+b);
}
module.exports.add=add;
or if you want to expose multiple functions, you can do as follows.
let add = (a,b)=>{
return (a+b);
}
let subtract = (a, b)=>{
return (a-b);
}
module.exports={
add : add,
subtract : subtract
};
This is happening because two files are referencing each other i.e You are calling function (s) from file A in file B and vice versa which is called Circular Dependency.
Your "AddNumbers" function in the "index.js" file should be as follows,
function AddNumbers(a,b){
var addition = function(a, b){
return (a + b) ;
};
module.exports = {
additionResult: addition
};
}
And you need to call it in your "Node.js" file as follows
var assert = require('assert');
var request = require('request');
var index = require('./index');
it('verify javascript function', function(done) {
var v2 = index.additionResult(5, 6);
assert.equal(11, v2);
done();
});
This should work. Please note that you call the function by whatever the token name you exported the return value by (I use a different name here just for clarity). Almost everybody uses the same name as the function name so there are no confusion. Also in ES6, if you use the same name you can export as just,
module.exports = {
addition
};
instead of,
module.exports = {
addition: addition
};
since you use the same name. It is an ES6 feature.
I ran into the same problem while trying to follow a Nodejs tutorial by w3schools.
I copied the following code from them:
exports.myDateTime = function () {
return Date();
};
That, however, wouldn't work for me. What resolved the problem for me was adding module. before the exports keyword like this:
module.exports.myDateTime = function () {
return Date();
};
The most correct answer was from #shimi_tap. I want to reply it as comment, but doesn't have enough reputation, so I am gonna answer it using a simple example, like in this case below:
File A has 3 functions to process database activity: function
addDB, updateDB, and delData;
File B has 2 functions to process User activity on smartphone:
function addHistory, and editHistory;
Function updateDB in file A is calling function editHis in file B, and function editHistory is calling function updateDB in file A. This is what we called circular-dependency. And we need to prevent it by only giving output of state from editHistory and the rest will be processed inside file A.
//ORIGINAL FUNCTIONS which caused CIRCULAR DEPENDENCY
function updateDB() {
//process update function here
//call function in fileB
const history = require("fileB.js");
await history.editHistory(data).then((output) => {
if(output["message"] === "success"){
response = {
state: 1,
message: "success",
};
}
});
return response;
}
//THIS is the WRONG ONE
function editHistory() {
//process function to edit History here
//call function in fileA
const file = require("fileA.js");
await file.updateDB(data).then((output) => { //You should not call it here
if(output["message"] === "success") {
output = {
state: 1,
message: "success",
};
}
});
return output;
}
//==================================================//
//THE FIX
function updateDB() {
//process function here
const history = require("fileB.js");
await history.editHistory(data).then((output) => {
if(output["message"] === "success"){
await updateDB(data).then((output) => {
response = {
state: 1,
message: "success",
};
});
} else {
log("Error");
}
});
return response;
}
function editHistory() {
//process function to edit History here
// No more calling to function inside the file A
output = {
state: 1,
message: "success",
};
return output;
}
https://medium.com/visual-development/how-to-fix-nasty-circular-dependency-issues-once-and-for-all-in-javascript-typescript-a04c987cf0de
this post visualizes the circular dependency injection
like a child or nested file tried to import parent or top-level file
repo.js
service.js
there are 2 files
service.js uses repo.js file by importing
it works
but check in repo.js that it tried to import service.js file
it shows circular dependency injection warning
In my case the problem was the missing semicolon at the end of the lines.
const codec = JSONCodec()
(async () => {
for await (const message of subscription) {
const payload = codec.decode(message.data)
stompServer.send('/topic/update-event', {}, payload)
}
})()
This produced the following error:
TypeError: JSONCodec(...) is not a function
I was so used to writing code without semicolons, which led me to this problem with the bare NodeJs. As soon as I had put the semicolons, the problem disappeared.
A simple way I debugged this (After about 2 days of troubleshooting) was to actually see why 'x' is not a function. Basically, console.log(x) to see the actual object returned. Turned out I was conflicting x with another declared variable (happens especially when you use axios.res and req,res args.
Require the other file in function level.
fileOne.js
function main() {
const fileTwo = require('./fileTwo');
console.log("hello from file one");
}
module.exports = main;
main();
fileTwo.js
function main() {
const fileOne = require('./fileOne');
console.log("hello from file two");
}
module.exports = main;
main();
Now execute > node fileOne.js
Output:
hello from file two
hello from file one
One silly mistake I did was while exporting was:
module.exports = [module_name_1, module_name_2, ..., module_name_n]
The right way is:
module.exports = {module_name_1, module_name_2, ..., module_name_n}

Categories