Testing a file that calls its own function - javascript

I have a file that is the entry-point to my application;
const main = async () => {
// Do stuff
}
main().then();
I understand that I can export the main function, delete the call in the first file and in a separate file import and call the function there;
const entry = require('./rewritten-entry.js');
entry.main().then();
But for arguments sake lets say I really want to test this first file's main function. As soon as I import the file in my test, the main function will execute before I even reach the test code;
const entry = require('./original-entry.js') // <-- main function executes here
const test = {
// Do test stuff
}
For a test, I don't want this to happen. Likewise I don't want to mock the function since it is exactly what I want to test.
I see this common 'init' logic all over the place so I imagine there is a way to test this.

Related

How to test an internal function

I'm trying to create a unit test to cover an internal function
fileA.js
module.exports.functionA = () {
const functionB = () {
// do something
}
functionB()
}
test.js
const { functionA } = require('fileA')
...
it('runs functionB', () => {
functionA()
expect(...).toHaveBeenCalled()
}
How do I access it?
There are two possibilities here (your situation looks like the first one, but is also clearly simplified for the purposes of the question).
Either:
functionB is entirely private to functionA, part of its implementation, which means you can't access it to test it (directly). Instead, test functionA, which presumably uses functionB as part of the work it does (otherwise, there's no point in an entirely private function in functionA).
or
functionA exposes functionB in some way (for instance, as a return value, or as a method on a returned object, or by setting it on an object that's passed in, etc.), in which case you can get it in whatever way functionA provides and then test it.
(Again, I think yours is the first case.)

Trying to export function without success with JavaScript, Node.js

I'm trying to learn Node.js and I have a pretty good app now, but I wanted to implement a library (venom-bot) to send some text messages. My problem is that I'm having trouble trying to use functions outside the primary file. Here is my code:
// Dependencies
const server = require("./lib/server")
const venom = require("venom-bot")
// Declare the app
const app = {}
// Init function
app.init = () => {
// Start the server
server.init()
}
// Init venom-bot
venom.create().then((client) => start(client))
// A function to test if I can send the message
async function testing(msg) {
await msg.sendText(...some code here...)
}
function start(client) {
app.msg = client
// Here, if I pass app.msg as an argument, works
// My problem is that I can't use app.msg outside of here,
// even with the module.exports down there
// (I'm trying to use it on a helpers.js file).
testing(app.msg)
// Execute the application
app.init()
}
// Export the app
module.exports = app
On the helpers.js file I'm requiring it this way:
// Dependencies
const app = require("./index.js")
// A function to test
async function sendMsg(msg) {
await msg.sendText(...some code here...)
}
helpers.send = () => {
sendMsg(app.msg)
}
module.exports = helpers
Whenever helpers.send gets invoked, it should correctly use the async function right above the helpers.send passing the app.msg as argument, at least I think it should. What I'm missing here?
The error I got is:
(node:18148) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'sendText' of undefined
On testing purpose, I'm trying to simple call that helper when I receive a get request to a specified route.

Javascript ES6 reuse exports with callback

Let's say I have 2 JavaScript files like the followings and they both import from fileimport.js(reusability)
The goal is, the fileimport.js has an event listener runs on each page. I want to call custom functions for each of the pages after the event runs.
file1.js
import * as fileimport from './fileimport';
...
callback(){
//run custom function
}
file2.js
import * as fileimport from './fileimport';
...
callback(){
//run custom function
}
fileimport.js
...
export ReusableFunc1(){
....
}
export ReusableFunc2(){
....
}
export Func3{
form.addEventListener('submit', function (e) { callback })// I want to call callback() for each of the pages that import this.
}
I tried adding the callback() inside the fileimport.js and override in the regular pages but it did not work out(it did not call the regular pages function). How can I achieve something like this.
If you have a file A.js which provides code, that is used in B.js and C.js, than this file loaded and parsed only once. So a function call within A.js will be triggered only once, when the file is loaded.
If you want to call a function, provided by A.js whenever code from A.js is included somewhere, you cannot use an event listener, since there isn't any.
One thing you always can do is to trigger the desired function »manually« whenever you import A.js.
//A.js
const fx1 = () => {}
const fx2 = () => {}
const callback = () => {}
export { callback }
export default { fx1, fx1 }
//B.js
import { * as CodeFromA, callback } from 'A.js';
callback();
Another thing could be to change the export of A.js to something like:
export default () => {
callback();
return { ReusableFunc1, ReusableFunc2 }
}
This way you can:
import CodeFromA from 'A.js'
const ReusableFx = CodeFromA();
That would ensure that you get error if you try to use any of those ReusableFunc* without triggering the callback before.
The problem is callback is not defined in fileimport.js. You would need circular dependency in order to achieve that, but I suggest you treat them with care, since, quoting this relevant article which I suggest you read:
They are not always evil, but you might want to treat them with special care. They cause tight coupling of the mutually dependent modules. These kinds of modules are harder to understand and reuse as doing so might cause a ripple effect where a local change to one module has global effects.
If you don't need to call the callback function at import time, you shouldn't give you many problems though.
That being said, this would be the code to achieve what you want with a function:
file1.js
import * as fileimport from './fileimport';
...
export function callback() {
//run custom function
}
fileimport.js
import * as file1 from './file1';
ReusableFunc1(){
....
}
eventListener() {
file1.callback();
}
As you can see, the problem remains if you have a lot of callback functions. If the number of functions you need to call increases, I suggest you change your code architecture not to have so much dependency from other modules.

Expect fs.watch callback to be called in Jasmine

I have a class that accepts a callback in it's constructor. It also has a method loadFile that gets the content from the file, modifies it a bit and calls the callback with the modified content. Up to this point a class can be easily tested.
Now, I want to add a watch feature. loadFile will do what I just described and will start watching a file. Every time the file changes, the process will be repeated (load the file -> modify the content -> call the callback). I try to test it using Jasmine but I cannot make it work.
The simplified code looks like this:
const path = require('path')
const watch = require('fs').watch
const write = require('fs').writeFile
class Loader {
constructor (callback) {
this.callback = callback
}
loadFile (file) {
watch(file, (p, e) => {
console.log('path changed', p, e)
// do some important stuff here
this.callback(p)
})
}
}
describe ('Loader', () => {
var loader
var callback
beforeEach(() => {
callback = jasmine.createSpy('callback')
loader = new Loader(callback)
})
it('converts a markdown file', () => {
// this file already exists
const file = path.join(__dirname, 'md', 'header.md')
loader.loadFile(file)
write(file, 'hello', 'utf8')
expect(callback).toHaveBeenCalled()
console.log('end of test')
})
})
The command line result is:
Started
end of test
path changed change header.md
path changed change header.md
F
Failures:
1) Loader converts a markdown file
Message:
Expected spy callback to have been called.
Stack:
Error: Expected spy callback to have been called.
at Object.it (/path/to/spec/callback.spec.js:34:22)
1 spec, 1 failure
Finished in 0.015 seconds
This test does not load actual content, I simplified it to show the problem I encountered: the callback is called, but after the expectation.
I've spent a lot of time figuring out what is wrong but I failed. I think I've tried every possible combinations of using done() that Jasmine provides, but I didn't have any luck. I would appreciate any help with this.

How to use Node.js global variable in mocha tests

I have got a problem with mocha tests around global object I'm using with Node.js.
In index file, I set value to global variable
// index.js
global.enums = enumTemp
export default app
And then used it in another file
// other.js
status = global.enums.object.status
It's REST API and runs well if I made a request to a server. However, when I use a Mocha test, it seems to I cannot get the value for Node.js global variable. Any idea everyone?
I have found a solution that works for me by using Mocha hooks to set global variable just for testing:
// setup.test.js
import MyFunc from '../helpers/my-func'
before((done) => {
MyFunc.then((variable) => {
global.variable = variable
done()
})
})
We can use the global.variable in the test just like in the real code.
You should get rid of the globals because they're ugly, which will probably solve your problem as well.
There's a somewhat little known fact about the way Node.js require() works: Modules are cached on the first require. This makes it possible to run costly calculations (or fetch something from the database) and have it cached on subsequent uses of the module.
Observe this example:
randomnumber.js
const calculateRandomNumber = limit => {
console.log('calculateRandomNumber called');
return parseInt(Math.random() * limit);
};
module.exports = calculateRandomNumber(1000);
other.js
module.exports = require('./randomnumber');
test.js
const randomnumber = require('./randomnumber');
const other = require('./other');
console.log(randomnumber);
console.log(other);
This will output the same random number twice and calculateRandomNumber is called only once, even though the randomnumber module has been required in different places.

Categories