How to mock library by SinonJs? - javascript

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.

Related

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

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.

General TypeScript usage for modules / namespaces

I have a standard web page (not an app rendered with angular, react, vue, etc) that uses jQuery, and other libraries.
I want to integrate good practices with TypeScript.
What is the best way to do this?
My current idea is to have 3 files:
the index.d.ts (describes the types of my module)
the test.ts (the implementation of the types described in the index.d.ts)
the page.js (the file that uses the javascript defined in the test.js -- output from the test.ts)
I currently have these contents:
index.d.ts
// Type definitions for test 1.0.0
// Project: Test
// Definitions by: Author Name
/// <reference path="../../lib/#types/jquery/index.d.ts" />
declare namespace TestNS {
export class TestClass {
// Fields
element: JQuery;
value: string;
constructor(element: JQuery, val: string);
OnCreate();
static AttachToJQuery(jq: JQueryStatic): void;
}
interface JQuery {
TestClass(element: JQuery, val?: string): TestNS.TestClass;
}
interface JQueryStatic {
TestClass(element: JQuery, val?: string): TestNS.TestClass;
}
}
Test.ts (loaded 2nd, after jQuery)
/// <reference path="../../lib/#types/jquery/index.d.ts" />
/// <reference path="./index.d.ts" />
export namespace TestNS {
export class TestClass {
// Fields
element: JQuery;
value: string;
constructor(element: JQuery, val: string) {
this.element = element;
this.value = val;
this.OnCreate();
}
OnCreate() {
this.element.data('test-value', this.value);
}
static AttachToJQuery(jq: JQueryStatic) {
//no jQuery around
if (!jq) {
return false;
}
jq.fn.extend({
TestNS: function(newVal) {
return this.each(function() {
var obj = jq(this);
new TestNS.TestClass(obj, newVal);
});
}
});
}//attach to jquery method (to easily work with noconflict mode)
}//test class
}
page.js (loaded last)
let newJquery:JQueryStatic = jQuery.noConflict();
TestNS.TestClass.AttachToJQuery(newJquery);
let testElems = newJquery(".testClass");
testElems.TestClass();
My goal is to have my code neatly organized into a namespace in typescript, as well as on the page (but doing so in typescript gives errors related to duplicate identifiers) as well as being modular and extensible. I understand that I should publish my types under the "node_modules/#types" directory, but I simply want them all encapsulated in this module for now.
I have tried using a module, and importing what is defined in the index.d.ts, but TypeScript says I cannot import this file, and that the module name cannot be found. I did learn that if I use a module, it should be accompanied with a loader, such as CommonJS, RequireJS or AMD, but I would prefer to avoid that for now (if it's not a horrible practice to do so, as I want to minimize the levels of complexity for now).
I've tried looking at other projects, but they all use a loader, which seems a bit overkill for this kind of script.
Is there a good way to go about this?
If you want to make things work without a module loader, you will have to make a few changes. First of all it's worth mentioning that TypeScript supports 2 primary ways of modularizing code, namespaces and modules.
NOTE: A slightly confusing nuance here is that the keywords namespace and module can be used interchangebly and don't by themselves determine whether a thing is a module or a namespace.
1) modules (formerly called external modules) - these use typical import, export, and require semantics and require a module loader and preferably a bundler of some sort. For this pattern, each file is considered a module. Any file that contains any import or export statements will be considered an external module by the TypeScript compiler.
2) namespaces (formerly called internal modules) - this pattern supports namespaces that can span multiple files. This is the module type that you would need to use if you don't want to use a module loader. Usage of this pattern is becoming less common, but it is still an option if you want to use it for whatever reason. To use this type of modularization, you can't have any import or export statements in the file.
Here's the docs on namespaces https://www.typescriptlang.org/docs/handbook/namespaces.html
Assuming you want to go with your original plan, you will need to tweak a couple of things in your code.
1) As mentioned by #sbat, your index.d.ts is re-declaring your namespace and class types. This is likely causing duplicate definition errors. You can replace the entire contents with a simple override of the JQuery interface.
/** Extend the JQuery interface with custom method. */
declare interface JQuery {
TestNS: () => TestNS.TestClass
}
2) Make sure you don't have any top level exports or imports. Specifically you will want to remove the export from your namespace in test.ts. This will make your module an internal one instead of an external one.
namespace TestNS {
export class TestClass {
...
}
}
Assuming you want to just try TypeScript in a simple project, I'd suggest you simplify things a bit:
Remove index.d.ts. Definition files describe existing javascript code, so that TypeScript is aware of types in it. You don't have this situation in your project, there is no need to repeat type information you have already in your test.ts, you can safely remove this file.
Just build your test.ts into test.js. Make sure your jquery .d.ts is located where it should be.
Remove page.js, just code everything in test.ts for a test. Once you make it work, you can reorganize it.
However, if your goal is to have your code modular and extensible, the recommended approach is to indeed use modules:
Starting with ECMAScript 2015, modules are native part of the
language, and should be supported by all compliant engine
implementations. Thus, for new projects modules would be the
recommended code organization mechanism.
https://www.typescriptlang.org/docs/handbook/namespaces-and-modules.html
To use modules in production environment in the browser you will need:
Bundler. A tool that will call TypeScript to build your individual TS modules, and then combine the build output into single javascript file. You will then include this single file into your HTML. WebPack is probably the easiest to start: https://webpack.js.org/concepts/
Package manager. A tool that installs and manages versions of the third-party modules. https://www.npmjs.com/
This is a big area to study, but I don't think it is an overkill. If your code is modular (even if it is just 2 modules), you need an infrastructure to bundle and load your modules one way or the other. There are several options with different benefits and trade-offs, but there is fundamentally no way around that.

How can I use functions and objects without require them in NodeJS

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.

Using external javascript files in node.js without using export and require

I don't like the whole export/require stuff in node, it takes too long. Let's say I have a file server.js and I want to use functions in whatever.js. in html I just add this to the header:
<script src='whatever.js'></script>
and then I can just use all the functions of whatever.js in my body's script.
But in node, in the server.js file I'd do:
var myobject = require('./whatever.js');
but then I need to set it to myobject, and further I need to go to whatever.js and manually decide what functions I want to export. not to mention that typing myobject.someFunction() is alot longer to write than someFunction() and I need to remember what I exposed/didn't expose.
I wanted something where I could just go:
require('./whatever.js');
and it puts it ALL in global, no bs. like in good old html/javascript. Is there a way to do this in node?
This will do the trick,
var fs = require('fs');
eval(fs.readFileSync('whatever.js')+'');
// here call functions from whatever.js file
(I realize this is an old thread but wanted to leave a note here for posterity.)
Here in 2022 there are several approaches for executing code from different files with Node.js:
ESM: Use standard ECMAScript modules
At the time of this writing, much of the node ecosystem (i.e. packages on npm) is in the process of transitioning to this paradigm, and there are some associated growing pains (e.g. things like __dirname are only available in CJS not ESM, though the workaround is easy).
For most developers, it would be advisable to become comfortable with this standard as it transcends node.js (i.e. is implemented in other runtimes like Deno and web browsers) and has been years in the making.
CJS: Use the original "CommonJS" module mechanism, e.g. require('./some-script.js')
It should be noted, particularly for the OP, that even though the "intended" way to use CJS modules is to export functions, constants, etc. and import them explicitly, it is possible to define everything in global scope using globalThis, though I would not recommend this.
// my-script.js
require('./foo.js');
require('./bar.js');
foo(); // This is foo from <...>foo.js
console.log(`bar = ${bar} (in ${__filename})`); // bar = 123 (in <...>my-script.js)
// foo.js
globalThis.foo = function() {
console.log(`This is foo from ${__filename}`);
}
// bar.js
globalThis.bar = 123;
If you try omitting globalThis. you'll find that foo and bar are no longer defined in the main script because require "wraps them" in "module scope."
Use eval
In my experience, there are very few legitimate use cases for eval (see Never use eval()!). Nevertheless, the functionality requested in this question is precisely what eval provides: "run some code as if it were written right here" and you can feed it from a file, as explained above by Mehul Prajapati
// include.js
// Defines a global function that works like C's "#include" preprocessor directive
const { readFileSync } = require('fs');
globalThis.include = function(scriptFile) {
console.warn('!!! EXTREMELY INSECURE !!!');
eval(readFileSync(scriptFile, 'utf-8'));
};
// main.js
require('./include.js'); // loads global include
// (I sure hope you completely trust these sources)
include('./foo.js');
include('./bar.js');
Note: Something that has contributed to much of my confusion in the past is that there have been competing standards/conventions/APIs that use some of the same identifiers, namely require, which require.js and other bundlers that support AMD (Asynchronous Module Definition)
use with different semantics. So for someone building a web application (using AMD for modules in web browsers) with node.js tooling (using CJS for modules locally) it can be frustrating to keep the functions straight, especially if it's an Electron application, which can expose Node.js APIs to scripts running in the renderer (browser). If you find yourself confused why a module is "not found" in a situation like that, check the stack trace to see which require is being called (and you may have to wrap/rename them on globalThis or something to avoid collisions).
Further reading:
JavaScript Modules: A Brief History [2019]
How the module system, CommonJS & require works [updated 2022]
What is AMD, CommonJS, and UMD? [2014]

How to introduce typescript on a big nodeJS code basis step by step?

I do own quite a big code basis implemented in JavaScript based on NodeJS. I do want to give typescript a try and want to implement all new modules in typescript. Just to see how it performs and how I like the idea. I do need to be able to revert back at any time so I do not want to put to much effort into the migration.
I started by using WebStorm and Gulp to support typescript. I created a new module and used the import / require combination.
import settings = require("./settings");
import _ = require("lodash-node");
By doing so I receive a couple of errors.
Error:(22, 27) TS2307: Cannot find external module './settings'.
Error:(23, 20) TS2307: Cannot find external module 'lodash-node'.
Those modules have no typescript definition file and are plain javascript modules (lodash is obviously a external library). I do have a couple of dependencies and I do not want to create definitions manually even if they are just empty stubs. As mentioned I do want to keep the integration as simple as possible.
Is there a general flag or something like that I can set?
The easiest way to proceed (if you don't want type information for a module) is to write:
declare module "lodash-node" {
var notTyped: any; // naming is unimportant here
export = notTyped;
}
Put that in a separate .d.ts file and /// <reference> it. Then _ will be of type any in your implementation file and the compiler won't complain about not know what module you're talking about.
You can use webpack's typescript-loader and start from there. Make sure you put target: 'node' on your webpack config so you don't have all the browser junk.

Categories