I have been struggling a lot to mock database in one of my side projects while writing junits. Can somebody please help me out here. Below is what the scenario looks like:
Before that, the source code is here - https://github.com/sunilkumarc/track-courier
I have created a model using sequelize module in Nodejs. And I access my db through this model.
I want to mock the db calls when running junits. For example findOne method here which returns a promise (https://github.com/sunilkumarc/track-courier/blob/master/models/parcels.js#L4). Basically when running this particular endpoint I want to skip accessing the db.
Any help is appreciated!
Regards, Sunil
While people have pointed out in the comments to a similar question that it's probably wise to include a test Database, I'm going to answer the question directly.
Utilizing Jest, you can do the following to mock individual calls on specific models
const myUser = User.build({
...attributes,
});
jest.spyOn(User, 'findOne').mockImplementation((options) => Promise.resolve(myUser));
const mockedUser = await User.findOne({});
In my experience, I do agree with the commenters. Mocking specific functions like these are not always the most useful as they may not accurately depict the return values of a specific Sequelize function. For example, User#findOne may return null. If you provide rejectOnEmpty, you will have to build your own logic for handling this, which may differ from the exact logic used in the Sequelize library.
Ultimately your code is likely responsible for handling whatever Sequelize returns correctly, and with the level of integration to your data layer, this is going to be something very difficult to mock correctly - not to mention extremely tedious.
Documentation to Model#findAll, where you can see rejectOnEmtpy: https://sequelize.org/api/v6/class/src/model.js~model#static-method-findAll
I used to use Sinon.JS for mocking anything, in this case I did it as:
// assume "Users" is our table
sinon.replace(Users, "create", () => console.log(`mocked "Users" table's "create" method`));
Related
I have an node-js application that I'm switching from a single-tenant database to a multi-tenant database. The application code is called from an express api but there are also services that run through a different entrypoints, so req.session is not always available.
Currently I have database function calls all throughout the app like:
database.select.users.findByUserId(123, callback)
Since the app is changing to multi-tenant database, I need to be able to send the postgreSQL schemaName to the database functions. I know I can edit the signature of every database call to this:
database.select.users.findByUserId(schemaName, 123, callback)
But it's very labor intensive, broad sweeping, and is going to create a lot of bugs. I'm hoping to find a safe way to pass the postgres schemaName to the database wrapper, without having a race condition of some kind where this "global" schemaName variable is somehow overwritten by another caller, thus sending the wrong data.
Here's some psuedo-code of what I'm considering writing, but I'm worried it wont be "thread-safe" once we deploy.
// as early as possible in the app call:
database.session.initSchema('schema123');
//session.js
let schema = null;
module.exports.initSchema = function (s) {
schema = s;
};
module.exports.getSchema = function () {
return schema;
};
// before I hit the database, i would call getSchema() and pass it to postgreSQL
This approach works, but what if Caller2 calls initForSchema() with different values while Caller1 hasn't finished executing? How can I distinguish which caller is asking for the data when using one variable like this? Is there any way for me to solve this problem safely without editing the signature of every database function call? Thanks for the advice.
edit
I'm leaning towards this solution:
database.session.initSchema('schema123');
//then immediately call
database.select.users.findByUserId(123, callback);
The advantage here is that nothing asynchonous happens between the two calls, which should nullify the race condition possibility, while keeping the original findByUserId signature.
I don't think doing what you're thinking will work because I don't see a way you're going to get around those race conditions. If you do:
app.use((request, response, next) => {
// initialize database schema
next()
})
It would be ideal because then you can do it only once across all routes, but another request might hit the server a millisecond later and it changes the schema again.
Alternatively you can do that in each separate route, which would work, but then you're doing just as much work as just doing it in the database call in the first place. If you have to reinitialize the schema in each route then it's the same thing as just doing it in the call itself.
I was thinking for a while about a solution and then best I can come up with is whether or not you can do it in the connection pool itself. I have no idea what package you're using or how it's creating DB connections. But something like this:
const database = connection.getInstance('schemaExample')
// continue to do database things here
Just to show an example of what I'm thinking. That way you can create multiple connection pools for the different schemas on startup and you can just query on the one with the correct schema avoiding all the race conditions.
The idea being that even if another request comes in now and uses a different schema, it will be executing on a different database connection.
I'm wondering if it is common practice to use an in-memory database for a test environment, instead of MySQL (that has to be used for development/production).
If it makes sense, how do I set this up?
I think I can create config/test.json as shown in their chat example, my app.js still requires Knex though.
Should I do something along the lines of
const knex = (NODE_ENV !== 'test') ? require('./knex') : undefined;
and then configure it only if knex !== undefined?
If I do so, all my models have to be set up twice (once for Knex, once for testing without it).
What is the right/standard way to go about this?
EDIT:
As suggested below, I use a different schema for testing.
This is done by declaring a different connection string in config/test.json.
This question is solved, thank you!
if it is common practice to use an in-memory database for a test environment, instead
Sadly it is a common practise, but not particulary good one. When you use different database for testing and other for production your tests are actually not testing that the application code is working in real database.
Other negative effect is also that you cannot use special features of any of those databases, but the code would have to use that subset of DB features, which are supported by both of the databases.
I would ran all the tests with all the supported real databases to actually make sure that code works on every targeted setup.
ps. Someone was telling to use mocks for abstracting database... that is another bad practise. They work for some small part of testing, but in general case you need to run tests against real database to be sure that code works correctly. Important thing is to setup tests in a way that you have a fast way of truncating old data and populating new test data in.
I was wondering if there is something that I can use to mock my database. Say a function makeRelations makes relation from a certain node to another. I want to test this function but without actually making a relation in my database. Is there an easy way to do so ?
I am using expect and mocha for testing.
To access database (I don't have experience with arangodb), you typically use some driver library. You can fake that driver library calls with proxyquire.
You're testing what is likely already tested at some level.
There's some way to use more than one MongoDB database in meteor keeping reactivity/Oplog working? I've been reading about it (Post1), (Post2) and still I don't see a straighforward way to achieve this. It's possible? What's the right way? Thank you.
As you say; Default Connection isn't really an option as you can only have one DB and DDP is a bit superfluous when you only need a DB and none of the Meteor stuff. I'd think, therefore, your best approach would be to use the MongoInternals option.
The only thing missing from that option is reactivity; a method of enabling oplog tailing for these additional DB connections is mentioned in this answer. It essentially seems to be a case of passing the oplogUrl when creating the RemoteCollectionDriver, here's the example given in their answer:
var driver = new MongoInternals.RemoteCollectionDriver(
"mongodb://localhost:27017/db",
{
oplogUrl: "mongodb://localhost:27017/local"
});
var collection = new Mongo.Collection("Coll", {_driver: driver});
I'm going to write here what I've discovered until now, but I'm not going to mark the question as answered.
Concepts
What is reactivity?
Using reactivity, the data you show to your users will be updated in real time. You can enable or disable reactivity when you subscribe to a collection. Like this:
// enabled by default
Meteor.publish('top', function() {
return Top.find({},{reactive: false});
});
What is oplog?
Oplog is the MongoDB log. When you tell meteor to use oplog, reactivity performance is much better, unless you have a really high volumne of insert operations. In this cases may be wise to keep it disabled. Oplog can optimize your reactive DB calls by ~x5 ~x20.If you are going to use oplog you should optimize your db calls. You can read more about it here.
What methods exists to connect DBS to meteor?
Default connection:
Reactivity? yes. Oplog? yes. DB Limit? One. Description: Meteor creates a default MongoDB database when you run 'meteor'. You can set a different database using enviromental variables, or just MONGO_URL=mongodb://localhost:27017/db_name meteor.
DDP:
Reactivity? yes. Oplog? yes. DB Limit? no. Description: You need a meteor project for every DB. That's about 600MB in memory for every DB. You can read about it here and here.
MongoInternal (SOLUTION: Thanks to carlevans719):
Reactivity? yes. Oplog? yes. DB Limit? no. Description: You can specify a DB in your subscriptions file like:
ar database = new MongoInternals.RemoteCollectionDriver('mongodb://user:password#localhost:27017/meteor', {oplogUrl: "mongodb://localhost:27017/local"});
var numberOfDocs = database.open('boxes').find().count();
Last Words:
MongoInternal If you are not going to use the default db, you have to tell meteor to do not create it. To achieve this you must always run meteor as MONGO_URL=mongodb://localhost:27017 meteor
I'm trying to insert an object in multiple table class, if error happens while inserting in any one of the class how to rollback to previous state like Begin, Commit and Rollback transation in MySQL server
Parse does not have roll-back feature based on their post:https://www.parse.com/questions/saveall-and-rollback--2
I don't think you can do transactions like in MySQL Server or as if you where having a unit of work in Entity Framework.
Best option I can think about is to use the promise pattern. With promises, your code can be much cleaner than the nested code you get with callbacks. This way you can deal with the erro handeling cases nicely but still not the same as transactions.