chain static methods in mongoose doesn't work - javascript

I tried static methods in es6, any clue why I can't chain my static method like below? Is it even possible to chain 2 static methods?
//nameModel.js
const schema = new mongoose.Schema({ name: String })
class NameClass {
static async findAll() {
return this.find({})
}
}
schema.loadClass(NameClass)
export const model = initModel('NameSchema', schema)
//controller.js
import { model as NameModel } from '../models/nameModel'
export default () => async (req, res) {
try {
const test = await NameModel.findAll()
console.log('test', test) //have all the records
const response = await NameModel.findAll().sort('-name') // NameMode.sort is not a function
} catch (e) {
console.log(e)
}
}
What is the diffrence between static and non static method in mongoose schema? I'm confused as the doc only show code sample. I felt it's redundant as it doesn't show difference between two http://mongoosejs.com/docs/advanced_schemas.html

this in static method is refer to class function itself, since it defined as a method of a class.
class NameClass {
static async findAll() {
return this.find({})
}
}
is equal to:
class NameClass {}
NameClass.findAll = async function() {
return this.find({})
}
see MDN Classes

Related

How can I mock a class using jest?

How can I mock something to test something like the following codes. I tried to follow this official doc, but still not working for me https://jestjs.io/docs/es6-class-mocks#calling-jestmock-with-the-module-factory-parameter
// somefile.ts
export const myPublish = async (event: any, context: any): Promise<any> => {
const myExportHelper = await ExportHelper.getInstance({
...commonProps,
});
// just some other stuff
// just some other stuff
await myExportHelper.transfer(arg1, arg2);
};
export class ExportHelper {
constructor(
private readonly bucket: string,
private readonly read: AWS.S3,
private readonly write: AWS.S3
) {}
static async getInstance(props: {
param1: string;
}) {
...
...
return new ExportHelper(arg1, arg2, arg3);
};
async transfer(param1, param2) {
...
...
console.log('bla bla bla');
}
}
// testfile.test.ts
import { myPublish, ExportHelper } from '../somefile';
beforeEach(() => {
});
afterEach(() => {
jest.clearAllMocks();
jest.resetAllMocks();
});
describe('myTest', () => {
it('should run successfully', async () => {
// Arrange
const eventMock = {
Records: [
{
...
}
]
}
jest.mock('../somefile');
const mockActualExportHelper = jest.requireActual('../somefile').ExportHelper;
const mockGetInstanceImpl = () => {};
// this says cannot read property instances of undefined
const mockExportHelper = mockActualExportHelper.mock.instances[0];
mockExportHelper.getInstance.mockImplementation(mockGetInstanceImpl);
mockExportHelper.transfer.mockImplementation(mockGetInstanceImpl);
// Act
await myPublish(eventMock, jasmine.any({}));
// Assert
expect(ExportHelper.getInstance).toBeCalled();
expect(ExportHelper.transfer).toBeCalled(); // also not sure if this is valid to use ExportHelper
});
});
I think what you're looking for is not a mock. If you want to spy what functions are called, you will need to use the spyOn. In jest you can do the following:
jest.spyOn(MyClass, 'myMethod');
And you can also mock the implementation to subtitute the default behavior of a method, a generalist example can be like this:
jest.spyOn(MyClass, 'myMethod').mockImplementation(jest.fn());
With that said, I would rewrite the test to spy the methods from ExportHelper and avoid external calls:
import {ExportHelper, myPublish} from '../app';
beforeEach(() => {
});
afterEach(() => {
jest.clearAllMocks();
jest.resetAllMocks();
});
describe('myTest', () => {
it('should run successfully', async () => {
// Arrange
const eventMock = {
Records: [
{
// ...
}
]
}
jest.spyOn(ExportHelper, 'getInstance').mockImplementation(jest.fn());
jest.spyOn(ExportHelper, 'transfer').mockImplementation(jest.fn());
// Act
await myPublish('arg1', 'arg2');
// Assert
expect(ExportHelper.getInstance).toBeCalled();
expect(ExportHelper.transfer).toBeCalled();
});
});
I just replaced this piece of code:
jest.mock('../somefile');
const mockActualExportHelper = jest.requireActual('../somefile').ExportHelper;
const mockGetInstanceImpl = () => {};
// this says cannot read property instances of undefined
const mockExportHelper = mockActualExportHelper.mock.instances[0];
mockExportHelper.getInstance.mockImplementation(mockGetInstanceImpl);
mockExportHelper.transfer.mockImplementation(mockGetInstanceImpl);
with
jest.spyOn(ExportHelper, 'getInstance').mockImplementation(jest.fn());
jest.spyOn(ExportHelper, 'transfer').mockImplementation(jest.fn());
Because jest will track down and watch any of these method's calls and then we can use jest's matchers to test if both of them were called. And the mockImplementation will isolate any further calls to be maded.
One thing that I noticed while reproducing your example, is that the transfer method is not being treated as a method when you get the instance from getInstance and therefore, the tests will not pass. But I think this question is not in the scope of the topic. Dunno if just happens to me.

How to stub exported function from module in cypress?

I'm not able to see stubbed response for getName method while invoking getUser from Cypress. Is there a way to correct this?
// Service.ts
export const getName = (): string => {
return name;
}
// User.ts
import {getName} from './Service'
export const getUser = (): User => {
const name = getName();
// ... rest of code for User creation
}
// User.cypress.ts
import * as service from './Service'
it('tests user', () => {
cy.stub(service, 'getName').returns('abc');
cy.get('#get-user-id').click();
});
You may need to change the way the function is exported from Service.ts.
Try adding a default export to the module.
// Service.ts
const getName = (): string => {
return name;
}
module.exports {
getName
}
// User.cypress.ts
import service from './Service'
it('tests user', () => {
cy.stub(service, 'getName').returns('abc');
cy.get('#get-user-id').click();
});

How to execute asynchronous functions in the constructor? [duplicate]

This question already has an answer here:
Asynchronous operations in constructor
(1 answer)
Closed 3 years ago.
I am trying to implement the Customer object in NodeJs and that the instance is able to collect its data.
class CustomerModel extends Model {
public customer
constructor(email:string) {
super();
this.collection = 'customer';
this.customer = await CustomerLayer.getCustomerByEmail(email);
}
}
But I can't have an asynchronous constructor. I have seen that in Javascript you can do the following:
const sleep = () => new Promise(resolve => setTimeout(resolve, 5000));
class Example {
constructor () {
return new Promise(async (resolve, reject) => {
try {
await sleep(1000);
this.value = 23;
} catch (ex) {
return reject(ex);
}
resolve(this);
});
}
}
(async () => {
// It works as expected, as long as you use the await keyword.
const example = await new Example();
console.log(example instanceof Example);
console.log(example.value);
})();
But I think it is not correct to return the data from the constructor.
Is there any correct way to call asynchronous methods from the constructor?
I wouldn't do it in constructor. Probably init method fits this use case better.
class CustomerModel extends Model {
public customer
constructor(email:string) {
super();
this.collection = 'customer';
}
async init() {
this.customer = await CustomerLayer.getCustomerByEmail(email);
}
}
const myClass = new CustomerModel();
await myClass.init();
You could also consider creating a static method to return the instance which internally create object and init.
It's not possible. You have a few options here:
You can explicitly return a Promise from the constructor (construct it via new Promise). This is what you're doing in your second code. await can always be substituted with the Promise constructor and .then. But, it's pretty weird, since one would always expect the result of new Example() to then be an instanceof Example - one wouldn't expect new Example() to result in a Promise. This is what your first code would look like using this method:
class CustomerModel extends Model {
public customer
constructor(email:string) {
super();
this.collection = 'customer';
return CustomerLayer.getCustomerByEmail(email)
.then((customerByEmailResolveValue) => {
this.customerByEmailResolveValue = customerByEmailResolveValue;
return this;
});
}
}
const customerProm = new CustomerModel('foo');
customerProm.then((actualCustomerInstance) => {
// use actualCustomerInstance
});
In the constructor, assign the Promise to a property of the instance. Then, when you need to consume that value, call .then on the property:
class CustomerModel extends Model {
public customer
constructor(email:string) {
super();
this.collection = 'customer';
this.customerProm = CustomerLayer.getCustomerByEmail(email);
}
}
const customer = new CustomerModel('foo');
customer.customerProm.then((result) => {
// use result;
});
You can also retrieve the value before creating the instance, then pass it in:
class CustomerModel extends Model {
public customer
constructor(email:string, customerByEmailResolveValue) {
super();
this.collection = 'customer';
this.customer = customerByEmailResolveValue;
}
}
CustomerLayer.getCustomerByEmail(email)
.then((customerByEmailResolveValue) => {
const customer = new CustomerModel('foo', customerByEmailResolveValue);
});

Unable to call javascript function globally

I am developing nodejs app with mongoDB. I have written a function which will filter some data from mongodb and store in variable "docs". I tried several methods to declare a function globally and tried to access that variable "docs" but I couldn't.`
conn.then(client=> client.db('myprojectone').collection('offlinemessage').find({nameTo:"sd"}).limit(1).toArray(function(err, docs) {
if(err) { console.error(err) }
res.send(JSON.stringify(docs))
console.log(docs);
return docs;
}))
`
Make one helper function which performs all your requirement along with DB query and filter result and returns that result. Exports this function using module.exports like below.
utils.js // Helper file
const utils = {};
utils.getData = async () => {
try {
// ...Your business logic
return true; // It will be your variable
} catch (err) {
logger.error(err);
throw err;
}
};
module.exports = utils;
call utils.getData() to get your result;
userController
const utils = require('./helper/utils');
const userController = {};
userController.getData = async () => {
try {
const result = await utils.getData();
} catch (err) {
logger.error(err);
throw err;
}
};
module.exports = userController;
You can't access async function variable outside of function.
Express.js Gives simple thing to access global functions
fnName = function(){ return "hi"; }; // i.e. don't do: var name = function(){ ... };
console.log(fnName()); // this prints "hi"
console.log(global.fnName()); // this also prints "hi" - it was assigned to global.
One possible solution would be to create a service js class file, called utils.js and then add in this class that you require globally,
utils.js
export default class Example {
static getDocs() {
// Rest of the global method
}
}
And in the place that you require the docs variable, you simply call the method getDocs()
import Utils from '<location_to_utils.js>';
console.log(Utils.getDocs());

How to mock a node module with jest within a class?

I need to mock the DNS node module in a class but I am unsure how to do so as it is enclosed in the class. Here is a sample of what the class looks like...
import { lookup } from 'dns';
class Foo {
// ...
protected async _bar(IP: string) {
// I want to mock "lookup"
await new Promise<undefined>((resolve, reject) => {
lookup(IP, (err, addr) => {
if (err) reject(new Error('DNS Lookup failed for IP_ADDR ' + IP));
resolve();
});
});
// If dns found then return true
return true;
}
// ...
}
I would like to create a test file foo.spec.ts that contains a test similar to the following:
import { Foo } from './Foo';
describe('Foo', () => {
it('Bar Method returns true on success', () => {
const test = new Foo();
expect(test._bar('192.168.1.1')).resolves.toBeTruthy();
});
});
I am unsure how to mock the lookup call within the class Foo given that the class definition is in a separate file from the test itself.
Any help would be appreciated!
The way you are using lookup won't work since it doesn't return a Promise...
...but you can convert it to a version that does return a Promise by using util.promisify.
The code would end up looking something like this:
import { lookup as originalLookup } from 'dns'; // <= import original lookup...
import { promisify } from 'util';
const lookup = promisify(originalLookup); // <= ...and promisify it
export class Foo {
async _bar(IP: string) {
await lookup(IP).catch(err => { throw new Error('Failed'); });
return true;
}
}
You could then mock lookup in your test using jest.mock like this:
import { Foo } from './Foo';
jest.mock('dns', () => ({
lookup: (hostname, callback) => {
hostname === 'example.com' ? callback() : callback('error');
}
}))
describe('Foo', () => {
it('Bar Method returns true on success', async () => {
const test = new Foo();
await expect(test._bar('example.com')).resolves.toBeTruthy(); // Success!
await expect(test._bar('something else')).rejects.toThrowError('Failed'); // Success!
});
});
Note that the mock needs to be created using jest.mock (and not something like jest.spyOn) since calls to jest.mock get hoisted and run first. The mock needs to be in place before Foo.js is imported since the first thing it does is create and store the promisified lookup.
From jest's tutorial,
jest.mock('./sound-player', () => {
return function() {
return {playSoundFile: () => {}};
};
});
so you could do sth like jest.mock('dns', () => ({ ... }));

Categories

Resources