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.
Related
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.
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.
I've done allot of reading and video watching on the topic of dependency injection in a Node.js application and I am yet to find the answer to this question:
What are the downsides/cons of passing an object of dependencies throughout a Node.js app versus importing?
Lets say in my index.js file I import 10 modules:
const module1 = require('module1')
const module2 = require('module2')
const module3 = require('module3')
...
const doSomeWork = require('./doSomeWork')
I then add these modules to an object:
const deps = {
module1,
module2,
module3,
...
}
Lets say I call an imported function in my index.js file called doSomeWork(). doSomeWork requires module2. I could import module2 in the doSomeWork file, or I could call doSomeWork(deps) and pass in the deps object. Passing in deps makes it easier to test doSomeWork, since if module2 was a database module, I could mock up a fake DB.
So I am wondering, what are the downsides/cons to importing all the modules your app needs in your root index file for example and passing a deps object to the different components that need those modules?
Are there performance hits, will I loose out on some module benefits of importing I am not aware of?
Thanks!
I would recommend against passing the dependencies from your index.js into dependencies. Specialized modules like sinon provide stubs which can override require()-d dependencies for your unit testing pleasure. Managing the dependency tree yourself will more likely cause you more headaches than you will save since you will have an additional step in interfacing with any existing javascript API.
In addition, your index.js will likely become monolithic and hard to decipher. require() calls have some caching behavior, so you should not worry about a performance hit on repeated calls. There are likely larger performance hits to your program than require() calls on initialization.
I can't find a way to require NPM module dynamically by variable.
Here is a sample code what I'm trying to do, Everything works great expect import NPM module dynamically.
const firstModule = 'my-npm-module';
const secondModule = './MyReactComponent';
// NPM Module
import(firstModule).then(...); // Doesn't work
import('my-npm-module').then(...); // Works
// Local React Component
import(secondModule).then(...); // Works
import('./MyReactComponent').then(...); // Works
From the Webpack docs on dynamic import:
Fully dynamic statements, such as import(foo), will fail because
webpack requires at least some file location information. This is
because foo could potentially be any path to any file in your system
or project. The import() must contain at least some information about
where the module is located, so bundling can be limited to a specific
directory or set of files.
Your best option would probably be to either not use dynamic loading for anything in node_modules, or add the explicit path to the module, e.g.
import(`./node_modules/${firstModule}/index.js`);
I have a few npm modules published, all modules for existing libraries, like three.js or react.
The packages seem to be downloaded but i've received no feedback on whether it's done right or not.
Dependencies
What is the high level goal when defining dependencies?
three.js:
This is confusing because every "extension" just assumes that there is a THREE object available in some context.
My three.js module thus only mentions:
"devDependencies": {
"three": "^0.88.0"
}
And it's being used like this:
require( 'three-instanced-mesh' )(THREE)
Which both makes sense and doesn't.
The module can't work without three.js and a proper context passed in (THREE), but since i pass it in at runtime(?) it doesn't seem like it is an actual dependency. When I checkout the repo and want to develop in it, i do need to install three.js if i want the code to run.
React
I've published a React component which i intended to be used as such:
npm install my-module
import MyModule from 'my-module
<MyModule/>
For some reason I listed react as a peerDependencies dep.
<MyModule/> in JSX would imply that I've done something to have react already available in this context (similar to how THREE is passed in first example?).
The difference here is that i don't define the class at runtime, and thus calling import MyModule requires react to be available in MyModule.js?
What is the desired goal here and how to describe it? I only know that i don't want npm install my-module to install a different version of react, or to cause more react somehow to be bundled in the final bundle (but i'm not even sure about that).
What type of dependency (if any) should react be to my-react-component and how would i actually link it to my module?
For example using the externals thing with webpack vs having an actual import React from 'react'?
Build
If i set up my repo to work with the latest and the greatest of JS (or not even JS?), how and what should i publish?
import Foo from 'foo' //<-- where does 'foo' point and what is 'foo'?
What is the high level goal when defining dependencies?
You have to define which dependencies you are using only when developing (devDependencies) and the ones that are needed when someone installs your package and are going to be installed automatic (dependencies), and dependencies you need to be available, but you want the user to install (which, honestly, does not makes sense) peerDependencies.
The difference here is that i don't define the class at runtime, and thus calling import MyModule requires react to be available in MyModule.js?
It would require React to be available where the file is being imported, i.e: A imports myModule, but A has to have react imported. Putting as peer dependencies is the best way here indeed.
For example using the externals thing with webpack vs having an actual import React from 'react'?
Using externals in webpack just tells webpack to not bundle react and says that react will have been imported before the import of this component.
If i set up my repo to work with the latest and the greatest of JS (or not even JS?), how and what should i publish?
Usually the index.js file that contains the library minified/bundled. Publish that with npm, you'll need to setup main field on package.json
import Foo from 'foo' //<-- where does 'foo' point and what is 'foo'?
foo points to the name of the package that you created, i.e: the name this package was publish under. When you go to npmjs.org and search for foo, that is going to be the package. foo is in your node_modules.