I want my module to export multiple functions and numerical constants.
When I import all exported properties of my module I see that functions are not actually imported.
//module1.js
const func1 = (a) => {console.log(1)};
const func2 = (b) => {console.log(2)};
const variable1 = 1;
const variable2 = 2;
export const exp1 = func1;
export const exp2 = func2;
export const exp3 = variable1;
export const exp4 = variable2;
.
//anotherFile.js
import * as module1 from './module1';
console.log(JSON.stringify(module1, null, 2)); // {"module1": {"exp3":1, "exp4":2}}
What is the correct way of importing functions?
All works!
my problem was in console.log(JSON.stringify(module1, null, 2));
JSON.stringify was cutting off my functions
Related
This works…
const { prop1:val1, prop2:val2 ) = req.query
val1 = val1.toLowerCase()
Though, I'm more inclined to do something like
const { prop1.toLowerCase():val1, prop2:val2 } = req.query
or
const { prop1:val1.toLowerCase(), prop2:val2 } = req.query
neither of which work. Is there a syntax similar to this or must manipulations be done outside of the destructing assignment?
No, this is not possible. A destructuring assignment does only assign, it does not do arbitrary transformations on the value. (Setters are an exception, but they would only complicate this).
I would recommend to write
const { prop1, prop2:val2 ) = req.query;
const val1 = prop1.toLowerCase();
or, in one statement:
const { prop1, prop2:val2 ) = req.query, val1 = prop1.toLowerCase();
The trouble with the temporary variable solutions is that they introduce different versions of the same data into the scope, which can lead to bugs.
This solution creates a utility function that receives the object to be destructured as well as a second object that is a mapping of property names to transformation functions. It's a little more verbose, but does the trick.
// Utility functions to perform specified transformations on an object
function transformProps(obj, trans) {
return Object.assign({}, obj, ...Object.entries(trans).map(([prop, fn]) =>
prop in obj ? {[prop]: fn(obj[prop])} : null
));
}
const { prop1:val1, prop2:val2 } = transformProps(
{prop1: "FOO", prop2: "BAR"},
{prop1: v => v.toLowerCase()} // Transformations to be made
);
console.log(val1, val2);
I've just started using feathers to build REST server. I need your help for querying tips. Document says
When used via REST URLs all query values are strings. Depending on the service the values in params.query might have to be converted to the right type in a before hook. (https://docs.feathersjs.com/api/databases/querying.html)
, which puzzles me. find({query: {value: 1} }) does mean value === "1" not value === 1 ? Here is example client side code which puzzles me:
const feathers = require('#feathersjs/feathers')
const fetch = require('node-fetch')
const restCli = require('#feathersjs/rest-client')
const rest = restCli('http://localhost:8888')
const app = feathers().configure(rest.fetch(fetch))
async function main () {
const Items = app.service('myitems')
await Items.create( {name:'one', value:1} )
//works fine. returns [ { name: 'one', value: 1, id: 0 } ]
console.log(await Items.find({query:{ name:"one" }}))
//wow! no data returned. []
console.log(await Items.find({query:{ value:1 }})) // []
}
main()
Server side code is here:
const express = require('#feathersjs/express')
const feathers = require('#feathersjs/feathers')
const memory = require('feathers-memory')
const app = express(feathers())
.configure(express.rest())
.use(express.json())
.use(express.errorHandler())
.use('myitems', memory())
app.listen(8888)
.on('listening',()=>console.log('listen on 8888'))
I've made hooks, which works all fine but it is too tidious and I think I missed something. Any ideas?
Hook code:
app.service('myitems').hooks({
before: { find: async (context) => {
const value = context.params.query.value
if (value) context.params.query.value = parseInt(value)
return context
}
}
})
This behaviour depends on the database and ORM you are using. Some that have a schema (like feathers-mongoose, feathers-sequelize and feathers-knex), will convert values like that automatically.
Feathers itself does not know about your data format and most adapters (like the feathers-memory you are using here) do a strict comparison so they will have to be converted. The usual way to deal with this is to create some reusable hooks (instead of one for each field) like this:
const queryToNumber = (...fields) => {
return context => {
const { params: { query = {} } } = context;
fields.forEach(field => {
const value = query[field];
if(value) {
query[field] = parseInt(value, 10)
}
});
}
}
app.service('myitems').hooks({
before: {
find: [
queryToNumber('age', 'value')
]
}
});
Or using something like JSON schema e.g. through the validateSchema common hook.
There is a specific thing i want to do from time to time, that i cannot figure out:
suppose module1.js exports 3 values:
//module1.js
export const a = 'a';
export const b = 'b';
export const c = 'c';
And then, in module2.js, I want to import two of these, into an object (as a kind of namespace thing):
//module2.js
import { a, c } as constants from './module1'; //<-WRONG!
So what I end up doing is something like this:
//module2.js
import { a, c } from './module1';
const constants = { a, c };
This works, but now a and c exist both in constants and also directly in the module scope.
Is there a way to avoid this?
As per MDN documentation, you can either set an alias on entire module contents such as * as constants or a single content such as b as constants. But you can't set an alias on specific contents. So one of the solutions would be using *.
import * as constants from './module1';
Another possible solution would be passing { a, c } as default.
//module1.js
export const a = ...
export const b = ...
export const c = ...
export const d = ...
export default { a, b, c };
/module2.js
import contants from './someModule';
doStuff(constatns);
Lastly, If you don't want to pass these constants as default, you can create an object and pass that object.
//module1.js
export const a = ...
export const b = ...
export const c = ...
export const b = ...
export const myCustomConstants = { a, c };
//module2.js
import { myCustomConstants } from './someModule';
doStuff(myCustomConstants);
Do you mean something like
import * as constants from './module1';
You could also remove some if you need to pass them down, something like lodash pick
const cleanConstants = _.pick(['a', 'c'], constants);
I'm trying to create unit tests for my class which follows:
MyService.js:
const ApiServce = require('./api-service')
const Config = require('./config')
const Redis = require('ioredis')
class MyService {
constructor () {
const self = this
self.apiService = new ApiServce('MyService', '1.0.0', Config.port)
self.registerRoutes() //this invokes self.apiSerivce.registerRoutes
self.redis = new Redis(Config.redisport, Config.redishost)
self.queueKey = Config.redisqueuekey
}
run () {
const self = this
self.apiService.run()
}
}
module.exports = MyService
Config.js
module.exports = {
port: process.env.SVC_PORT || 8070,
redishost: process.env.REDIS_HOST || '127.0.0.1',
redisport: process.env.REDIS_PORT || 6379,
redisqueuekey: process.env.REDIS_Q_KEY || 'myeventqueue'
}
Test file:
const Redis = require('ioredis')
const MyService = require('../src/myservice')
const ApiService = require('../src/api-service')
const Chai = require('chai')
const Sinon = require('sinon')
const SinonChai = require('sinon-chai')
Chai.use(SinonChai)
const should = Chai.should()
const expect = Chai.expect
describe('MyService', function () {
let apiservicestub, redisstub, apiconststub
beforeEach(function () {
apiservicestub = Sinon.stub(ApiService.prototype, 'registerRoutes')
redisstub = Sinon.stub(Redis.prototype, 'connect')
redisstub.returns(Promise.resolve())
})
describe('.constructor', function () {
it('creates instances of api service and redis client with correct parameters', Sinon.test(function () {
try {
const service = new MyService()
expect(apiservicestub).called
expect(redisstub).called
} catch (e) {
console.error(e)
expect(false)
}
}))
Questions, Issues:
I actually want(ed) to test that the constructors of the dependent classes (apiservice and redis) are being called with the right parameters. But I couldn't find a way so I am currently resorting to one of their methods which is not what I want.
Is there a way in Sinon to achieve this? Do I need to restructure the code to fit Sinon's requirements?
I also want to provide test values for Config items e.g. port to see if they get used. Again I couldn't find a way in Sinon to do that.
I tried the createStubInstance for both 1 and 2 as well but keep getting errors.
Any advice will be appreciated.
In order to make CommonJS modules testable without additional measures, classes should be exclusively used as properties of exports object all through the application. The classes should be destructured from module object in-place. This is not very convenient, but it works with Sinon alone.
I.e.
class ApiService {...}
exports.ApiService = ApiService;
...
const apiServiceModule = require('./api-service');
class MyService {
constructor () {
const { ApiService } = apiServiceModule;
...
In this case the properties on module objects can be mocked before MyService instantiation. Sinon spies don't support classes properly, the constructors should be wrapped:
sinon.stub(apiServiceModule, 'ApiService', function MockedApiService(...) {
return new class { constructor (...) ... };
})
Alternatively, DI can be used, and the app should be refactored according to that. Existing DI libraries (injection-js, inversify, pioc) can handle this job reasonably, but a simple DI pattern looks like this:
class MyService {
constructor (ApiService, ...) {
...
In this case all dependencies can be supplied on construction - both in application and in tests.
But most simple way is to use test-oriented packages that mess with module cache and allow to take control over require calls (rewire, proxyquire, mock-require).
Updated test file, thanks #estus for the direction:
const Redis = require('ioredis')
const ApiService = require('../src/api-service')
const Chai = require('chai')
const Sinon = require('sinon')
const SinonChai = require('sinon-chai')
const Proxyquire = require('proxyquire')
const MyService = require('../src/myservice')
Chai.use(SinonChai)
const should = Chai.should()
const expect = Chai.expect
var namespace = {
apiServiceStubClass: function () {
},
redisStubClass: function () {
}
}
describe('MyService', function () {
let ProxiedMyService
let apiservicestub, redisstub, regroutestub, configstub, apiserviceregroutes, ioredisstub
beforeEach(function () {
apiservicestub = Sinon.stub(namespace, 'apiServiceStubClass')
redisstub = Sinon.stub(namespace, 'redisStubClass')
configstub = {
version: 'testversion',
port: 9999,
redishost: 'testhost',
redisport: 9999,
redisrteventqueuekey: 'testqueyekey'
}
ProxiedMyService = Proxyquire('../src/myservice', {
'./api-service': apiservicestub,
'./config': configstub,
'ioredis': redisstub
})
regroutestub = Sinon.stub(ProxiedMyService.prototype, 'registerRoutes')
regroutestub.returns(true)
apiserviceregroutes = Sinon.stub(ApiService.prototype, 'registerRoutes')
regroutestub.returns(true)
ioredisstub = Sinon.stub(Redis.prototype, 'connect')
ioredisstub.returns(Promise.resolve())
})
afterEach(function () {
namespace.apiServiceStubClass.restore()
namespace.redisStubClass.restore()
ProxiedMyService.prototype.registerRoutes.restore()
ApiService.prototype.registerRoutes.restore()
Redis.prototype.connect.restore()
})
describe('.constructor', function () {
it('creates instances of api service and redis client with correct parameters', Sinon.test(function () {
const service = new ProxiedMyService()
expect(apiservicestub).to.have.been.calledWithNew
expect(apiservicestub).to.have.been.calledWith('MyService', 'testversion', 9999)
expect(regroutestub).to.have.been.called
expect(redisstub).to.have.been.calledWithNew
expect(redisstub).to.have.been.calledWith(9999, 'testhost')
expect(service.queueKey).to.be.equal('testqueyekey')
}))
it('creates redis client using host only when port is -1', Sinon.test(function () {
configstub.redisport = -1
const service = new ProxiedMyService()
expect(redisstub).to.have.been.calledWith('testhost')
}))
})
describe('.registerRoutes', function () {
it('calls apiService registerRoutes with correct url and handler', Sinon.test(function () {
const service = new MyService()
expect.....
}))
})
I would like to have this object:
const m = {a:undefined, b:undefined, d:undefined};
but would prefer to produce it from an array:
const m = {}
const l = ["a","b","c"];
for (const i in l){
m[l[i]] = undefined;
}
But what would be a nicer way of doing this when using es6?
You could use for/of, but Array#reduce (which exists since ES5) might be more elegant:
const m = ['a', 'b', 'c'].reduce((o, k) => (o[k] = undefined, o), {});
However, if you need this a lot, I'd recommend to create a reusable function with a proper name.
You can Array#map each property to an object, and combine them using the spread syntax, and Object#assign:
const m = Object.assign(...['a', 'b', 'c'].map((prop) => ({ [prop]: undefined })));
console.log(m);