How do you use distinct with mockingoose? - node.js

I'm using Mockingoose to mock my mongoose calls when running tests with Jest. I tried this but I get an error
mockingoose.Account.toReturn(
["593cebebebe11c1b06efff0372","593cebebebe11c1b06efff0373"],
"distinct"
);
Error:
ObjectParameterError: Parameter "obj" to Document() must be an object, got 593cebebebe11c1b06efff0372
So then I try passing it an array of document objects but it just returns the documents. How di I get it to return just an array or strings?
Here's the code inside the function I'm testing:
const accountIDs = await Account.find({
userID: "test",
lastLoginAttemptSuccessful: true
}).distinct("_id");
I'm open to other ways of mocking my mongoose calls if someone knows of a better way. Thanks!

You can't.
My bad. I looked into mockingoose implementation and realized, it kind of "supports" distinct by implementing a mock, but it actually returned just the given documents, as for the other operations.
Opened a pull request for this issue and added a test, so you're example should be valid and working.

I think the answer is to not use mockingoose. You can do it pretty easily with jest alone.
You can use jest.spyOn() and then mockImplementation() to mock the first call like find() and update(). Here's an example of findOneAndUpdate() where we're checking to make sure the correct object is passed:
// TESTING:
// await Timeline.findOneAndUpdate(query, obj);
//
const Timeline = require("./models/user.timeline");
...
const TimelineFindOneAndUpdateMock = jest.spyOn(Timeline, "findOneAndUpdate");
const TimelineFindOneAndUpdate = jest.fn((query, obj) => {
expect(obj.sendDateHasPassed).toBeFalsy();
expect(moment(obj.sendDate).format()).toBe(moment("2018-11-05T23:00:00.000Z").format());
});
TimelineFindOneAndUpdateMock.mockImplementation(TimelineFindOneAndUpdate);
If you want to mock a chained function you can have it return an object with the next chained function you want to call. Here's an example of how to mock a chained distinct() call.
// TESTING:
// let accountIDs = await Account.find(query).distinct("_id");
//
// WILL RETURN:
// ["124512341234","124512341234","124512341234"]
//
const Account = require("./models/user.account");
...
const AccountFindMock = jest.spyOn(Account, "find");
const AccountFindDistinctResult = ["124512341234","124512341234","124512341234"];
const AccountFindDistinctResult = jest.fn(() => AccountFindDistinctResult);
const AccountFindResult = {
distinct: AccountFindDistinct
};
const AccountFind = jest.fn(() => AccountFindResult);
AccountFindMock.mockImplementation(AccountFind);
And after your test runs, if you want to check how many times a function is called like how many times distinct() was called you can add this:
expect(AccountFindDistinct).toHaveBeenCalledTimes(0);

Related

what is the argument "arguments" passed to exec function in mongoose?

i've been trying to use cache with redis in my nodejs mongodb application but i didn't find any tutorial on how to do that except few which are using logic that seems to not be explained in mongoose documentation
const exec = mongoose.Query.prototype.exec;
mongoose.Query.prototype.exec = async
function (){
// our caching logic
return await exec.apply(this, arguments);
}
where does arguments come from ? because it seems to be undefined yet it is used
mongoose.Query.prototype.exec = async function(){
const collectionName = this.mongooseCollection.name;
if(this.cacheMe){
// You can't insert json straight to redis needs to be a string
const key = JSON.stringify({...this.getOptions(),
collectionName : collectionName, op : this.op});
const cachedResults = await redis.HGET(collectionName,key);
// this.op is the method which in our case is "find"
if (cachedResults){
// if you found cached results return it;
const result = JSON.parse(cachedResults);
return result;
}
//else
// get results from Database then cache it
const result = await exec.apply(this,arguments);
redis.HSET(collectionName, key, JSON.stringify(result) , "EX",this.cacheTime);
//Blogs - > {op: "find" , ... the original query} -> result we got from database
return result;
}
clearCachedData(collectionName, this.op);
return exec.apply(this,arguments);
}
and what is this.getOptions()?
i would be thankful if any one can explain me this logic, because i did not find any help in the documentations nor internet blogs and articls
The arguments object is a local variable that is available inside every function in JavaScript and contains the values of the arguments passed to the function.
this.getOptions() is the local method that returns the options to the query.
// A key example for mongoose Redis integration
const key = JSON.stringify({
collectionName: this.mongooseCollection.name,
op: this.op,
options: this.getOptions(),
filter: this.getFilter(),
projection: this.projection(),
populatedPaths: this.getPopulatedPaths(),
});
There are a lot of similar packages on NPM, but I highly recommend standard mongoose and redis ones to get up and running. I assumed your initialization point was similar to this post. This can also be a relevant source.

Jest spy.On() does not call the method in React Js

I write a test for a method:
const methods = {
run: (name) => {
console.log('run');
return name;
}
}
Using const testMethod = jest.spyOn(methods, 'run').mockResolvedValue({});, the console.log('run') is not triggered, but if i write: const testMethod = jest.spyOn(methods, 'run');, the console.log() is triggered. Why in the first case the console.log() is not triggered and how to solve this?
When you use mockResolvedValue, you are replacing your function with a stub. As the purpose of a stub is not to execute the real implementation of the function but just to return a fictitious value, this behavior is normal.
jest.fn().mockResolvedValue({})
is equivalent to:
jest.fn().mockImplementation(() => Promise.resolve({}));
https://jestjs.io/docs/mock-function-api#mockfnmockresolvedvaluevalue
Update:
If you want to verify if your function is called and if it returned a specific value, then:
const spy = jest.spyOn(methods, 'run');
const myName = 'John Doe';
// Call the method...
expect(spy).toBeCalled();
expect(spy).toHaveReturnedWith(myName);

Fake return value of function call, each call with different data

I have function which I would like to fake using sinon. I inject faked function using DI.
Usually I do
fake.resolves(result) but I cannot change resolved value during test.
I execute function three times and I expect different result each time. I would like to do something like here fake.resolvesEach([result1, result2, result3]).
What could I use to solve my problem?
You should use onCall(n) function
Sample 1:
const FetchStub = sinon
.stub()
.onCall(0)
.resolves(serviceAccountAccessTokenRes)
.onCall(1)
.resolves(signJsonClaimRes)
.onCall(2)
.resolves(getTokenRes)
.onCall(3)
.resolves(makeIapPostRequestRes);
const sample = getSample(FetchStub);
Sample 2:
describe("stub", function () {
it("should behave differently on consecutive calls", function () {
const callback = sinon.stub();
callback.onCall(0).returns(1);
callback.onCall(1).returns(2);
callback.returns(3);
assert.equals(callback(), 1); // Returns 1
assert.equals(callback(), 2); // Returns 2
assert.equals(callback(), 3); // All following calls return 3
});
});
You can read documents in https://sinonjs.org/releases/latest/stubs/

Why does my async code not work properly unless I log the promise?

I have some async code that makes calls to a mongo database and inserts/fetches items. When I am developing locally, the code below works fine. However, when I make the mongoose instance connect to MongoDb Atlas, issues arise. In particular, it seems that my code does not work properly unless I console.log the promise, which makes no sense to me. For example, with the console.log statement, all my tests pass as expected. Without it, 35 tests fail... This is because the promise I am expecting returns null, when it should return some JSON object from the database. Is my code not blocking properly?
It feels like I'm dealing with Schrodinger's cat... Any help would be appreciated. Thanks in advance.
Below is an example promise/function call. I then pass it into _executeQuery. I have await on relevant functions, so I don't think it's because I'm missing the word await somewhere.
async _inSomeAsyncFunction = () => {
const dbQueryPromise = this._dbModel.findById(_id, modelView).lean();
await this._executeQuery({ dbQueryPromise, isAccessPermitted: true })
}
_executeQuery basically gets the result of the promise if the user has access.
private _executeQuery = async (props: {
isAccessPermitted: boolean;
dbQueryPromise: Promise<any>;
}): Promise<any> => {
const { isAccessPermitted, dbQueryPromise } = props;
if (!isAccessPermitted) {
throw new Error('Access denied.');
}
console.log(dbQueryPromise, 'promise'); // without this line, dbQueryResult would be null...
const dbQueryResult = await dbQueryPromise;
return dbQueryResult;
};
After some more testing, I found out that the first API call works but any calls after that returns null...
EDIT:
this._dbModel is some mongoose schema. For example,
const dbSchema= new Schema({
name: String,
});
const dbModel = mongoose.model('DbSchema', dbSchema);
Try replacing your dbQueryPromise as follows:
const dbQueryPromise = this._dbModel.findById(_id, modelView).lean().exec();
Mongoose queries do not get executed unless you pass a callBack function or use exec()
For anyone else having similar problems, here's how I solved it:
I changed
const dbQueryResult = await dbQueryPromise;
to
const dbQueryResult = await dbQueryPromise.then((doc) => {
return doc;
});

nodejs - Export queries result to other files

I'm doing some query to retrieve some data from a database, and trying to export said data to be used in my nodejs aplication. But everything I've tried so far, does not work.
r.js
async function site() {
var test = await db
.select("*")
.from("site_credentials")
.then(data => {
return data;
});
return test;
}
module.exports = { user: site().then(data=>{return data})}
but I always get Promise pending. Even when I do the imports:
import users = require("./r")
users.then(data=>{return data})
and still doesnt work. How can I fix this?
Thank you,
For starters, there's no reason to resolve a promise and immediately return the same object resolved in its then block. Just omit the "then" if there is nothing else you need to do.
So this:
async function site() {
var test = await db
.select("*")
.from("site_credentials")
.then(data => {
return data; <--- this isn't necessary. Only adds noise unless there is something else you need to do. It's similar to "catching" and immediately "rethrowing" an error... just pointless
});
return test;
}
Can be this:
async function site() {
var test = await db
.select("*")
.from("site_credentials");
return test;
}
Secondly, I'm not really sure why you're trying to resolve it in the export. Just export the function.
module.exports = site;
Then when you require it elsewhere in your app, call it and resolve it there:
const users = require("./r")
users.then(data=>{
// do something with your data here...
})
Note that in your first example, you are exporting an object, containing a "users" property which is the function. If you do it that way, you would need to invoke it like so:
const users = require("./r")
users.users().then(data=>{
// do something with your data here...
})
You can see that users.users clearly doesn't make sense. So, export properly to avoid that. Export only the function itself, not nested inside some other object.
But, if you look closely, you'll notice another thing I did wrong. I'm exporting a "site" function, yet requiring it in as a "users" function. Naming conventions matter. If this function is called "site" here, you ought to require (or import depending on your module loader...) it in as "site"... thus:
const site = require('./r');
Otherwise you just confuse the crud out of a fellow developer.

Resources