I have been studying NodeJS for about a year and today I found something strange for me. We all know that in order to use a module (function, object or variable) in another module we must export and import it except for the native modules like String, Number, Promise, etc. I installed an external package for unit testing called Jest.
The strange thing here is that I have created two test modules called: logic.js and logic.test.js, in none I have imported the Jest module, however I can access all its methods. Let's show some code:
logic.js
module.exports.add = function(a, b){
return a + b;
}
logic.test.js
const lib = require('../logic')
test('Add - Should return the sum of two numbers', () => {
const result = lib.add(1,3);
expect(result).toBe(4);
});
As you can see in logic.test.js I have access to expect and test methods and i have not impoted nothing about Jest.
The questions here are:
How is this posible?
Its a good practice to do this with my modules
As Jonas W stated in the comments, they make use of the global variable that is common to all your application.
The use of the global variable is very simple
test.js
global.myObject = 'MyMan'
app.js
require('./test')
console.log(myObject)
Loading app.js will render MyMan
You might say that I actually import the test module and that Jest does not.
The thing is that you execute your node application using node yourFile.js but you instanciate your jests tests with the jest command line.
It's the jest command line that handles the binding between its framework (the expect and test methods.) and your script.
Is it a good practice?
I would say no. Except if you plan to make a library like Jest that have its own command line launcher and that you want to give tools like that to the users of your library.
The power of Node lives into the module organization, don't be afraid to use them.
Related
I've been creating an application using NodeJS ESModules (a.k.a import instead of require()) and the problem comes when I trying to test it - I am stuck on this for about a month now because I cannot find a good package to solve this "simple" issue.
Since I am using ESM, I cannot use libs such as rewire because, as it says on the docs, they don't support it. So I need a way to mock some dependencies during tests using the import syntax, and the only (trustable) package I've found that does the job is Jest.
I think that installing the whole Jest package to only use it's mock functionality would be overkill, so I've been trying to research packages on the internet to make it work. I came across a bunch of them (proxyquire, testouble, etc) and NONE of them worked. So by digging up Jest's repo I've found this jest-mock that is used under the hood, but I don't know how to use to mock those dependencies.
That leads to the question: which method/function does Jest calls when using jest.mock ?
I will give an example of what I want to test so, maybe, you guys can help me out (I am in a point that I can try anything, I just need to proceed with the application):
someFile.js
export const generateSomething = () => Date.now();
Creator.js
import { generateSomething } from './someFile.js';
export default () => {
const create = () => {
return { a: 1, b: generateSomething() };
}
}
So, I want to perform some unit tests on this Creator.js, then I need to mock this generateSomething function to return 0 (for example). How can I do it using this jest-mock (or any other package)? Does jest.mock call this package at all?
P.S: I don't want to use jest as testing lib, I am currently using #hapi/code with #hapi/lab
I have a file: browser-launcher.ts
import * as Browser from "#lib/browser";
class BrowserLauncher {
launch(options) {
browser = Browser(options);
}
}
export const browserLauncher = new BrowserLauncher()
How to mock library '#lib/browser' to test method launch of BrowserLauncher?
How to right to test variable browserLauncher in module browser-launcher.ts?
Sinon is not really a library that deals with your specific problem, which is substituting a module for a fake one at the module loader level, as that is very specific to the environment in which you are working. Sinon only deals with the actual creation of a substitute (a mock or stub implementation). Such module substitution is solved by specific third party libraries such as proxyquire, rewire and the likes and the dependant module you want to replace is called a "link seam" in testing literature.
You can see a how-to by us in the Sinon team for how to do this in CommonJS environments: https://sinonjs.org/how-to/link-seams-commonjs/.
Seeing the #lib/... string makes me think this is a webpack specific problem, in which case you should find some module replacement library that deals with webpack. One such library is inject-loader.
That being said, sometimes Sinon can be used to replace exports on some module, but this is totally environment specific! Spec conforming ES Modules export an immutable namespace, so you are not supposed to be able to override exports.
You can see Sinon's expected behavior in our test code, where you see that an export such as export default { foo(){} } can be stubbed (since its members are not immutable), whereas export function foo(){} cannot be stubbed.
The only way you can stub the exports then is by having your ES environment be non-compliant: producing exports that are writable or disabling the read-only nature.
Testing the module without using link seams
In another answer I detail two ways you can use plain dependency injection to do this without external tooling. You should check that out, as it is quite straight forward, but the downside is that it will require a small test-only change to your production code to facilitate it. It's a balancing act of pros and cons: change code or introduce more dependencies.
I have written a more elaborate example of this technique on the Sinon issue tracker you might want to check out, where I show how to optionally inject the dependencies.
I have a Node server with a utils file that looks like this:
update = () => {
//
};
module.exports = { update };
This works, but when I use the same file in a React app, I get the error
"'update' is not defined no-undef"
To fix this I must add const keyword. This makes no sense to me. Why is declaring the function needed only in React?
This is because the React you're using has come bundled with a linter which warns you of potential code quality problems. create-react-app is one of the React bundles which comes integrated with ESLint.
If you don't want to see the warning and don't want to use ESLint, you can build your project manually from scratch, rather than using a boilerplate setup like create-react-app.
If you installed ESLint for your Node project, you'd see the no-undef warning for Node code as well.
Note that what you're seeing is a linter warning, not a runtime error. If you ignore the warning and bundle and serve the project anyway, it will be still be runnable if it doesn't run in strict mode. (In strict mode, the use of an undefined variable without declaring it with const / let / var first will throw an error)
The linting rule being violated in the code is described at length here:
https://eslint.org/docs/rules/no-undef
For further reference on what's going on when you don't declare a variable (and why it probably isn't a good idea), see
What is the purpose of the var keyword and when should I use it (or omit it)?
If you're using modules, you should pretty much always be using explicit imports and exports only - don't implicitly create global variables (like you're currently doing with update = () => {), since that defeats the point of using modules.
I am just re-looking at Jest as it's been getting a lot of good reports. However struggling to find a good way the access internal functions to test them.
So if I have:
const Add2 = (n)=> n+2;
export default (list)=>{
return list.map(Add2());
}
Then if I was using Jasmine or Mocha I'd use rewire or babel-plugin-rewire to get the internal Add2 function like this:
var rewire = require('rewire');
var Add2 = rewire('./Adder').__get__('Add2');
it('Should add 2 to number', ()=>{
let val = Add2(1);
expect(val).toEqual(3);
});
However neither of them seem to work with jest and while there looks like an excellent mocking syntax I can't see any way to get internal function.
Is there a good way to do this, something I'm missing on the jest api or set up?
You can actually achieve this if you are willing to use babel to transform your files before each test. Here's what you need to do (I'll assume you know how to get babel itself up and running, if not there are multiple tutorials available for that):
First, we need to install the babel-jest plugin for jest, and babel-plugin-rewire for babel:
npm install --save-dev babel-jest babel-plugin-rewire
Then you need to add a .babelrc file to your root directory. It should look something like this:
{
"plugin": ["rewire"]
}
And that should be it (assuming you have babel set up correctly). babel-jest will automatically pick up the .babelrc, so no additional config needed there unless you have other transforms in place already.
Babel will transform all the files before jest runs them, and speedskater's rewire plugin will take care of exposing the internals of your modules via the rewire API.
I was struggling with this problem for some time, and I don't think the problem is specific to Jest.
I know it's not ideal, but in many situations, I actually just decided to export the internal function, just for testing purposes:
export const Add2 = x => x + 2;
Previously, I would hate the idea of changing my code, just to make testing possible/easier. This was until I learned that this an important practice in hardware design; they add certain connection points to their piece of hardware they're designing, just so they can test whether it works properly. They are changing their design to facilitate testing.
Yes, you could totally do this with something like rewire. In my opinion, the additional complexity (and with it, mental overhead) that you introduce with such tools is not worth the payoff of having "more correct" code.
It's a trade-off, I value testing and simplicity, so for me, exporting private functions for testing purposes is fine.
This is not possible with jest. Also you should not test the internals of a module, but only the public API, cause this is what other module consume. They don't care how Add2 is implemented as long as yourModule([1,2,3]) returns [3,4,5].
Given
3 Node.js projects Main - Framework - Repositories
Main has the two other projects connected via npm link.
In a test i wrapped the require in a method. I've got some problems resolving linked projects (details see below)
Simplified code looks like this:
module.export.resolve = function(file){
[...]//Some more logik to handle relative pathes
return require(file)
}
This works fine in most scenarios. I also worked out to get handled with relatives pathes (looking up for caller and apply pathes based on this path)
Now this is in Project Framework which is linked (npm link) to Project Main. Project main has also Project Repositories linked.
Now in Project Main i have:
require('ProjectRepositories/foo') // Works as expected
myRequire.resolve('ProjectRepositories/foo') // Returns MODULE_NOT_FOUND "Cannot find module 'ProjectRepositories/foo'
I assume the problem is that Repositories Project ist not linked in the Framework Project. But is there an other way than linking them ?
I'd prefer to have less dependencies. Any hints on that?
You are absolutely correct in that the reason why the Project Framework resolve does not work is because the requireFn being used from within that project only knows about the modules installed in that framework. This is because when you require a javascript file, node evaluates the script within the context of the module, and not the context of the current project (this is how dependency modules' requires work when from your top-level script).
What you can do, however, is provide a way for the framework resolver to use a user-specified require function to do its work, once it has transformed the paths.
module.exports.resolve = function(file, resolver) {
//Some more logik to handle relative pathes
resolver = typeof resolver == 'function' ? resolver : require;
return resolver(file)
}
Now in your code, you could do
myRequire.resolve('ProjectRepositories/foo', require);
So now your Project Main require will be used to resolve the file.
You can also take this a step further if you want and have the module be stateful and remember the resolver it's supposed to use.
var _requireFn = require;
module.exports = {
resolve: resolve,
setRequireFn: setRequireFn
};
function resolve(path) {
return _requireFn(path);
}
function setRequireFn(requireFn) {
_requireFn = requireFn;
}
On another note, I would be careful about using the term resolve because in node that's semantically used for looking up the correct file path to be required, a la require.resolve.
Finally, in terms of minimizing dependencies, I'd recommend including your subprojects in npm using github repos. This has worked pretty well for me in the past, unless your two subrepos are in a constant state of flux. See the install docs for more info.