How to share singleton across multiple threads in nodejs - javascript

I'm trying to have a singleton work across multiple threads in node/ts. But for some reason only main event loop has access to it. Is there any way I can have a singleton updated in the main loop, and have the changes reflected in a worker thread?
Here's the code -
data_source.ts
class DataSource {
private _data: any
constructor() {
this._data = []
}
set(data: any) {
this._data.push(data)
}
get(id: any) {
return this._data.find((d: any) => d.id === id)
}
}
const instance = new DataSource();
Object.freeze(instance)
export default instance
index.ts
let workerpool = require('workerpool')
import DataSource from './data_source'
import { test } from './worker'
console.log(DataSource)
function main () {
let pool = workerpool.pool()
pool.exec(test, [])
setInterval(() => {
DataSource.set({id: 'asd'})
}, 1000)
}
main()
worker.ts
import DataSource from './data_source'
export function test() {
setInterval(() => {
console.log(DataSource)
console.log("test")
}, 500)
}
As you can see, the main.ts executes a worker which "should" console log DataSource singleton every 0.5s, while main.ts adds new item to the DataSource every second.
This works if I emit the following line from worker - console.log(DataSource), a string "test" gets logged properly every 0.5s. But, when I try to console log DataSource in my worker thread, it gets logged properly for the first time only, and after that it seems that interval from main loop completely takes over the singleton and completely breaks the worker, and it stops logging anything.
I'm trying to use singleton as the schedule, main loop as scheduler, and worker as the executor (which checks the schedule every second).
Thank you!

Related

Can I pass an object to React hooks instead of a dependency array, so I can print out the names of variables?

A test in our frankly very complicated React app is taking ~1000 times longer to run than expected, and I'm trying to pin down where/why.
I started by manually calling console.time('name of test') in the test file, and then manually dotting console.timeLog('name of test', 'did a thing') around the application in all the places that were called and seemed likely to cause slow downs.
I noticed a lot of these places were inside React hooks - they aren't slow themselves, but were helping me see how long it took for coffee to get there.
I decided I needed to write a monkey patch in a Jest setupFilesAfterEnv file for logging when React hooks callback are called, and what with (for this example, I'll use useEffect)
const React = require('react');
let timeTestName = null;
let doTimeLog = false;
let prevUseEffect;
beforeAll(() => {
({ timeTestName } = global);
const prevUseEffect = React.useEffect;
React.useEffect = (cb, deps) => {
if(doTimeLog && timeTestName && Array.isArray(deps) && !__filename.includes('node_modules')){
console.timeLog(timeTestName, `Use Effect called with ${JSON.stringify(deps, null, 2)}`): // log useEffect being called with timer
}
prevUseEffect(cb, deps);
}
});
beforeEach(() => {
const { testPath } = expect.getState();
if(testPath.endsWith(`${timeTestName}.test.js`)) {
doTimeLog = true;
console.time(timeTestName); // start timer
} else {
doTimerLog = false;
}
});
afterEach(() => {
doTimerLog = false;
console.log(testToTimeName); // end timer
});
afterAll(() => {
React.useEffect = prevUseEffect;
})
However what I really want as well is the variable names in the dependency list, which I cannot get (at least not without changing non-test code).
One thought I had was using a Jest transformer to make all the arrays into objects so I preserve the variable names as keys; something like:
module.exports = {
process(sourceText) {
return {
code: `convertSquareToCurly(sourceText)`,
};
},
};
module.exports = {
transform: {
'matchDepList':
'<rootDir>/deplistTransformer.js',
},
};
So during tests only my useEffects become:
useEffect(() => foo(a, b, c), { a, b, c})
(I will handle this new object in my monkey patch)
I'm reasonably confident I can make the above work.
Then I in my monkey patch I can call console.log(Object.entries(deps)); and prevUseEffect(cb, Object.values(deps));.
However if I'm concerned that calling Object.values will cause the hook's callback to always be called.
I haven't been able to try the transformer yet, but I don't want to waste time writing it if passing Object.values won't work in place of the untransformed dependency list.
Is there another way to monkey patch and get variable names, or would this work?

Function is not calling mocked constructor in Jest test

I have a setup similar to this question but the partial mocking answer is not working for me.
I have a file 'FiscalYear.jsx' which contains a class FiscalYear and a few related non-class functions eg
export class FiscalYear {
constructor(fy) { //does stuff }
// other functionality
}
export function getFY(offset) {
// does stuff
return new FiscalYear(fy);
}
I want my Jest test for the getFY function to simply check that the FiscalYear constructor was called with the right parameter. I used partial mocking in my test file to accomplish this:
import { FiscalYear, getFY } from '../FiscalYear';
jest.mock('../FiscalYear', () => {
const originalModule = require.requireActual('../FiscalYear');
return {
...originalModule,
FiscalYear: jest.fn().mockImplementation((a) => { })
};
});
test("getFY", () => {
getFY(2);
expect(FiscalYear).toHaveBeenCalled();
});
I'll update the toHaveBeenCalled to toBeCalledWith once I make it actually call the function. However currently the test fails with
expect(jest.fn()).toHaveBeenCalled()
Expected number of calls: >= 1
Received number of calls: 0
I put a console.log in the real constructor and confirmed that the getFY function is calling that instead of the mock constructor.
How to get this working? Is it a bad design to have these both in the same file? Totally new to JavaScript so if this is bad practice please let me know.

How can multiple Imported classes call each others methods?

I have this index.js file:
import GameController from './Controllers/GameController'
import LandController from './Controllers/LandController'
const GAME = new GameController();
const LAND = new LandController();
And in this class, I want to call a function every second using setInterval:
export default class GameController {
tickCount = 0;
tick = setInterval( () => {
this.Tick()
}, 1000);
Tick () {
LAND.updateLand();
this.tickCount++;
}
}
But I get this error:
Uncaught ReferenceError: GAME is not defined
I'm new to webpack, I had this working in vanilla javascript but I'm still learning, so any help would be greatly appreciated! Thank you
EDIT:
Updated GameController class based on bravo's answer, however now I get the error:
Uncaught ReferenceError: LAND is not defined
How can I make it so multiple classes can call each others methods?
You started using modules and classes, so you are moving to OOP (Object Oriented Programming). This means that you should forget about scripting, where you declare globals, everything manipulates everything, and start reasoning in a structured, hierarchical, and isolated fashion (where you have objects responsible for specific tasks).
You started properly by understanding what isolation is. You created an autonomous class to control the land. Then you need a game controller, which controls the land (and anything else, but we will come back to this lather).
Follow the reasoning; Game controls Land, in OOP this would translate in either Game receives Land or Game creates Land. Which of two to choose depends on the specific use case.
Game creates Land
// ./Controllers/GameController.js
import LandController from './Controllers/LandController'
export default class GameController {
tickCount = 0;
land = new LandController();
intercal = setInterval(this.onTick.bind(this), 1000);
onTick () {
this.land.updateLand();
this.tickCount++;
}
}
// index.js
import GameController from './Controllers/GameController'
const GAME = new GameController();
Here you can see how Game creates Land, this is useful but reduces your access to the Land constructor (assuming you do not want to change Game to configure land)
Game receives Land
// ./Controllers/GameController.js
export default class GameController {
tickCount = 0;
land;
constructor(land) {
this.land = land;
}
intercal = setInterval(this.onTick.bind(this), 1000);
onTick () {
this.land.updateLand();
this.tickCount++;
}
}
// index.js
import GameController from './Controllers/GameController'
import LandController from './Controllers/LandController'
const LAND = new LandController();
const GAME = new GameController(LAND);
Here you have access to both constructors and your code is more maintainable. Game is not concerned about Land (what is, what it has in or where it came from) as long as it has a method updateLand() that has to be called at an interval.
More than land
The second solution allows you to implement a more versatile Game. Assume your Land has a method called update() rather than updateLand().
// ./Controllers/GameController.js
export default class GameController {
tickCount = 0;
children;
constructor(children) {
this.children = children;
}
intercal = setInterval(this.onTick.bind(this), 1000);
onTick () {
this.children.forEach(c => c.update());
this.tickCount++;
}
}
And then you can go beyond Land, maybe have a Character or more Land, or anything else.
// index.js
import GameController from './Controllers/GameController'
import CharController from './Controllers/CharController'
import LandController from './Controllers/LandController'
const LAND1 = new LandController();
const LAND2 = new LandController();
const CHAR = new CharController();
const GAME = new GameController([ LAND1, LAND2, CHAR ]);
This is how you can start reasoning to structure your OOP software properly; objects that are concerned about tasks and other objects.
When an object is not concerned about another object it should not have access to it (so no globals)
When an object is responsible for another object it should have direct access (receive them from the constructor or a setter) or directly instantiate them (create them).
If you really want to make the instances global, which I would not recommend because there are normally are better methods then using global variables, you should setup the variables in the window object
window.yourNamespace = {};
window.yourNamespace.game = new GameController();
window.yourNamespace.land = new LandController();
Then you can access both objects everywhere you like with
window.yourNamespace.game
window.yourNamespace.land
Answering your question about a non global solution:
I can only guess what your application will do but it sounds like a game. So GameController should be the main class. From this class you should instantiate all other classes like the LandController(). In that case you can access the LandController of course from the GameController. If you need an object in a class you should give the object as an parameter. E.g.
Class GameController()
{
landController = new LandController();
… here all needed methods for the class …
// Example if you need LandController in an other Class
exampleMethod()
{
Let otherClass = new OtherClass(this.landController);
}
}
You can do it this way:
We may use a constructor for that.
export default class GameController {
tickCount = 0;
tick = null;
constructor() {
this.tick = setInterval(() => {
this.Tick()
}, 1000);
}
Tick () {
LAND.updateLand();
this.tickCount++;
}
}

Jest manual mocking a package requiring new instance

I'm trying to use Jest manual mock to mock the behaviour of a package X used
in a project.
The usage of package X within the the actual application code is like so:
// Real.js
import X from '../lib/X.js';
export default class App {
...
execute() {
const x = new X(); // creating a new instance of X
x.doSomething(); // calling someThing() of X
}
...
}
my Jest manual mock to mimic the behaviour of X looks like:
global.__test__X = () => ({
doSomething: jest.fn(async () => console.log('mocked async do something')),
id: (Math.random() * 1000) % 10,
});
module.exports = global.__test__X;
In my test i'm trying to see how many times X was called and with what parameters
using the blow code:
jest.mock('../X');
import Real from '../Real';
const X = require('../X');
describe('Testing...', async () => {
it('DoSomething should print mocked console statement', async () => {
Real.execute(); // this would internally call X.doSomething()
const x = new X();
console.log(x.doSomething.mock.calls); // gives []
});
});
Using above, I'm trying to check how many times X was called but can't figure out what
i'm doing wrong since mock.calls is always []. Note that the mock is getting
executed as i can see mocked async do something.
There's a complete explanation for doing this with jest.mock('./SomeClass'); that applies to this question. "ES6 class, Automatic mock" .
let's get started.
// ./lib/X.js
export default class X {
constructor () {
this.id = '1234567890';
}
doSomething = () => {
return 'Original X';
}
}
Note, the above code has never been called during the test.
This is the resource we want to test, what I mean is, in this class create objects by the class or module that is mocked. We want to make a fake version instead of the original.
// Real.js
import X from './lib/X.js';
export default class App {
constructor() {
this.x = new X(); // creating a new instance of X
}
execute = () => {
this.x.doSomething(); // calling someThing() of X
}
}
Accepts a function that should be used as the implementation of the mock. So what we will do is using manual mocks ( __ mocks __ folder) to mock ES6 classes.
// ./__mocks__/lib/X.js
module.exports = jest.fn().mockImplementation(() => {
return {
doSomething: jest.fn(() => 'Mocking Original X'),
id: (Math.random() * 1000) % 10
}
});
When we import './lib/X.js' on our test file, Now, in order to test this method without actually hitting the library (and thus creating slow and fragile tests), we immediately use the mock the './lib/X.js' module.
// Real.test.js
import X from './lib/X.js';
import Real from './Real';
jest.mock('./lib/X.js'); // // X module is now a mock constructor
describe('Testing', async () => {
beforeEach(() => {
// Clear all instances and calls to constructor and all methods:
X.mockClear();
});
it('DoSomething should print mocked correct statement', async () => {
// Ensure our mockClear() is clearing out previous calls to the constructor
expect(X).not.toHaveBeenCalled();
const real = new Real();
expect(X).toHaveBeenCalledTimes(1); // Constructor has been called X.js
real.execute();
// mock.instances is available with automatic mocks:
const mockXInstance = X.mock.instances[0];
const mockDoSomething = mockXInstance.doSomething;
expect(mockDoSomething).toHaveBeenCalledTimes(1);
expect(mockDoSomething.mock.calls[0][0]).toEqual('Mocking Original X');
});
});
maybe this is not enough to answer, at least this explains how mock works in similar cases

Mock Es6 classes using Jest

I'm trying to mock an ES6 class with a constructor that receives parameters, and then mock different class functions on the class to continue with testing, using Jest.
Problem is I can't find any documents on how to approach this problem. I've already seen this post, but it doesn't resolve my problem, because the OP in fact didn't even need to mock the class! The other answer in that post also doesn't elaborate at all, doesn't point to any documentation online and will not lead to reproduceable knowledge, since it's just a block of code.
So say I have the following class:
//socket.js;
module.exports = class Socket extends EventEmitter {
constructor(id, password) {
super();
this.id = id;
this.password = password;
this.state = constants.socket.INITIALIZING;
}
connect() {
// Well this connects and so on...
}
};
//__tests__/socket.js
jest.mock('./../socket');
const Socket = require('./../socket');
const socket = new Socket(1, 'password');
expect(Socket).toHaveBeenCalledTimes(1);
socket.connect()
expect(Socket.mock.calls[0][1]).toBe(1);
expect(Socket.mock.calls[0][2]).toBe('password');
As obvious, the way I'm trying to mock Socket and the class function connect on it is wrong, but I can't find the right way to do so.
Please explain, in your answer, the logical steps you make to mock this and why each of them is necessary + provide external links to Jest official docs if possible!
Thanks for the help!
Update:
All this info and more has now been added to the Jest docs in a new guide, "ES6 Class Mocks."
Full disclosure: I wrote it. :-)
The key to mocking ES6 classes is knowing that an ES6 class is a function. Therefore, the mock must also be a function.
Call jest.mock('./mocked-class.js');, and also import './mocked-class.js'.
For any class methods you want to track calls to, create a variable that points to a mock function, like this: const mockedMethod = jest.fn();. Use those in the next step.
Call MockedClass.mockImplementation(). Pass in an arrow function that returns an object containing any mocked methods, each set to its own mock function (created in step 2).
The same thing can be done using manual mocks (__mocks__ folder) to mock ES6 classes. In this case, the exported mock is created by calling jest.fn().mockImplementation(), with the same argument described in (3) above. This creates a mock function. In this case, you'll also need to export any mocked methods you want to spy on.
The same thing can be done by calling jest.mock('mocked-class.js', factoryFunction), where factoryFunction is again the same argument passed in 3 and 4 above.
An example is worth a thousand words, so here's the code.
Also, there's a repo demonstrating all of this, here:
https://github.com/jonathan-stone/jest-es6-classes-demo/tree/mocks-working
First, for your code
if you were to add the following setup code, your tests should pass:
const connectMock = jest.fn(); // Lets you check if `connect()` was called, if you want
Socket.mockImplementation(() => {
return {
connect: connectMock
};
});
(Note, in your code: Socket.mock.calls[0][1] should be [0][0], and [0][2] should be [0][1]. )
Next, a contrived example
with some explanation inline.
mocked-class.js. Note, this code is never called during the test.
export default class MockedClass {
constructor() {
console.log('Constructed');
}
mockedMethod() {
console.log('Called mockedMethod');
}
}
mocked-class-consumer.js. This class creates an object using the mocked class. We want it to create a mocked version instead of the real thing.
import MockedClass from './mocked-class';
export default class MockedClassConsumer {
constructor() {
this.mockedClassInstance = new MockedClass('yo');
this.mockedClassInstance.mockedMethod('bro');
}
}
mocked-class-consumer.test.js - the test:
import MockedClassConsumer from './mocked-class-consumer';
import MockedClass from './mocked-class';
jest.mock('./mocked-class'); // Mocks the function that creates the class; replaces it with a function that returns undefined.
// console.log(MockedClass()); // logs 'undefined'
let mockedClassConsumer;
const mockedMethodImpl = jest.fn();
beforeAll(() => {
MockedClass.mockImplementation(() => {
// Replace the class-creation method with this mock version.
return {
mockedMethod: mockedMethodImpl // Populate the method with a reference to a mock created with jest.fn().
};
});
});
beforeEach(() => {
MockedClass.mockClear();
mockedMethodImpl.mockClear();
});
it('The MockedClassConsumer instance can be created', () => {
const mockedClassConsumer = new MockedClassConsumer();
// console.log(MockedClass()); // logs a jest-created object with a mockedMethod: property, because the mockImplementation has been set now.
expect(mockedClassConsumer).toBeTruthy();
});
it('We can check if the consumer called the class constructor', () => {
expect(MockedClass).not.toHaveBeenCalled(); // Ensure our mockClear() is clearing out previous calls to the constructor
const mockedClassConsumer = new MockedClassConsumer();
expect(MockedClass).toHaveBeenCalled(); // Constructor has been called
expect(MockedClass.mock.calls[0][0]).toEqual('yo'); // ... with the string 'yo'
});
it('We can check if the consumer called a method on the class instance', () => {
const mockedClassConsumer = new MockedClassConsumer();
expect(mockedMethodImpl).toHaveBeenCalledWith('bro');
// Checking for method call using the stored reference to the mock function
// It would be nice if there were a way to do this directly from MockedClass.mock
});
For me this kind of Replacing Real Class with mocked one worked.
// Content of real.test.ts
jest.mock("../RealClass", () => {
const mockedModule = jest.requireActual(
"../test/__mocks__/RealClass"
);
return {
...mockedModule,
};
});
var codeTest = require("../real");
it("test-real", async () => {
let result = await codeTest.handler();
expect(result).toMatch(/mocked.thing/);
});
// Content of real.ts
import {RealClass} from "../RealClass";
export const handler = {
let rc = new RealClass({doing:'something'});
return rc.realMethod("myWord");
}
// Content of ../RealClass.ts
export class RealClass {
constructor(something: string) {}
async realMethod(input:string) {
return "The.real.deal "+input;
}
// Content of ../test/__mocks__/RealClass.ts
export class RealClass {
constructor(something: string) {}
async realMethod(input:string) {
return "mocked.thing "+input;
}
Sorry if I misspelled something, but I'm writing it on the fly.

Categories