Jest: automock modules, but only those defined in __mocks__, rather than all - javascript

TL;DR
I would like to have some kind of automock feature enabled, but only (and only!) for the modules that I have explicitly defined in a corresponding __mocks__ folder. Is there a way for this in Jest?
General advices and suggestions are also welcome.
A bit of context: (optional)
Turns out I was totally misunderstanding Jests automock feature. Btw, looking back now, I don't understand why, 'cause docs are pretty clear on what it actually does:
This option tells Jest that all imported modules in your tests should be mocked automatically.
As if I just noticed ALL keyword. Maybe I was just thinking - but it doesn't makes sense to have an automock even for the imported function that I'm actually going to test here, does it? Like obviously I would like to automock third party stuff from node_modules, but not my own code. And it turns out that:
Note: Node modules are automatically mocked when you have a manual mock in place (e.g.: __mocks__/lodash.js).
Note: Core modules, like fs, are not mocked by default. They can be mocked explicitly, like jest.mock('fs')
So it's kind of doing the opposite of what I thought it was doing.

If I understand correctly, you have certain mock files in your __mocks__ folder which you would like to be globally replaced whenever they're needed as part of a test file imports.
Jest has a means of configuring that. If you go through the Jest Configuration Documentation, you'll find a property named moduleNameMapper which takes an object with the keys being regular expressions and the values being the paths for the mock for the corresponding modules which match the regex.
In your jest.config.js, you would need to add a separate parameter and specify the mock pattern and path.
moduleNameMapper: {
"/^bootstrap/": "<root>/tests/__mocks__/bootstrapMock.js",
"/^axios/": "<root>/tests/__mocks/axiosMock.js"
}
You can find more info about the usage in the Jest docs.
However, if you do not want to go through all this, this is also achievable by placing a __mocks__ folder next to your node_modules folder. However, as defined in the documentation for Manual Mocks:
If we want to mock Node's core modules (e.g.: fs or path), then explicitly calling e.g. jest.mock('path') is required, because core Node modules are not mocked by default.

Related

How to mock library by SinonJs?

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.

Mock vs Import Then Mock

I'm trying to understand when I can just import a mock vs when I need to import the mock and still use jest.mock in the test file. I'm looking at the manual-mocks example from Jest's Github.
One Step Module Mocking
In the Lodash test, Lodash is mocked in the __mocks__ directory using createMockFromModule, exported, and simply imported using the standard module import and used directly in the test (no additional mocking).
Two Step Mocking
In that same project, the User model is exported and there is a separate User mock file. But in the User mocked test, the User is imported but there is an additional step using jest.mock('../models/user');
My Question/Confusion
Why would the Lodash test not require the additional jest.mock in the test file, or why does the User test require it? In the project, it seems like I can test both actual and mocked User implementation, but Lodash will only use the mocked implementation, even though both are created/exported using createMockFromModule in the __mocks__ directories.
The difference is that lodash is Node module and user is local module, the latter needs jest.mock('../models/user') in order for a mock from __mocks__ to be used.
As the documentation states,
If the module you are mocking is a Node module (e.g.: lodash), the mock should be placed in the __mocks__ directory adjacent to node_modules (unless you configured roots to point to a folder other than the project root) and will be automatically mocked. There's no need to explicitly call jest.mock('module_name').
Warning: If we want to mock Node's core modules (e.g.: fs or path), then explicitly calling e.g. jest.mock('path') is required, because core Node modules are not mocked by default.
This allows to avoid accidental collisions between mocks for NPM packages and local modules of the same name.

Exporting outside webpack

This is just something I thought today and I didn't see a lot of information so I'm going to share this weird cases and how I personally solved them (if there's a better way please comment, but meanwhile this might help others ^^)
In a regular module, you would do something like this to export your function/library/object/data:
// regular NodeJS way:
module.exports = data;
// ES6 way
// (will get transpiled to the regular way using the module variable by webpack)
export data;
default export data;
When compiling the library usually babel or tsc are used, but if for any reason you want not only to compile (transpile) your library but also pack it using webpack, you will encounter this case.
As you know, in a webpack bundle the module variable is local to the bundle (every module/file gets wrapped with a function where module is a parameter = local variable), so nothing really gets exported outside the bundle, is just nicely managed by webpack.
That means that you can't also access the contents using the regular require/import methods.
In some case you might find necessary to export outside webpack. (i.e. you are trying to build a library using webpack and you want it to be accessible by other people). This basically means you need to access the original module variable, but webpack doesn't expose it like it happened with __non_webpack_require__.
See also: Importing runtime modules from outside webpack bundle
The solution is to create our own __non_webpack_module__ (as webpack does with __non_webpack_require__.
How I did it is using webpack.BannerPlugin to inject some code outside the bundle. This code is prepended to the build after the minification is done, so it's preserved safely.
In your webpack.config.js:
plugins: [
new BannerPlugin({
raw: true,
banner: `const __non_webpack_module__ = module;`,
}),
]
And again, if you are using TypeScript, in global.d.ts:
declare const __non_webpack_module__: NodeModule;
And now, you can do something like this in your code:
__non_webpack_module__.exports = /* your class/function/data/whatever */
This will allow to import it as usual from other files
Tip: You might want to look at BannerPlugin to check other options, like include or exclude so this variable is only generated on the desired files, etc.

Webpack, eval certain modules during the compilation

I'd like to create a Webpack loader (or plugin) which would allow to replace a call to a certain method to the result returned by this method during the compilation. For example, let's assume I have a file named transpile_time.js:
import {SomeUsefullStuff} from 'a_very_cool_lib'; //<-- this should be resolved correctly
module.exports = function{
//let's assume that 'a_very_cool_lib' is used somewhere here
return 'console.log(\'tralala\')';
}
And, I have another another file named App.ts:
import {default as tt} from './transpile-time';
....
function someFunc(){
...
tt(); // <------ I want this code to be replaced to
// 'console.log(\'tralala\')'
// during the compilation process
}
For App.js my custom loader is used.
With the help of Esprima and Estraverse and Escodegen I'm able to manipulate the sources, but at some moment I need to resolve the dependency
import {default as tt} from './transpile-time';
dynamically in my loader with respect to the fact that it can have it's own dependencies as well. In the example above,
import {SomeUsefullStuff} from 'a_very_cool_lib';
should be resolved correctly. Webpack Loader API offers loadModule method, this gives the sources and the module object. The latter theoretically contains dependencies with their sources and I can go through them and replace every import statement with the corresponding source code and then use eval. But that is quite ugly, I would prefer to let Webpack doing this job. For example, there is a compilation object, it seems like it is possible to insert some hooks, but this is not described well. Does anyone have an idea?
P.S. I'm going to use it all for template resolving and code generation.
Update:
After doing a small investigation on how Weback works internally (this article was quite helpful), I think that it would be better to use a plugin instead of a loader. And, probably, I don't need Esprima and other tools because Webpack also builds AST at some stage. But still I don't know how to use it.

Node.js require() vs RequireJS?

Hello with RequireJS I can set a base path like this: base : './app/' so when I am in ./app/foo/bar/ for example and I have a script where I use require('foo'); RequireJS then would search for ./app/foo.js and not in node_module folder or in ./app/foo/bar/foo.js this comes handy when you have a kind of structure where it would be much cleaner for you as a developer to see the dependencies instead of having ../../foo.js. I could have ./app/foo.js and ./app/foo/foo.js and ./app/foo/bar/foo.js it would be much more cleaner to have:
require('foo');
require('foo/foo');
require('foo/bar/foo');
rather than:
require('../../foo');
require('../foo');
require('./foo');
Now you could say why not change the name and not have foo everywhere, let's say that we can't for any reason…
Another lack of feature that I see in node's require method against RequireJS is the ability of setting path mapping, if I have a directory named ./app/super-sized-directory-name/ in RequireJS I could simply do 'big-dir' : 'super-sized-directory-name' and then I could simply use require('./app/big-dir/foo') with Node.js's require method this is not possible as far as I know…
--alias, -a Register an alias with a colon separator: "to:from"
Example: --alias 'jquery:jquery-browserify'
You can register aliases with browserify, so that covers your renaming.
As for your rooted absolute paths, that can't really be done. As mentioned modul8 has a namespacing mechanism to solve this.
I would recommend you pong SubStack in #stackvm on freenode and ask him directly.
It may or may not help you, but I believe the Dojo Frameworks AMD Loader is API compatible with RequireJS and providing you are using a new microkernel does not pollute the global namespace.
I believe it only has require() and define() in the global namespace now.
Anyway their method of dealing with this is to do something like:
require(["dojo/node!util"], function(util){
// Module available as util
});
The documentation is at http://dojotoolkit.org/reference-guide/1.8/dojo/node.html
Use uRequire which provides a 'bridge' between nodejs require and AMD define modules, without reinventing the wheel (it is build on top of the two standards). It basically converts modules from AMD or commonJS format to the other format or UMD that runs smoothly on both nodejs & the browser.
It is also translating dependency paths with flexible path conventions, so you can have either '../../foo' or 'bar/foo' depending on which makes more sense at the point you are at.
Your AMD or UMD modules are loaded asynchronously on browser (using AMD/requireJs or other AMD loader) and on node the asynchronous require(['dep1', 'dep2'], function(dep1,dep2){...}) is also simulated.

Categories