Using Loopback, we have created some custom remote methods, and we want to unit test that logic. What I am looking to accomplish is to load just one model, not all our models, and unit test the custom remote method(s) for that one model.
We can connect this model to the memory database (instead of Postgres in our case), but somehow I need to tell Loopback about this isolated model, without using Loopback boot. (If we use the standard Loopback boot (app.boot()), it will load all our models and the whole shebang, which I think we should avoid for isolation purposes).
We have this setup in a unit test that is a work in progress:
const supertest = require('supertest');
//load the schema for the model
const ContactSchema = require(path.resolve(projectRoot + '/server/models/contact.json'));
const opts = {
strict: true
};
const dataSource = loopback.createDataSource({
connector: loopback.Memory
});
const Contact = dataSource.createModel('Contact', ContactSchema, opts);
//load remote methods for this model
require(path.resolve(projectRoot + '/server/models/contact.js'))(Contact);
const app = loopback();
this.it.cb('test contact', t => {
supertest(app).get('/api/Contacts')
.expect(200)
.end(function (err, res) {
if (err) {
t.fail(err); // we naturally get a 404, because the model hasn't been attached to this Loopback server
}
else {
t.done();
}
});
});
so instead of using Loopback boot, I want to load the model schema and model logic and then attach it to the Loopback app in an isolated way.
Is there a Loopback call we can use, to attach this model to the Loopback server/app?
I am looking for this type of call:
app.useModel(Contact);
Essentially what I am looking to do is something like this:
app.models.Contact = Contact;
but that is definitely the wrong way to do it - just looking for the right API call.
Perhaps this is the right call?
Contact.attachTo(loopback.memory());
Disclaimer: I am a LoopBack maintainer and the original author of loopback-boot@2
The canonical way of setting up a model (which is used by loopback-boot under the hood too) is to call app.registry.createModel(json)
and then app.model(ModelCtor, config)
.
In your particular case:
const app = loopback();
// Consider using local per-app registry of models to avoid
// interference between tests. By default, all LoopBack apps
// share the same global registry (one per process)
// const app = loopback({ localRegistry: true });
// create in-memory datasources
app.dataSource('db', { connector: 'memory' });
//load the schema for the model
const ContactSchema = require(path.resolve(projectRoot + '/server/models/contact.json'));
const Contact = app.registry.createModel(ContactSchema);
//load remote methods for this model
require(path.resolve(projectRoot + '/server/models/contact.js'))(Contact);
// Caveat lector: the configuration may contain more than just dataSource,
// It may be safer to read the model configuration from "server/model-config"
// and override "dataSource" property.
app.model(Contact, { dataSource: 'db' });
// setup REST API
app.use('/api', loopback.rest());
// now we are good to start testing
const supertest = require('supertest');
this.it.cb('test contact', t => {
supertest(app).get('/api/Contacts')
.expect(200)
.end(function (err, res) {
if (err) {
t.fail(err); // we naturally get a 404, because the model hasn't been attached to this Loopback server
}
else {
t.done();
}
});
});
I see two possible caveats with this approach:
server/middleware.json
nor any other additions from boot scripts.I would personally recommend you to try using loopback-boot, but override dataSources and the list of models to be configured in the application. Something along the following lines:
const app = loopback();
boot(app, {
appRootDir: path.resolve('../server'),
env: 'unit-test',
// Alternatively, the "dataSources" configuration for tests
// can be provided in "server/datasources.unit-test.json"
dataSources: {
db: {
connector: 'memory'
}
},
models: {
Contact: {
// as I mentioned before, it's probably better to load this section
// from "server/model-config.json"
dataSource: 'db'
}
},
});
This works because loopback-boot loads models lazily, i.e. only models configured in the app and their parents.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With