Writing unit tests for services in feathers without using a database - node.js

I would like to write some unit tests for feathers services.
I want this test to run completely independent, which means i do not want to use the database.
This is an example snippet of my service which is using sequelize:
src/services/messages/messages.service.js
// Initializes the `messages` service on path `/messages`
const createService = require('feathers-sequelize');
const createModel = require('../../models/messages.model');
const hooks = require('./messages.hooks');
const filters = require('./messages.filter');
module.exports = function (app) {
const app = this;
const Model = createModel(app);
const paginate = app.get('paginate');
const options = {
name: 'messages',
Model,
paginate
};
// Initialize our service with any options it requires
app.use('/messages', createService(options));
// Get our initialized service so that we can register hooks
const service = app.service('messages');
service.hooks(hooks);
if (service.filter) {
service.filter(filters);
}
};
I would maybe try to mock the database with the library sequelize-test-helpers but I am not sure how this would work in combination with feathers.
This is how my current test in typescript for this service looks like:
src/test/services/messages.test.ts
import assert from 'assert';
import { app } from '../../src/app';
describe('\'messages\' service', () => {
before(() => {
// maybe add an entry to the mocked database
});
after(() => {
// maybe delete that entry
});
it('registered the service', () => {
const service = app.service('messages');
assert.ok(service, 'Registered the service');
});
it('returns a single record', async () => {
// get result with id 1 (maybe added item in before-hook)
const res = await service.get(1);
should().exist(res);
res.should.be.a('object');
// more checks...
});
});
The first 'it(...)' was generated by feathers itself and the second 'it(...)' shows the functionality I want the test to have.
But the problem is that I am not sure how to write this test so that the service will not use the original database.
Does anybody of you have an idea how I could write a test for a feathers service without using the actual database?
Thanks in advance!

Set environment to TEST and in config set the database on the test.json . As seen here : https://docs.feathersjs.com/guides/basics/testing.html#test-database-setup

Related

How to use cls-hooked unmanaged transactions?

I'm writing tests with jest and sequelize and I need to keep my database clean for every test, so I want to set a transaction for every test and then rollback at the end.
This is what I've got, but it wont pass the transaction to my tests:
beforeEach(async () => {
this.transaction = await db.sequelize.transaction();
});
test('Database should be clean', async () => {
const role = await db.role.create({
name: 'someName',
});
expect(role.id).toBe(1);
});
afterEach(async () => {
await this.transaction.rollback();
});
Sequelize is already setted to use cls
const Sequelize = require('sequelize');
const config = require('../../config/config.js');
const cls = require('cls-hooked');
const namespace = cls.createNamespace('testing-namespace');
Sequelize.useCLS(namespace);
const sequelize = new Sequelize(config);
...
It would be really helpful if somenone could explain me how to use unmanaged transactions with cls-hooked.
I managed to keep my database clean by using umzug to run seeds programatically, this is my code:
const { db } = require('../models');
const Umzug = require('umzug');
const path = require('path');
const umzug = new Umzug({
migrations: {
path: path.join(__dirname, './../../seeders'),
params: [db.sequelize.getQueryInterface()],
},
logging: false,
});
beforeEach(async () => {
const reverted = await umzug.down({ to: 0 });
const executed = await umzug.up();
});
I tried to use umzug v3 (beta), but it didn't work, so I used the stable version 2. This approach isn't as performant as I'd like, but gets the job done.

AVA Test Fails in case of accessing global variable

We've recently started migrating tests for database models.
Facing an issue while trying to separate out different type of tests in different files.
I am writing some AVA unit tests
In one file test_1.js
it is,
const test = require('ava');
const sDB = require('../services/serviceDB');
const config = require('../../config').production;
const { CONFIG_RDS } = config;
let x = 1;
test.before(async (t) => {
t.context.log = console.log;
// following line connects with database and sets global.db
await loaderDB.connect(CONFIG);
})
test('Test 1 - to access database',async(t)=>{
// test some functionality that accesses global.db
// it passes
})
On the other file test_2.js
const test = require('ava');
const sDB = require('../services/serviceDB');
const config = require('../../config').production;
const { CONFIG_RDS } = config;
let x = 1;
test.before(async (t) => {
t.context.log = console.log;
// wait for db to be connected
await timeout(4000) // custom timeout that awaits
})
test('Test 2 - to access database',async(t)=>{
// test some functionality that accesses global.db
// it FAILS
// It returns exception that global.db is undefined
})
Thanks for your help in advance.
Each test file runs in its own process, so you need to connect to the database in each test file.

NPM Factory-Bot/Girl how to export a factory definition for use in my NodeJS specs?

JS newbie here. I am using Jasmine to test a NodeJS application which uses MongoDB and Mongoose, and I would like to replace my static test fixtures with dynamic factories. https://github.com/ratson/factory-bot looks good to me.
However, all of the examples are from a single file and don't demonstrate exporting/importing between files, so I don't understand what to modules.exports = in order to use a factory in my specs.
I'm also using ES5 if that matters.
My question is: how do I export this definition?
spec/factories/user.js
const factory = require('factory-bot').factory;
factory.setAdapter(new FactoryBot.MongooseAdapter());
const User = require('../models/user');
factory.define('user', User, {
username: 'Bob',
expired: false
});
factory.extend('user', 'expiredUser', {
expired: true
});
And then how do I use my export so that I can make sampleUsers?
spec/controllers/user.js
const reqs = require("../support/require")
describe("GET /users", () => {
describe("index", () => {
var data = {};
var sampleUsers = factory.createMany('user', 5);
beforeEach((done) => {
reqs.Request.get(/users", (error, response, body) => {
data.status = response.statusCode;
data.body = JSON.parse(body);
done();
});
});
it("returns a 200 response status", () => {
expect(data.status).toBe(200);
});
it("responds with the users collection", async () => {
expect(data.body.users).toBe(sampleUsers);
});
});
});
Thanks in advance for any advice.
You just need to require your factory definitions before using them.
Here's an example of what you could do:
spec/factories/user.js
const { factory } = require('factory-bot');
const User = require('../models/user');
factory.setAdapter(new FactoryBot.MongooseAdapter());
factory.define('user', User, {
username: 'Bob',
expired: false
});
factory.extend('user', 'expiredUser', {
expired: true
});
spec/factories/index.js:
const { factory } = require("factory-bot");
// Require factories to use with the exported object
require("./user.js");
module.exports = factory;
spec/controllers/user.js:
const factory = require("../../factories");
...
const sampleUsers = factory.createMany('user', 5);
The key difference between the example above and your sample code is the index.js file which requires factory-bot and all the factory definitions. By requiring the definitions, you will be able to use them.
If you require('factory-bot') directly instead of require('spec/factories'), you will need to require the factory definitions you want to use.

Custom service in feathersjs

I try to write a custom service, but it doesn't work at all. I try to post a request and make two update queries in the collections, but i will not work at all
this is my code
// Initializes the `bedrijven` service on path `/bedrijven`
const createService = require('feathers-mongoose');
const createModel = require('../../models/bedrijven.model');
const hooks = require('./bedrijven.hooks');
const filters = require('./bedrijven.filters');
module.exports = function() {
const app = this;
const Model = createModel(app);
const paginate = app.get('paginate');
const options = {
name: 'bedrijven',
Model,
paginate
};
// Initialize our service with any options it requires
app.post('/bedrijven/setfavo', function(req, res) {
Promise.all([
app.service('bedrijven').update({
owner: req.body.userid
}, {
favo: false
}),
app.service('bedrijven').update(req.body._id, {
favo: true
})
]);
});
app.use('/bedrijven', createService(options));
// Get our initialized service so that we can register hooks and filters
const service = app.service('bedrijven');
service.hooks(hooks);
if (service.filter) {
service.filter(filters);
}
};
Make sure this file is included in your main app.js file.
Something like:
const bedrijven = require('./bedrijven/bedrijven.service.js');
app.configure(bedrijven);
Is there a reason you don't want to use feathers generate service? It would take care of these questions for you.

REST data source in NodeJS/ Express MVC Pattern

What are the best practice to include external REST data sources in an Express MVC application?
Should we create a Model for the entities that we retrieve from external REST sources?
Let's take this practical example :
Our starting point is a user.js model that use mongoose for ODM.
var mongoose = require('mongoose');
var userModel = function () {
//Define a simple schema for our user.
var userSchema = mongoose.Schema({
name: String,
twitterId: Number
});
return mongoose.model('User', userSchema);
};
module.exports = new userModel();
Our objective is to show all tweets for a specific user, so we create a controller controller/userTweets.js where we prepare the data for our View.
How should we include the Twitter REST API in our application to handle this use case? (let's say we are using a nodejs client for twitter apis)
I'm more comfortable to use a specific model for the tweet entity, and then retrieve users tweet from the controller using our model, but how should our tweet.js model looks like?
Or should we design our REST API integration in a different way?
I would create a class called Tweet and a corresponding repository for it.
Assuming you are using es6, because why not.
lets call it tweets.js
'use strict';
module.exports = function (cfg) {
class Tweet {
constructor() {
this.userid = null;
this.text = null;
}
}
class Repo {
static getTweetsForUser(usedId) {
// make a call to twitter api, use https://www.npmjs.com/package/request
// psuedo code
let _ = require('lodash');
getTweets(userid, function (err, tweets) {
return new Promise(function (resolve, reject) {
if (err) {
return reject(err);
}
let data = [],
tweet = new Tweet;
if (! tweets.length) {
return resolve(data);
}
resolve(_.collect(tweets, function (t) {
tweet.userId = userId;
tweet.text = t.getTheTweet;
return tweet;
}));
});
});
}
}
return {
'tweet': Tweet,
'repo' : Repo
}
}
// export whatever modules, like above, lets call it index.js
'use strict';
let _ = require('lodash');
let modules = [
'tweets',
];
// cfg = any app configs that you might need in modules
function init(cfg) {
let core = {};
return _.collect(modules, function (m) {
core[m] = require('./' + m)(cfg);
});
}
module.exports = init;
Example - https://github.com/swarajgiri/express-bootstrap/blob/master/core/index.js
Now in routing side, in your main whatever is your server.js, inject the modules into an instance of express()
app.set('core', require('path/to/core/index')(whateverConfigYouMightNeed))
Once that is done, your route can look something like
'use strict'
let wrap = require('co-wrap');
route.get(':userId/tweets'), wrap(function* (req, res, next) {
let tweets = [];
try {
tweets = yield req.app.get('core').tweets.Repo.getTweetsForUser(req.params.userId)
} catch(e) {
// let the common error handler do its job.
return next(e);
}
// render whatever view you want.
});

Resources