Jest mocked implementation not being called outside of test function - javascript

I have a 3rd party module I need to mock (moment.js). I want to set the implementation to the default implementation before requiring the file I'm testing. The only function I'm mocking is the default export of the module, so I assign the prototype and static members to those of the actual implementation, as well.
season.js
import moment from 'moment';
export var currentSeason = getCurrentSeason();
export function currentSeasion() {
const diff = moment().diff(/* ... */);
// ...
return /* a number */;
}
__tests__/season.js
import moment from 'moment';
jest.mock('moment');
const actualMoment = jest.requireActual('moment');
moment.mockImplementation((...args) => actualMoment(...args));
Object.assign(moment, actualMoment);
const { getCurrentSeason } = require('../season');
test('getCurrentSeason()', () => {
moment.mockReturnValue(actualMoment(/* ... */));
expect(getCurrentSeason()).toBe(1);
});
I confirmed via debugging that mockImpelementation() is being called correctly, and within the test, it's being called correctly too. However, in the initialization of currentSeason, moment() is returning undefined. When I stepped into the moment() mocked function, mockConfig.mockImpl is undefined.
Running expect(moment()).toBeUndefined() in the test file but outside of any test before the import of season.js runs the mock implementation, as well.
I can't for the life of me figure out why it's only not working in the initialization of currentSeason.

I don't know how useful this will be for anyone else, but my solution turned out to be to pull my mock code into it's own /__mocks__/moment.js file.
const actual = jest.requireActual('moment'),
moment = jest.fn((...args) => actual(...args));
export default Object.assign(moment, actual);

Related

Importing a module that requires immediate function call [duplicate]

Is it possible to pass options to ES6 imports?
How do you translate this:
var x = require('module')(someoptions);
to ES6?
There is no way to do this with a single import statement, it does not allow for invocations.
So you wouldn't call it directly, but you can basically do just the same what commonjs does with default exports:
// module.js
export default function(options) {
return {
// actual module
}
}
// main.js
import m from 'module';
var x = m(someoptions);
Alternatively, if you use a module loader that supports monadic promises, you might be able to do something like
System.import('module').ap(someoptions).then(function(x) {
…
});
With the new import operator it might become
const promise = import('module').then(m => m(someoptions));
or
const x = (await import('module'))(someoptions)
however you probably don't want a dynamic import but a static one.
Concept
Here's my solution using ES6
Very much inline with #Bergi's response, this is the "template" I use when creating imports that need parameters passed for class declarations. This is used on an isomorphic framework I'm writing, so will work with a transpiler in the browser and in node.js (I use Babel with Webpack):
./MyClass.js
export default (Param1, Param2) => class MyClass {
constructor(){
console.log( Param1 );
}
}
./main.js
import MyClassFactory from './MyClass.js';
let MyClass = MyClassFactory('foo', 'bar');
let myInstance = new MyClass();
The above will output foo in a console
EDIT
Real World Example
For a real world example, I'm using this to pass in a namespace for accessing other classes and instances within a framework. Because we're simply creating a function and passing the object in as an argument, we can use it with our class declaration likeso:
export default (UIFramework) => class MyView extends UIFramework.Type.View {
getModels() {
// ...
UIFramework.Models.getModelsForView( this._models );
// ...
}
}
The importation is a bit more complicated and automagical in my case given that it's an entire framework, but essentially this is what is happening:
// ...
getView( viewName ){
//...
const ViewFactory = require(viewFileLoc);
const View = ViewFactory(this);
return new View();
}
// ...
I hope this helps!
Building on #Bergi's answer to use the debug module using es6 would be the following
// original
var debug = require('debug')('http');
// ES6
import * as Debug from 'debug';
const debug = Debug('http');
// Use in your code as normal
debug('Hello World!');
I've landed on this thread looking up for somewhat similar and would like to propose a sort of solution, at least for some cases (but see Remark below).
Use case
I have a module, that is running some instantiation logic immediately upon loading. I do not like to call this init logic outside the module (which is the same as call new SomeClass(p1, p2) or new ((p1, p2) => class SomeClass { ... p1 ... p2 ... }) and alike).
I do like that this init logic will run once, kind of a singular instantiation flow, but once per some specific parametrized context.
Example
service.js has at its very basic scope:
let context = null; // meanwhile i'm just leaving this as is
console.log('initialized in context ' + (context ? context : 'root'));
Module A does:
import * as S from 'service.js'; // console has now "initialized in context root"
Module B does:
import * as S from 'service.js'; // console stays unchanged! module's script runs only once
So far so good: service is available for both modules but was initialized only once.
Problem
How to make it run as another instance and init itself once again in another context, say in Module C?
Solution?
This is what I'm thinking about: use query parameters. In the service we'd add the following:
let context = new URL(import.meta.url).searchParams.get('context');
Module C would do:
import * as S from 'service.js?context=special';
the module will be re-imported, it's basic init logic will run and we'll see in the console:
initialized in context special
Remark: I'd myself advise to NOT practice this approach much, but leave it as the last resort. Why? Module imported more than once is more of an exception than a rule, so it is somewhat unexpected behavior and as such may confuse a consumers or even break it's own 'singleton' paradigms, if any.
I believe you can use es6 module loaders.
http://babeljs.io/docs/learn-es6/
System.import("lib/math").then(function(m) {
m(youroptionshere);
});
You just need to add these 2 lines.
import xModule from 'module';
const x = xModule('someOptions');
Here's my take on this question using the debug module as an example;
On this module's npm page, you have this:
var debug = require('debug')('http')
In the line above, a string is passed to the module that is imported, to construct. Here's how you would do same in ES6
import { debug as Debug } from 'debug'
const debug = Debug('http');
Hope this helps someone out there.
I ran into an analogous syntax issue when trying to convert some CJS (require()) code to ESM (import) - here's what worked when I needed to import Redis:
CJS
const RedisStore = require('connect-redis')(session);
ESM Equivalent
import connectRedis from 'connect-redis';
const RedisStore = connectRedis(session);
You can pass parameters in the module specifier directly:
import * as Lib from "./lib?foo=bar";
cf: https://flaming.codes/en/posts/es6-import-with-parameters

How can I reset a statically imported module in favour of a dynamically imported one, in mocha tests?

I have written a javascript file that provides configuration, and separately a proxy that lets me provide a fallback value if the property access was unsuccessfull
// stackUrlMap.js
export const stacks = {
'stackoverflow.com': 'StackOverflow',
'cooking.stackexchange.com': 'SeasonedAdvice',
}
// stackUrlConfig.js
import { stacks } from './stackUrlMap';
const getOrParse = parser => ({
get: (target, prop) => target?.[prop] ?? parser(prop);
})
const urlToName = url => url.split('.')[0];
export const stackUrlToNameMap = new Proxy(stacks, getOrParse(urlToName));
This was working perfectly, and I had a test written for it that passed.
import { expect } from 'chai';
import sinon from 'sinon';
import * as urlMap from './stackUrlMap';
describe('stackUrlConfig.js', function() {
let sandbox;
let stackUrlToNameMap = {};
beforeEach(async function() {
sandbox = sinon.createSandbox();
sandbox.stub(urlMap, 'stack').value({'madeup.url.com': 'Made Up Stack'});
// import here so we get the stubbed values passed through.
({ stackUrlToNameMap } = await import('./stackUrlConfig.js'))
});
it('can get an existing prop from the proxied map', function() {
const field = 'madeup.url.com';
// stub working correctly when accessed from test file
expect(urlMap.stacks[field]).to.exist();
// tests that if the proxied map can find the prop it uses that value
expect(stackUrlToNameMap[field]).to.equal('Made Up Stack');
});
})
However, now I've written code that uses stackUrlConfig.js elsewhere, that is imported by my test setup elsewhere, and the dynamically imported proxied map stackUrlToNameMap in the beforeEach of my test is not being 'respected', and the real stacks object is being used for the lookup, meaning I get this AssertionError:
AssertionError: expected 'madeup' to equal 'Made Up Stack'
If I put a console.trace in the stackUrlConfig.js, I can see it's first called before my test runs as it's imported into another file that isn't run by mocha yet.
I don't want to have to use decache (I'm not even sure it would work, I'm not using require) as seen in Re-importing modules between mocha tests if I can avoid it.
I took the code and tried to add a cache busting, like so:
const absolutePath = path.resolve(__dirname, './stackUrlConfig.js');
({ stackUrlToNameMap } = await import(`${absolutePath}?update=${Date.now()}`));
But my mocha setup did not find the resulting module.
I can't control whether this file is imported elsewhere first in my test, so can I reset mocha's reference to the module somehow?

Jest virtual mock: how do I troubleshoot this failure?

So I have this import statement in a module that I'm trying to test using jest 25.1 running on node 11.1.0. The import statement is for a module that is only available when running on the jvm's nashorn runtime, so I'm using jest's virtual mock to control the behavior in the unit tests. Here's what the import statement looks like in the module under test:
import RequestBuilder from 'nashorn-js/request_builder'
...and after the other lines in the import block, this:
const request = RequestBuilder.create('some-string')
.sendTo('some-other-string')
.withAction('yet-another-string')
.getResultWith( consumer => consumer.result( consumer.message().body() ) )
export const functionA = () => {...} // uses 'request' variable
export const functionB = () => {...} // uses 'request' variable
In the corresponding .spec file, I have this virtual mock setup:
const mockGetResultWith = {
getResultWith: jest.fn()
}
const mockWithAction = {
withAction: jest.fn().mockImplementation(() => mockGetResultWith)
}
const mockSendTo = {
sendTo: jest.fn().mockImplementation(() => mockWithAction)
}
const mockBuilder = {
create: jest.fn().mockImplementation(() => mockSendTo)
}
jest.mock(
'nashorn-js/request_builder',
() => mockBuilder,
{ virtual: true }
)
require('nashorn-js/request_builder')
import { functionA, functionB } from './module-under-test'
I have been trying unsuccessfully to get past this failure from jest:
● Test suite failed to run
TypeError: Cannot read property 'create' of undefined
35 | }
36 |
> 37 | const verify = RequestBuilder.create('some-string')
| ^
38 | .sendTo('some-other-string')
39 | .withAction('yet-another-string')
40 | .getResultWith( consumer => consumer.result( consumer.message().body() ) )
I've tried all kinds of different mock structures, using require vs import, etc, but haven't found the magic bullet.
As near as I can tell, it does not appear that the RequestBuilder import statement is even invoking the virtual mock. Or at least, if I add console.log() statements to the virtual mock factory function, I never see those log messages in the output.
Anybody have any idea what I'm missing or what else to try? I have pretty much the same pattern in use in other parts of the code, where this setup works, but for some mystical reason with this module, I can't get the virtual mock working. Any help is greatly appreciated.
So, the problem here turned out to be jest's implementation of import vs require in the .spec file.
By changing this line:
import { functionA, functionB } from './module-under-test'
To this:
const module = require('./module-under-test')
const functionA = module.functionA
const functionB = module.functionB
The module under test now loads successfully, and the tests run as expected.
I have no explanation for this, and haven't been able to find jest documentation describing why I'd get any different behavior between require vs import. In fact, I have the mock configuration setup before any import statements as described here:
https://github.com/kentcdodds/how-jest-mocking-works
If anybody out there understands what's going on with this import behavior, or has a link describing what I'm missing, I'd sure appreciate the info.

ES6 syntax for import binding [duplicate]

Is it possible to pass options to ES6 imports?
How do you translate this:
var x = require('module')(someoptions);
to ES6?
There is no way to do this with a single import statement, it does not allow for invocations.
So you wouldn't call it directly, but you can basically do just the same what commonjs does with default exports:
// module.js
export default function(options) {
return {
// actual module
}
}
// main.js
import m from 'module';
var x = m(someoptions);
Alternatively, if you use a module loader that supports monadic promises, you might be able to do something like
System.import('module').ap(someoptions).then(function(x) {
…
});
With the new import operator it might become
const promise = import('module').then(m => m(someoptions));
or
const x = (await import('module'))(someoptions)
however you probably don't want a dynamic import but a static one.
Concept
Here's my solution using ES6
Very much inline with #Bergi's response, this is the "template" I use when creating imports that need parameters passed for class declarations. This is used on an isomorphic framework I'm writing, so will work with a transpiler in the browser and in node.js (I use Babel with Webpack):
./MyClass.js
export default (Param1, Param2) => class MyClass {
constructor(){
console.log( Param1 );
}
}
./main.js
import MyClassFactory from './MyClass.js';
let MyClass = MyClassFactory('foo', 'bar');
let myInstance = new MyClass();
The above will output foo in a console
EDIT
Real World Example
For a real world example, I'm using this to pass in a namespace for accessing other classes and instances within a framework. Because we're simply creating a function and passing the object in as an argument, we can use it with our class declaration likeso:
export default (UIFramework) => class MyView extends UIFramework.Type.View {
getModels() {
// ...
UIFramework.Models.getModelsForView( this._models );
// ...
}
}
The importation is a bit more complicated and automagical in my case given that it's an entire framework, but essentially this is what is happening:
// ...
getView( viewName ){
//...
const ViewFactory = require(viewFileLoc);
const View = ViewFactory(this);
return new View();
}
// ...
I hope this helps!
Building on #Bergi's answer to use the debug module using es6 would be the following
// original
var debug = require('debug')('http');
// ES6
import * as Debug from 'debug';
const debug = Debug('http');
// Use in your code as normal
debug('Hello World!');
I've landed on this thread looking up for somewhat similar and would like to propose a sort of solution, at least for some cases (but see Remark below).
Use case
I have a module, that is running some instantiation logic immediately upon loading. I do not like to call this init logic outside the module (which is the same as call new SomeClass(p1, p2) or new ((p1, p2) => class SomeClass { ... p1 ... p2 ... }) and alike).
I do like that this init logic will run once, kind of a singular instantiation flow, but once per some specific parametrized context.
Example
service.js has at its very basic scope:
let context = null; // meanwhile i'm just leaving this as is
console.log('initialized in context ' + (context ? context : 'root'));
Module A does:
import * as S from 'service.js'; // console has now "initialized in context root"
Module B does:
import * as S from 'service.js'; // console stays unchanged! module's script runs only once
So far so good: service is available for both modules but was initialized only once.
Problem
How to make it run as another instance and init itself once again in another context, say in Module C?
Solution?
This is what I'm thinking about: use query parameters. In the service we'd add the following:
let context = new URL(import.meta.url).searchParams.get('context');
Module C would do:
import * as S from 'service.js?context=special';
the module will be re-imported, it's basic init logic will run and we'll see in the console:
initialized in context special
Remark: I'd myself advise to NOT practice this approach much, but leave it as the last resort. Why? Module imported more than once is more of an exception than a rule, so it is somewhat unexpected behavior and as such may confuse a consumers or even break it's own 'singleton' paradigms, if any.
I believe you can use es6 module loaders.
http://babeljs.io/docs/learn-es6/
System.import("lib/math").then(function(m) {
m(youroptionshere);
});
You just need to add these 2 lines.
import xModule from 'module';
const x = xModule('someOptions');
Here's my take on this question using the debug module as an example;
On this module's npm page, you have this:
var debug = require('debug')('http')
In the line above, a string is passed to the module that is imported, to construct. Here's how you would do same in ES6
import { debug as Debug } from 'debug'
const debug = Debug('http');
Hope this helps someone out there.
I ran into an analogous syntax issue when trying to convert some CJS (require()) code to ESM (import) - here's what worked when I needed to import Redis:
CJS
const RedisStore = require('connect-redis')(session);
ESM Equivalent
import connectRedis from 'connect-redis';
const RedisStore = connectRedis(session);
You can pass parameters in the module specifier directly:
import * as Lib from "./lib?foo=bar";
cf: https://flaming.codes/en/posts/es6-import-with-parameters

Import statement using .get in the statement [duplicate]

Is it possible to pass options to ES6 imports?
How do you translate this:
var x = require('module')(someoptions);
to ES6?
There is no way to do this with a single import statement, it does not allow for invocations.
So you wouldn't call it directly, but you can basically do just the same what commonjs does with default exports:
// module.js
export default function(options) {
return {
// actual module
}
}
// main.js
import m from 'module';
var x = m(someoptions);
Alternatively, if you use a module loader that supports monadic promises, you might be able to do something like
System.import('module').ap(someoptions).then(function(x) {
…
});
With the new import operator it might become
const promise = import('module').then(m => m(someoptions));
or
const x = (await import('module'))(someoptions)
however you probably don't want a dynamic import but a static one.
Concept
Here's my solution using ES6
Very much inline with #Bergi's response, this is the "template" I use when creating imports that need parameters passed for class declarations. This is used on an isomorphic framework I'm writing, so will work with a transpiler in the browser and in node.js (I use Babel with Webpack):
./MyClass.js
export default (Param1, Param2) => class MyClass {
constructor(){
console.log( Param1 );
}
}
./main.js
import MyClassFactory from './MyClass.js';
let MyClass = MyClassFactory('foo', 'bar');
let myInstance = new MyClass();
The above will output foo in a console
EDIT
Real World Example
For a real world example, I'm using this to pass in a namespace for accessing other classes and instances within a framework. Because we're simply creating a function and passing the object in as an argument, we can use it with our class declaration likeso:
export default (UIFramework) => class MyView extends UIFramework.Type.View {
getModels() {
// ...
UIFramework.Models.getModelsForView( this._models );
// ...
}
}
The importation is a bit more complicated and automagical in my case given that it's an entire framework, but essentially this is what is happening:
// ...
getView( viewName ){
//...
const ViewFactory = require(viewFileLoc);
const View = ViewFactory(this);
return new View();
}
// ...
I hope this helps!
Building on #Bergi's answer to use the debug module using es6 would be the following
// original
var debug = require('debug')('http');
// ES6
import * as Debug from 'debug';
const debug = Debug('http');
// Use in your code as normal
debug('Hello World!');
I've landed on this thread looking up for somewhat similar and would like to propose a sort of solution, at least for some cases (but see Remark below).
Use case
I have a module, that is running some instantiation logic immediately upon loading. I do not like to call this init logic outside the module (which is the same as call new SomeClass(p1, p2) or new ((p1, p2) => class SomeClass { ... p1 ... p2 ... }) and alike).
I do like that this init logic will run once, kind of a singular instantiation flow, but once per some specific parametrized context.
Example
service.js has at its very basic scope:
let context = null; // meanwhile i'm just leaving this as is
console.log('initialized in context ' + (context ? context : 'root'));
Module A does:
import * as S from 'service.js'; // console has now "initialized in context root"
Module B does:
import * as S from 'service.js'; // console stays unchanged! module's script runs only once
So far so good: service is available for both modules but was initialized only once.
Problem
How to make it run as another instance and init itself once again in another context, say in Module C?
Solution?
This is what I'm thinking about: use query parameters. In the service we'd add the following:
let context = new URL(import.meta.url).searchParams.get('context');
Module C would do:
import * as S from 'service.js?context=special';
the module will be re-imported, it's basic init logic will run and we'll see in the console:
initialized in context special
Remark: I'd myself advise to NOT practice this approach much, but leave it as the last resort. Why? Module imported more than once is more of an exception than a rule, so it is somewhat unexpected behavior and as such may confuse a consumers or even break it's own 'singleton' paradigms, if any.
I believe you can use es6 module loaders.
http://babeljs.io/docs/learn-es6/
System.import("lib/math").then(function(m) {
m(youroptionshere);
});
You just need to add these 2 lines.
import xModule from 'module';
const x = xModule('someOptions');
Here's my take on this question using the debug module as an example;
On this module's npm page, you have this:
var debug = require('debug')('http')
In the line above, a string is passed to the module that is imported, to construct. Here's how you would do same in ES6
import { debug as Debug } from 'debug'
const debug = Debug('http');
Hope this helps someone out there.
I ran into an analogous syntax issue when trying to convert some CJS (require()) code to ESM (import) - here's what worked when I needed to import Redis:
CJS
const RedisStore = require('connect-redis')(session);
ESM Equivalent
import connectRedis from 'connect-redis';
const RedisStore = connectRedis(session);
You can pass parameters in the module specifier directly:
import * as Lib from "./lib?foo=bar";
cf: https://flaming.codes/en/posts/es6-import-with-parameters

Categories