I'm setting up a mongoDB endpoint with NodeJS. Implementing this backend
I seem to have a problem with the code where the function static async injectDB sets a global variable let restaurants which another function static async getRestaurants accesses, but then it turned into undefined
import mongodb from "mongodb"
const ObjectId = mongodb.ObjectID
let restaurants
export default class RestaurantsDAO {|
static async injectDB(conn) {
if (restaurants) {
return
}
try {
restaurants = await conn.db(process.env.RESTREVIEWS_NS).collection("restaurants")
} catch (e) {
console.error(
`Unable to establish a collection handle in restaurantsDAO: ${e}`,
)
}
}
static async getRestaurants({
filters = null,
page = 0,
restaurantsPerPage = 20,
} = {}) {
console.log(restaurants) // undefined
...
getRestaurants is of course called at a much later point than injectDB, if I console.log(restaurants) in that function, it writes out its values. But its undefined when the other function is called. Why is that?
The injectDB function is called at server start, while the getRestaurants is called when someone acceesses the endpoint.
An alternative solution is to open the connection to DB in the getRestaurants function, is that best practice?
See full code for restaurantsDAO.js
Be aware that you cannot know if the await code has finished unless you check for it. It can really help to put console.logs everywhere! See this example:
export default class RestaurantsDAO {
static restaurants
static async injectDB(conn) {
if (RestaurantsDAO.restaurants) {
return
}
try {
console.log("start loading")
RestaurantsDAO.restaurants = await conn.db(process.env.RESTREVIEWS_NS).collection("restaurants")
console.log("finished loading restaurants!")
} catch (e) {
console.error(
`Unable to establish a collection handle in restaurantsDAO: ${e}`,
)
}
}
static showRestaurants() {
if (RestaurantsDAO.restaurants) {
console.log("restaurants are loaded")
} else {
console.log("restaurants not yet loaded")
}
}
}
So if you call injectDB anywhere else in your code, that doesn't guarantee that restaurants is filled right away.
import RestaurantsDAO from "./restaurantsdao.js"
RestaurantsDAO.injectDB(..) // console: "start loading"
RestaurantsDAO.showRestaurants() // console: "restaurants not yet loaded"
// console: "finished loading" (because await has finished)
BTW I think it makes more sense if you make the restaurants variable part of the class, instead of defining it outside of the class on the module.
Related
I'm working with nuxtjs in ssr mode, i'm having this function to setup data for my component. I keep getting
Cannot read property 'data' of undefined. But some how myInfo state still get the data. Can someone explain why this happend. Here is my code
async created() {
this.isLoading = true;
await this.initData();
},
methods: {
async initData() {
let myInfo;
if (this.param) {
if (this.$store.userLogin) {
myInfo= await this.$repositories.api1(this.param);
} else {
myInfo= await this.$repositories.api2(this.param);
}
}
this.myInfo= myInfo.data;
}
}
I'm trying to create a function to setup data for component in Nuxtjs, using async created
I am trying to make a connection to a database and would like to see that my code stops executing while the connection has not been established yet. So it can print an error/success message to the console at the right time (before the program will output that startup was successful).
My current code to establish the connection is:
dbConnectAsync = async () => {
try {
await mongoose.connect("mongodb://localhost:27017/meetstation", { useNewUrlParser: true });
console.log(SUCCESS_MSG);
} catch (err) {
console.log(ERROR_MSG);
console.log(err.name);
}
}
I know it is possible to put all other code inside the try block but this is not desirable as I plan on moving the function that establishes the database connection to an other file.
Is there any way to call this function that forces other execution of code to wait until this function is done executing?
Within your code you can await dbConnectAsync and then run after the successful connection occurs. So code will appear in a separate try/catch block, but not in the internal try/catch of dbConnectAsync.
async function program() {
try {
await dbConnectAsync();
// code that executes after successful connection
} catch (err) {
// handle connection error
}
}
One change I would mention is to use throw in the catch block of dbConnectAsync so that any consuming code can respond to it. My example above won't receive any errors as a result.
Actually your server can't do much without a database. Therefore the most appropriate reaction in case of an error is just to crash. With top-level await I would just write a module like this:
export * from "mongoose";
import { connect } from "mongoose";
await connect("mongodb://localhost:27017/meetstation", { useNewUrlParser: true });
Then whenever you use mongoose, import it from that file and not from "mongoose" itself. That way, no code will run until the database is ready and in case of an error the server does crash.
You could extract the database setup function into module:
// db.js
let connection;
export async function setup() {
try {
connection = await mongoose.connect('mongodb://localhost:27017/meetstation', { useNewUrlParser: true });
console.log(SUCCESS_MSG);
} catch (err) {
console.log(ERROR_MSG);
console.log(err.name);
}
}
export function getConnection() {
return connection;
}
Init the connection and then start your application:
// main.js
import { setup, getConnection } from './db.js';
(async function bootstrap() {
await setup();
// start your application
// using getConnection()
})();
I created an Express REST API using Dependency Injection with Inversify. I have a basic controller class
import { Request, Response, NextFunction } from 'express';
import { injectable, inject } from 'inversify';
import { IUserController } from './IUserController';
import { AppEntity } from '../../enterpriseBusinessRules/entities/AppEntity';
import { UserEntity } from '../../enterpriseBusinessRules/entities/UserEntity';
import { GroupEntity } from '../../enterpriseBusinessRules/entities/GroupEntity';
import { IUserUseCases } from '../../applicationBusinessRules/useCases/IUserUseCases';
import { IOCTypes } from '../../iOC/IOCTypes';
#injectable()
export class UserController implements IUserController {
public userUseCases: IUserUseCases;
constructor(#inject(IOCTypes.IUserUseCases) userUseCases: IUserUseCases) {
this.userUseCases = userUseCases;
}
public async fetchUsers(request: Request, response: Response, next: NextFunction): Promise<void>{
try {
const users: UserEntity[] = await this.userUseCases.fetchUsers(request);
response.status(200).json({
message: 'Users were fetched.',
users,
});
} catch (error) {
next(error);
}
}
}
Whenever I request the route /users the function fetchUsers gets executed. Unforunately my API crashes and throws this error
RangeError [ERR_HTTP_INVALID_STATUS_CODE]: Invalid status code:
undefined
I debugged and found this error
TypeError: Cannot read property 'userUseCases' of undefined
Within the constructor of the class this is set and works fine. Within the fetchUsers function this is undefined. How can I fix this? Because I have to use this to get access to my userUseCases variable.
Edit: Thanks to Adam Kosmalas comment I was able to fix it by binding the function within the constructor
this.fetchUsers = this.fetchUsers.bind(this)
but I don't know if this is the best solution. Then I would have to bind every function in every class within its constructor...
Any other ideas?
Another options is to use arrow functions for method declarations. This way you don't need to explicitly bind this:
public fetchUsers = async (request: Request, response: Response, next: NextFunction): Promise<void> => {
try {
const users: UserEntity[] = await this.userUseCases.fetchUsers(request);
response.status(200).json({
message: 'Users were fetched.',
users,
});
} catch (error) {
next(error);
}
}
Inside the constructor bind "this" to your function using bind(this) this will make sure It will execute the function in context of "this" of the class.
constructor(#inject(IOCTypes.IUserUseCases) userUseCases: IUserUseCases) {
this.userUseCases = userUseCases;
this.fetchUsers = this.fetchUsers.bind(this)
}
It was not working because your function was executing somewhere else thus it was getting 'this' from there
If you don't want to bind and properties are public then you can directly access if from outside of the service like.
const users: UserEntity[] = await this.nameOfTheImportedService.userUseCases.fetchUsers(request);
I have a module, for the purposes of learning testing, that looks like this:
api.js
import axios from "axios";
const BASE_URL = "https://jsonplaceholder.typicode.com/";
const URI_USERS = 'users/';
export async function makeApiCall(uri) {
try {
const response = await axios(BASE_URL + uri);
return response.data;
} catch (err) {
throw err.message;
}
}
export async function fetchUsers() {
return makeApiCall(URI_USERS);
}
export async function fetchUser(id) {
return makeApiCall(URI_USERS + id);
}
export async function fetchUserStrings(...ids) {
const users = await Promise.all(ids.map(id => fetchUser(id)));
return users.map(user => parseUser(user));
}
export function parseUser(user) {
return `${user.name}:${user.username}`;
}
Pretty straight forward stuff.
Now I want to test that fetchUserStrings method, and to do that I want to mock/spy on both fetchUser and parseUser. At the same time - I don't want the behaviour of parseUser to stay mocked - for when I'm actually testing that.
I run in the problem that it seems that it is not possible to mock/spy on functions within the same module.
Here are the resources I've read about it:
How to mock a specific module function? Jest github issue. (100+ thumbs up).
where we're told:
Supporting the above by mocking a function after requiring a module is impossible in JavaScript – there is (almost) no way to retrieve the binding that foo refers to and modify it.
The way that jest-mock works is that it runs the module code in isolation and then retrieves the metadata of a module and creates mock functions. Again, in this case it won't have any way to modify the local binding of foo.
Refer to the functions via an object
The solution he proposes is ES5 - but the modern equivalent is described in this blog post:
https://luetkemj.github.io/170421/mocking-modules-in-jest/
Where, instead of calling my functions directly, I refer to them via an object like:
api.js
async function makeApiCall(uri) {
try {
const response = await axios(BASE_URL + uri);
return response.data;
} catch (err) {
throw err.message;
}
}
async function fetchUsers() {
return lib.makeApiCall(URI_USERS);
}
async function fetchUser(id) {
return lib.makeApiCall(URI_USERS + id);
}
async function fetchUserStrings(...ids) {
const users = await Promise.all(ids.map(id => lib.fetchUser(id)));
return users.map(user => lib.parseUser(user));
}
function parseUser(user) {
return `${user.name}:${user.username}`;
}
const lib = {
makeApiCall,
fetchUsers,
fetchUser,
fetchUserStrings,
parseUser
};
export default lib;
Other posts that suggest this solution:
https://groups.google.com/forum/#!topic/sinonjs/bPZYl6jjMdg
https://stackoverflow.com/a/45288360/1068446
And this one seems to be a variant of the same idea:
https://stackoverflow.com/a/47976589/1068446
Break the object into modules
An alternative, is that I would break my module up, such that I'm never calling functions directly within each other.
eg.
api.js
import axios from "axios";
const BASE_URL = "https://jsonplaceholder.typicode.com/";
export async function makeApiCall(uri) {
try {
const response = await axios(BASE_URL + uri);
return response.data;
} catch (err) {
throw err.message;
}
}
user-api.js
import {makeApiCall} from "./api";
export async function fetchUsers() {
return makeApiCall(URI_USERS);
}
export async function fetchUser(id) {
return makeApiCall(URI_USERS + id);
}
user-service.js
import {fetchUser} from "./user-api.js";
import {parseUser} from "./user-parser.js";
export async function fetchUserStrings(...ids) {
const users = await Promise.all(ids.map(id => lib.fetchUser(id)));
return ids.map(user => lib.parseUser(user));
}
user-parser.js
export function parseUser(user) {
return `${user.name}:${user.username}`;
}
And that way I can mock the dependency modules when I'm testing the dependant module, no worries.
But I'm not sure that breaking up the modules like this is even feasible - I imagine that there might be a circumstance where you have circular dependencies.
There are some alternatives:
Dependency injection in the function:
https://stackoverflow.com/a/47804180/1068446
This one looks ugly as though, imo.
Use babel-rewire plugin
https://stackoverflow.com/a/52725067/1068446
I have to admit - I haven't looked at this much.
Split your test into multiple files
Am investigating this one now.
My question: This is all quite a frustrating and fiddly way of testing - is there a standard, nice and easy, way people are writing unit tests in 2018, that specifically solve this issue?
As you've already discovered attempting to directly test an ES6 module is extremely painful. In your situation it sounds like you are transpiling the ES6 module rather than testing it directly, which would likely generate code that looks something like this:
async function makeApiCall(uri) {
...
}
module.exports.makeApiCall = makeApiCall;
Since the other methods are calling makeApiCall directly, rather than the export, even if you tried to mock the export nothing would happen. As it stands ES6 module exports are immutable, so even if you did not transpile the module you would likely still have issues.
Attaching everything to a "lib" object is probably the easiest way to get going, but it feels like a hack, not a solution. Alternatively using a library that can rewire the module is a potential solution, but its extremely hokey and in my opinion it smells. Usually when you're running into this type of code smell you have a design problem.
Splitting up the modules into tiny pieces feels like a poor mans dependency injection, and as you've stated you'll likely run into issues quickly. Real dependency injection is probably the most robust solution, but it's something you need to build from the ground up, it's not something that you can just plug into an existing project and expect to have things working immediately.
My suggestion? Create classes and use them for testing instead, then just make the module a thin wrapper over an instance of the class. Since you're using a class you'll always be referencing your method calls using a centralized object (the this object) which will allow you to mock out the things you need. Using a class will also give you an opportunity to inject data when you construct the class, giving you extremely fine grained control in your tests.
Let's refactor your api module to use a class:
import axios from 'axios';
export class ApiClient {
constructor({baseUrl, client}) {
this.baseUrl = baseUrl;
this.client = client;
}
async makeApiCall(uri) {
try {
const response = await this.client(`${this.baseUrl}${uri}`);
return response.data;
} catch (err) {
throw err.message;
}
}
async fetchUsers() {
return this.makeApiCall('/users');
}
async fetchUser(id) {
return this.makeApiCall(`/users/${id}`);
}
async fetchUserStrings(...ids) {
const users = await Promise.all(ids.map(id => this.fetchUser(id)));
return users.map(user => this.parseUser(user));
}
parseUser(user) {
return `${user.name}:${user.username}`;
}
}
export default new ApiClient({
url: "https://jsonplaceholder.typicode.com/",
client: axios
});
Now lets create some tests for the ApiClient class:
import {ApiClient} from './api';
describe('api tests', () => {
let api;
beforeEach(() => {
api = new ApiClient({
baseUrl: 'http://test.com',
client: jest.fn()
});
});
it('makeApiCall should use client', async () => {
const response = {data: []};
api.client.mockResolvedValue(response);
const value = await api.makeApiCall('/foo');
expect(api.client).toHaveBeenCalledWith('http://test.com/foo');
expect(value).toBe(response.data);
});
it('fetchUsers should call makeApiCall', async () => {
const value = [];
jest.spyOn(api, 'makeApiCall').mockResolvedValue(value);
const users = await api.fetchUsers();
expect(api.makeApiCall).toHaveBeenCalledWith('/users');
expect(users).toBe(value);
});
});
I should note that I have not tested if the provided code works, but hopefully the concept is clear enough.
I would like to export a class which initial state depends on a value returned from a Promise in another module i cannot modify.
Here's the code:
let e = true;
APromiseFromAnotherModule()
.then(value => return value;);
export default class E {
constructor() {
if (e) {
//...
} else {
//...
}
}
}
I also tried with async/await encapsulating the Promise into an async function like this:
let e = true;
getInitialValue = async () => {
return await APromiseFromAnotherModule()
.then(value => e = value;);
};
e = getInitialValue();
export default class E {
constructor() {
if (e) {
//...
} else {
//...
}
}
}
But it doesn't make sense because that one is an async function so obviously it doesn't work.
What am I missing?
module exports are done synchronously. So, they cannot depend upon the results of an asynchronous operation.
And, await only works inside a function. It doesn't actually block the containing function (the containing function returns a promise) so that won't help you make an async operation into a synchronous one either.
The usual ways to deal with a module that uses some async code in its setup is to either export a promise and have the calling code use .then() on the promise or to initialize the module with a constructor function that returns a promise.
The code is only pseudo code so it's hard to tell exactly what your real problem is to show you specific code for your situation.
As an example. If you want to export a class definition, but don't want the class definition used until some async code has completed, you can do something like this:
// do async initialization and keep promise
let e;
const p = APromiseFromAnotherModule().then(val => e = val);
class E {
constructor() {
if (e) {
//...
} else {
//...
}
}
};
// export constructor function
export default function() {
return p.then(e => {
// after async initialization is done, resolve with class E
return E;
});
}
The caller could then use it like this:
import init from 'myModule';
init().then(E => {
// put code here that uses E
}).catch(err => {
console.log(err);
// handle error here
});
This solves several issues:
Asynchronous initialization is started immediately upon module loading.
class E is not made available to the caller until the async initialization is done so it can't be used until its ready
The same class E is used for all users of the module
The async initialization is cached so it's only done once
Edit in 2023. Modern nodejs versions when using ESM modules have top level await so it is possible to await an asynchronous result before your exports.
I understand that #jfriend00's answer works fine, but in my case, there is not just one block of code that depends on the foreign async function's completion.
In my app, configuration loading is asynchronous, so the server must wait for the configs to load before starting. Since there are other files (like routes) that compose the server that also need access to the configs, I would have to reluctantly call .then() in each other file.
Here is how I was able to do it with require statements instead of export.
config.js
Module that can be required by other modules in order to gain access to the global configs.
module.exports.loadAllConfigs = async () => {
const appConfigs = await AsyncConfigLibrary.load();
module.exports.CONFIG = appConfigs;
};
server.js
Main file for Node application that makes use of other modules that require access to global configs.
const { loadAllConfigs } = require('./modules/config');
loadAllConfigs()
.then(() => {
const { CONFIG } = require('./modules/config');
/* import modules */
const auth = require('./modules/auth');
};
auth.js
Module used by server.js that requires access to configs.
const { CONFIG } = require('./config');
const cookieSecret = CONFIG.secretItem;
Therefore, as long as the CONFIG property is set in config.js before any of the other modules attempt to access it, that is, before the modules are require'd, then the single .then() in server.js is sufficient.