I am trying to implement a singleton pattern for the fastify instance. My code is as follows :-
const { createFastifyServer: server } = require("../app");
const getFastifyInstance = (() => {
let fastify;
return {
fastifyInstance: async () => {
if (!fastify) {
console.log("Called")
fastify = server();
await fastify.ready();
}
return fastify
}
}
})();
const { fastifyInstance } = getFastifyInstance
module.exports = fastifyInstance
Now wherever I am importing the code in a different file, the console prints "Called" each time it's imported in a new file, but shouldn't that be only once if singleton pattern was correctly implemented. Any idea what am I doing wrong?
Related
I don't understand why my spy is not being used. I have used this code elsewhere and it has worked fine.
Here is my test:
const {DocumentEngine} = require('../documentEngine')
const fileUtils = require('../utils/fileUtils')
const request = {...}
const fieldConfig = {...}
test('If the Carbone addons file is not found, context is built with the carboneAddons property as an empty object', async () => {
const expectedResult = {
carboneAddons: {},
}
const fileExistSpy = jest
.spyOn(fileUtils, 'checkFileExists')
.mockResolvedValue(false)
const result = await docEngine.buildContext(request, fieldConfig)
expect(fileExistSpy).toHaveBeenCalledTimes(1)
})
Here is the code that it is being tested:
async function buildContextForLocalResources(request, fieldConfig) {
/* other code */
const addonFormatters = await getCarboneAddonFormatters()
const context = {
sourceJson,
addonFormatters,
documentFormat,
documentTemplateId,
documentTemplateFile,
responseType,
jsonTransformContext
}
return context
}
async function getCarboneAddonFormatters() {
const addOnPath = path.resolve(
docConfig.DOC_GEN_RESOURCE_LOCATION,
'library/addon-formatters.js'
)
if (await checkFileExists(addOnPath)) {
logger.info('Formatters found and are being used')
const {formatters} = require(addOnPath)
return formatters
}
logger.info('No formatters were found')
return {}
}
This is the code from my fileUtils file:
const fs = require('fs/promises')
async function checkFileExists(filePath) {
try {
await fs.stat(filePath)
return true
} catch (e) {
return false
}
}
My DocumentEngine class calls the buildContext function which in turn calls the its method getCarboneAddonFormatters. The fileUtils is outside of DocumentEngine class in a utilities folder. The original code I had this working on was TypeScript as opposed to this which is just NodeJS Javascript. The config files for both are the same. When I try to step through the code (VSCode debugger), as soon as I hit the line with await fs.stat(filePath) in the checkFileExists function, it kicks me out of the test and moves on to the next test - no error messages or warnings.
I've spent most of the day trying to figure this out. I don't think I need to do an instance wrapper for the documentEngine, because checkFileExists is not a class member, and that looks like a React thing...
Any help in getting this to work would be appreciated.
I'm having doubts about which is the best strategy to manage the many service clients in this web app.
"Best" in terms of a good compromise between user's device RAM and Javascript execution speed (main thread ops).
This is what I'm doing right now, this is the main file:
main.ts:
import type { PlayerServiceClient } from './player.client';
import type { TeamServiceClient } from './team.client';
import type { RefereeServiceClient } from './referee.client';
import type { FriendServiceClient } from './friend.client';
import type { PrizeServiceClient } from './prize.client';
import type { WinnerServiceClient } from './winner.client';
import type { CalendarServiceClient } from './calendar.client';
let playerService: PlayerServiceClient;
export const player = async (): Promise<PlayerServiceClient> =>
playerService ||
((playerService = new (await import('./player.client')).PlayerServiceClient()),
playerService);
let teamService: TeamServiceClient;
export const getTeamService = (): TeamServiceClient =>
teamService ||
((teamService = new (await import('./team.client')).TeamServiceClient()),
teamService);
let refereeService: RefereeServiceClient;
export const getRefereeService = (): RefereeServiceClient =>
refereeService ||
((refereeService = new (await import('./referee.client')).RefereeServiceClient()),
refereeService);
let friendService: FriendServiceClient;
export const getFriendService = (): FriendServiceClient =>
friendService ||
((friendService = new (await import('./friend.client')).FriendServiceClient()),
friendService);
let prizeService: PrizeServiceClient;
export const getPrizeService = (): PrizeServiceClient =>
prizeService ||
((prizeService = new (await import('./prize.client')).PrizeServiceClient()),
prizeService);
let winnerService: WinnerServiceClient;
export const getWinnerService = (): WinnerServiceClient =>
winnerService ||
((winnerService = new (await import('./winner.client')).WinnerServiceClient()),
winnerService);
let calendarService: CalendarServiceClient;
export const getCalendarService = (): CalendarServiceClient =>
calendarService ||
((calendarService = new (await import('./calendar.client')).CalendarServiceClient()),
calendarService);
// and so on... a lot more...
As you can see there are many service clients.
I'm using this code because I thought it was better given my web app structure based on routes almost overlapping with client services:
I mean, if the player goes from /home to /players page I can use it like this:
components/players.svelte
import { getPlayerService } from "main";
const playerService = await getPlayerService();
const players = await playerService.queryPlayers();
In this way, if the PlayerService does not exist, it is imported at the moment and returned, otherwise it returns the one imported and instantiated before.
Since the user switches pages frequently this way I can avoid the sudden creation and destruction of those clients, right?
But in this way I am using global variables which I don't like to use and I'm using verbose, DRY and long code in each component.
Is there a way to use the below code in components instead?
import { playerService } from "main";
const players = await playerService.queryPlayers();
What do you suggest me to do?
The patterns you are implementing are "lazy loading" and "singleton".
You could have a single service factory which implements those patterns and use it for every service:
File serviceFactory.js
const serviceMap = {};
export function getService(serviceName) {
return serviceMap[serviceName] ?? (serviceMap[serviceName] = import(serviceName).then(x => new x.default));
}
The ECMAScript modules standard will take care of executing the serviceFactory.js code only once in the application (no matter how many times you import it), so you can hold the singletons in a map assigned to a private top-level variable of the serviceFactory.js module.
This service factory implies that every service is exported with the default keyword like that:
export default class SomeService {
constructor() {
// ...
}
fetchSomething() {
// ...
}
}
Then, use the services everywhere in your application with this code:
import { getService } from './serviceFactory.js';
const service = await getService('./services/some.service.js');
const something = await service.fetchSomething();
If you really want to remove the double await, you can encapsulate it in the service factory like that:
const serviceMap = {};
export function getService(serviceName) {
return serviceMap[serviceName] ?? (serviceMap[serviceName] = resolveService(serviceName));
}
function resolveService(name) {
const futureInstance = import(name).then(x => new x.default);
const handler = {
get: function (target, prop) {
return function (...args) {
return target.then(instance => instance[prop](...args));
}
}
}
return new Proxy(futureInstance, handler);
}
Which allows you to write this code:
const something = await getService('./services/some.service.js').fetchSomething();
This allows the service to be loaded at the exact line of code where you need it.
If it doesn't bothers you to load it with a static import because you need the import { playerService } from "main"; syntax, you can expose every service like this in one file per service:
export const playerService = getService('./services/player.service.js');
I have published the full working demo here: https://github.com/Guerric-P/lazy-singletons-demo
I don't think the code from #Guerric will work with build tools (like webpack.)
Specifically dynamic string imports import(modulePath) is not supported.
My recommendation is to reduce the repeating bits of code to their smallest representation... Hopefully, it'll end up feeling less noisy.
Solution #1/2
Here's an example using a higher-order memoize function to help with the caching.
// Minimal definition of service loaders
export const getPlayerService = memoize<PlayerServiceClient>(async () => new (await import('./player.client')).PlayerServiceClient());
export const getTeamService = memoize<TeamServiceClient>(async () => new (await import('./team.client')).TeamServiceClient());
export const getRefereeService = memoize<RefereeServiceClient>(async () => new (await import('./referee.client')).RefereeServiceClient());
export const getFriendService = memoize<FriendServiceClient>(async () => new (await import('./friend.client')).FriendServiceClient());
export const getPrizeService = memoize<PrizeServiceClient>(async () => new (await import('./prize.client')).PrizeServiceClient());
export const getWinnerService = memoize<WinnerServiceClient>(async () => new (await import('./winner.client')).WinnerServiceClient());
// Mock hacked together memoize fn
// TODO: Replace with some npm library alternative
const fnCache = new WeakMap();
function memoize<TReturn>(fn): TReturn {
let cachedValue = fnCache.get(fn);
if (cachedValue) return cachedValue;
cachedValue = fn();
fnCache.set(fn, cachedValue);
return cachedValue;
}
Solution #2/2
Depending on the version of the JS engine & transpiler, you could possibly cut out some code and use the nature of modules to cache singletons of your services.
(Note: I've occasionally run into gotchas here around how ES Modules rely on deterministic exports. The workaround is to assign the exports to pending promises which return the instance.)
The important feature to know about Promises: they are only resolved once, and can be used to effectively cache their result.
Each await or .then will get the initial resolved value.
// SUPER minimal definition of services
export const playerService = (async (): PlayerServiceClient => new (await import('./player.client')).PlayerServiceClient())();
export const teamService = (async (): TeamServiceClient => new (await import('./team.client')).TeamServiceClient())();
export const refereeService = (async (): RefereeServiceClient => new (await import('./referee.client')).RefereeServiceClient())();
export const friendService = (async (): FriendServiceClient => new (await import('./friend.client')).FriendServiceClient())();
export const prizeService = (async (): PrizeServiceClient => new (await import('./prize.client')).PrizeServiceClient())();
export const winnerService = (async (): WinnerServiceClient => new (await import('./winner.client')).WinnerServiceClient())();
Calling the Service Wrapper
import { playerService } from "./services";
// Example: Using async/await IIFE
const PlayerService = (async () => await playerService)();
function async App() {
// Example: Function-scoped service instance:
// const PlayerService = await playerService
const players = await PlayerService.queryPlayers();
}
Let's say I have the following
class Logger {
// logger class
}
const logger: Logger = new Logger();
// And a few scripts
const script1 = (log: Logger) => () => {
log.info('running script1');
}
const script2 = (log: Logger) => (var1: string) => {
log.info('running script2', var1);
}
I want to basically automatically inject the logger into each of these scripts; I have something like:
const injectLogger = (...scripts) => {
return scripts.reduce((accum, script) => {
accum[script.name] = script(logger);
return accum;
}, {});
}
const scripts = injectLogger(script1, script2);
// scripts.script1 and scripts.script2 are available for usage
This works well, however, the dynamic type definition is not - i.e. IDE does not understand scripts.script1 exists or that scripts.script2 requires a variable var1 of type string.
How do I generate this dynamic type of definition? Can this even be done?
For example, if I have main.js calling a defined in src/lib/a.js, and function a calls node-uuid.v1, how can I stub node-uuid.v1 when testing main.js?
main.js
const a = require("./src/lib/a").a
const main = () => {
return a()
}
module.exports = main
src/lib/a.js
const generateUUID = require("node-uuid").v1
const a = () => {
let temp = generateUUID()
return temp
}
module.exports = {
a
}
tests/main-test.js
const assert = require("assert")
const main = require("../main")
const sinon = require("sinon")
const uuid = require("node-uuid")
describe('main', () => {
it('should return a newly generated uuid', () => {
sinon.stub(uuid, "v1").returns("121321")
assert.equal(main(), "121321")
})
})
The sinon.stub(...) statement doesn't stub uuid.v1 for src/lib/a.js as the above test fails.
Is there a way to globally a library function so that it does the specified behavior whenever it gets called?
You should configure the stub before importing the main module. In this way the module will call the stub instead of the original function.
const assert = require("assert")
const sinon = require("sinon")
const uuid = require("node-uuid")
describe('main', () => {
it('should return a newly generated uuid', () => {
sinon.stub(uuid, "v1").returns("121321")
const main = require("../main")
assert.equal(main(), "121321")
})
})
Bear in mind that node-uuid is deprecated as you can see by this warning
[Deprecation warning: The use of require('uuid') is deprecated and
will not be supported after version 3.x of this module. Instead, use
require('uuid/[v1|v3|v4|v5]') as shown in the examples below.]
About how to stub that for testing would be a bit more harder than before as actually there is no an easy way to mock a standalone function using sinon
Creating a custom module
//custom uuid
module.exports.v1 = require('uuid/v1');
Requiring uuid from the custom module in your project
const uuid = require('<path_to_custom_module>');
Sinon.stub(uuid, 'v1').returns('12345');
In a previous project I mocked the mysql library with Sinon. I did this like so:
X.js:
const con = mysql.createPool(config.mysql);
...
Some other place in the project:
const rows = await con.query(query, inserts);
...
X.test.js:
const sinon = require('sinon');
const mockMysql = sinon.mock(require('mysql'));
...
mockMysql.expects('createPool').returns({
query: () => {
// Handles the query...
},
...
It worked perfectly.
In another project I am trying to mock pg, again with Sinon.
pool.js:
const { Pool } = require('pg');
const config = require('#blabla/config');
const pool = new Pool(config.get('database'));
module.exports = pool;
Some other place in the project:
const con = await pool.connect();
const result = await con.query(...
Y.test.js:
???
I can't understand how to mock connect().query(). None of the following approaches work:
1:
const { Pool } = require('pg');
const config = require('#blabla/config');
const mockPool = sinon.mock(new Pool(config.get('database')));
...
mockPool.expects('connect').returns({
query: () => {
console.log('query here');
},
});
1 results in no error but the real db connection is used.
2:
const { Pool } = sinon.mock(require('pg'));
const config = require('#blabla/config');
const pool = new Pool(config.get('database'));
pool.expects('connect').returns({
query: () => {
console.log('query here');
},
});
2 => TypeError: Pool is not a constructor
3:
const { Pool } = sinon.mock(require('pg'));
const config = require('#blabla/config');
const pool = sinon.createStubInstance(Pool);
pool.connect.returns({
query: () => {
console.log('query here');
},
});
3 => TypeError: The constructor should be a function.
Can anybody point me in the right direction with how to mock my PostgreSQL connection?
Example: I have postgres.js like this.
const { Pool } = require('pg');
const handler = {
count: async (pgQuery) => {
try {
const pool = new Pool();
const res = await pool.query(pgQuery);
return { count: parseInt(res.rows[0].counter, 10) };
} catch (error) {
// Log/Throw error here.
}
return false;
}
}
module.exports = handler;
The spec test I created on postgres.spec.js is like this.
const { expect } = require('chai');
const sinon = require('sinon');
const pgPool = require('pg-pool');
const handler = require('postgres.js');
describe('Postgres', function () {
it('should have method count that bla bla', async function () {
// Create stub pgPool query.
const postgreeStubQuery = sinon.stub(pgPool.prototype, 'query');
postgreeStubQuery.onFirstCall().throws('XXX');
postgreeStubQuery.onSecondCall().resolves({
rows: [{ counter: 11 }],
});
// Catch case.
const catcher = await handler.count('SELECT COUNT()..');
expect(catcher).to.equal(false);
expect(postgreeStubQuery.calledOnce).to.equal(true);
// Correct case.
const correct = await handler.count('SELECT COUNT()..');
expect(correct).to.deep.equal({ count: 11 });
expect(postgreeStubQuery.calledTwice).to.equal(true);
// Restore stub.
postgreeStubQuery.restore();
});
});
To stub pool.query(), you need to stub pg-pool prototype and method query.
Hope this helps.
Since you're needing to mock the returned results of a query, I think the easiest solution would be to abstract your database from the the code needing the query results. Example being, your query results are returning information about a person. Create a person.js module with specific methods for interacting with the database.
Your other code needing the person information from the database won't know or care what type of database you use or how you connect to it, all they care to know is what methods are exposed from person.js when they require it.
//person.js
const { Pool } = require('pg')
// do other database connection things here
const getPersonById = function (id) {
// use your query here and return the results
}
module.exports = { getPersonById }
Now in your tests, you mock the person module, not the pg module. Imagine if you had 20 some odd tests that all had the mock MySQL pool set up then you changed to pg, you'd have to change all of those, nightmare. But by abstracting your database connection type/setup, it makes testing much easier, because now you just need to stub/mock your person.js module.
const person = require('../person.js') //or whatever relative file path it's in
const sinon = require('sinon')
describe('person.js', function () {
it('is stubbed right now', function () {
const personStub = sinon.stub(person)
personStub.getPersonById.returns('yup')
expect(personStub.getPersonById()).to.eq('yup')
})
})
Below is a simpler approach that means the system-under-test doesn't need any special tricks.
It is comprised of two parts, though the first is "nice to have":
Use a DI framework to inject the pg.Pool. This is a better approach IMO anyway, and fits really well with testing.
In the beforeEach() of the tests, configure the DI framework to use a mock class with sinon.stub instances.
If you aren't using a DI framework, pass the mock as a Pool parameter... but DI is better ;)
The code below is TypeScript using tsyringe, but similar approaches will work fine with plain JavaScript etc.
Somewhere you'll have code that uses pg.Pool. A contrived example:
import { Pool } from 'pg'
...
function getPets(pool: Pool): Promise<Pet[]> {
return pool.connect()
.then(db => db.query(SQL_HERE)
.then(result => {
db.release()
return result.rows // or result.rows.map(something) etc
})
.catch(error => {
db.release()
throw error
})
)
}
That works, and it's fine if you want to pass the Pool instance in. I'd prefer not to, so I use tsyringe like this:
import { container } from 'tsyringe'
...
function getPets(): Promise<Pet[]> {
return container.resolve<Pool>().connect()
.then(...)
}
Exactly the same outcome, but getPets() is cleaner to call - it can be a pain to lug around a Pool instance.
The main of the program would set up an instance in one of a few ways. Here's mine:
...
container.register(Pool, {
useFactory: instanceCachingFactory(() => {
return new Pool(/* any config here */)
})
})
The beauty of this comes out in tests.
The code above (the "system under test") needs a Pool instance, and that instance needs a connect() method that resolves to a class with query() and release() methods.
This is what I used:
class MockPool {
client = {
query: sinon.stub(),
release: sinon.stub()
}
connect () {
return Promise.resolve(this.client)
}
}
Here's the setup of a test using MockPool:
describe('proof', () => {
let mockPool: MockPool
beforeEach(() => {
// Important! See:
// https://github.com/microsoft/tsyringe#clearing-instances
container.clearInstances()
mockPool = new MockPool()
container.registerInstance(Pool, mockPool as unknown as Pool)
})
})
The cast through unknown to Pool is needed because I'm not implementing the whole Pool API, just what I need.
Here's what a test looks like:
it('mocks postgres', async () => {
mockPool.client.query.resolves({
rows: [
{name: 'Woof', kind: 'Dog'},
{name: 'Meow', kind: 'Cat'}
]
})
const r = await getPets()
expect(r).to.deep.equal([
{name: 'Woof', kind: 'Dog'},
{name: 'Meow', kind: Cat'}
])
})
You can easily control what data the mock Postgres Pool returns, or throw errors, etc.