I have a class constructor that has async elements. Later when I create an instance of this class, I want to read a property that will only exist when the constructor finished 100%. I always run into problem Can not read property 'id' of undefined. I'm almost sure this is a problem about async .. await.
class NewPiecePlease {
constructor(IPFS, OrbitDB) {
this.OrbitDB = OrbitDB;
(async () => {
this.node = await IPFS.create();
// Initalizing OrbitDB
this._init.bind(this);
this._init();
})();
}
// This will create OrbitDB instance, and orbitdb folder.
async _init() {
this.orbitdb = await this.OrbitDB.createInstance(this.node);
console.log("OrbitDB instance created!");
this.defaultOptions = { accessController: { write: [this.orbitdb.identity.publicKey] }}
const docStoreOptions = {
...this.defaultOptions,
indexBy: 'hash',
}
this.piecesDb = await this.orbitdb.docstore('pieces', docStoreOptions);
await this.piecesDb.load();
}
...
}
Later I create an instance of this class like this:
(async () => {
const NPP = new NewPiecePlease;
console.log(NPP.piecesDb.id);
// This will give 'undefined' error
})();
How can I tell NodeJS that I want new NewPiecePlease to fully finish? await console.log(NPP.piecesDb.id); does not help, which is understandable, because it won't understand what I'm await-ing. What is the correct way to do this?
You could use a factory for this. They are very good for doing complex, potentially async object creation and to keep your constructors clean and focused.
class NewPiecePlease {
constructor(orbitdb, node, pieceDB) {
this.orbitdb = orbitdb;
this.node = node;
this.pieceDB = pieceDB;
}
static async create(IPFS, OrbitDB) {
const node = await IPFS.create();
const orbitdb = await OrbitDB.createInstance(node);
console.log("OrbitDB instance created!");
const defaultOptions = {
accessController: {
write: [orbitdb.identity.publicKey]
}
}
const docStoreOptions = { ...defaultOptions, indexBy: 'hash' };
const piecesDb = await orbitdb.docstore('pieces', docStoreOptions);
await piecesDb.load();
return new NewPiecePlease(orbitdb, node, piecedb);
}
}
As you can see the create method does all the async stuff and just passes the results into the constructor where it really doesn't have to do anything except assign and maybe validate some arguments.
(async () => {
const NPP = await NewPiecePlease.create(IPFS, OrbitDB);
console.log(NPP.piecesDb.id);
// This will give 'undefined' error
})();
Related
I have a problem with reading the variable from a file, which changes it with the promise function.
I have two modules:
one that exposes configuration, and obtains mac address from the device, the app is running on
one that consumes the configuration
Example
config.js
// some imports
let currentMacAddress;
(async () => {
currentMacAddress = await toMAC(cameraIp);
console.log(currentMacAddress) // works, sets mac address as expected
})();
module.exports = {
currentMacAddress,
}
somefile.js
const { currentMacAddress } = require('./config.js')
console.log(currentMacAddress) // undefined
setTimeout(() => console.log(currentMacAddress), 10000) // undefined, the timeout is enough for this operation
I believe this is because require does import only one time, so it sets the variable with the value of currentMacAddress which is undefined and that's it.
How can I persist the value in currentMacAddress so that any other module could access its updated value?
Make the currentMacAddress a property of the exported object:
const config = {currentMacAddress: undefined};
(async () => {
config.currentMacAddress = await toMAC(cameraIp);
})();
module.exports = config;
And import it without deconstructing:
const config = require('./config.js')
console.log(config.currentMacAddress)
setTimeout(() => console.log(config.currentMacAddress), 10000)
Export the async function from config.js
config.js
const some = async () => {
await timeout(3000);
currentMacAddress = "some value";
return currentMacAddress;
};
function timeout(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
module.exports = some;
Now in other file
somefile.js
import like this
const some = require("./config.js");
const start = async () => {
const currentMacAddress = await some();
console.log(currentMacAddress);
};
start();
Sorry for the bad description but I would like to create a "service.js" -file where I export multiple functions that retrieve data from a database.
something like:
...
import Person from '../models/Person'
import dbConnect from "../lib/dbConnect";
await dbConnect()
const save = async (person) => {
const savedPerson = await Person.save(person)
return savedPerson
}
const geAll = async () => {
const persons = await Person.find({})
return persons
}
...
export default { getAll, save };
But how do I always run dbConnect() when the exported functions are used in the code ? do I need to add the await dbConnect() to every function or is there some smarter way to do this ?
What about a class with await dbConnect() on constructor? Something like:
class MyClass {
constructor() {
return (async () => {
await dbConnect();
return this; // new instance created
})();
}
async save(person) {
const savedPerson = await Person.save(person)
return savedPerson
}
async geAll() {
const persons = await Person.find({})
return persons
}
}
Then:
const myClass = await new MyClass(); // <-- this calls dbConnect()
myClass.geAll();
Scenario is, I have a function that needs to be unit tested: AppleLoginService().
It will contain 3 other functions within it. These need to be mocked. Simple.
//UserService.js
const AppleLoginService = async () => {
await verifyAppleToken();
const response = await verifyAuthorizationCode(authorizationCode);
const id = await RegisterIdpAccount(); //Can ignore for now.
return {id}
}
async function verifyAppleToken() {
//etc code
}
async function verifyAuthorizationCode() {
//etc code
}
The issue is, I need to retain AppleLoginService original code but mock everything else but cannot do so.
I've tried:
But obviously, mocks AppleLoginService which isn't what I want.
jest.mock("UserService");
The functions doesn't actually get mocked in this way which is weird. I've put logs in the functions and ends up printing.
jest.mock("UserService", () => {
const original = jest.requireActual("UserService");
return {
...original,
verifyAppleToken: jest.fn(),
verifyAuthorizationCode: jest.fn()
}
});
Also doesn't get mocked. Original implementation runs.
const verifyToken = jest.spyOn(services, "verifyAppleToken").mockImplementation(() => true);
const verifyCode = jest.spyOn(services, "verifyAuthorizationCode").mockImplementation(() => 1);
const services = require("UserService.js");
const queries = require("SQLTransactionService.js"));
/*
jest.mock() ETC
*/
describe("When using AppleLoginService,", () => {
it("given all values are correct then, return response", async () => {
services.verifyAppleToken.mockReturnValueOnce(true);
services.verifyAuthorizationCode.mockResolvedValueOnce("ANYTHING");
queries.RegisterIdpAccount.mockResolvedValueOnce(1);
const res = await services.AppleLoginService("apple", signUpPayload);
expect(services.verifyAppleToken).toBeCalledTimes(1); // Is called 0 times
expect(services.verifyAuthorizationCode).toBeCalledTimes(1); // Is called 0 times.
expect(res).toBeDefined();
});
});
class person {
constructor() {
...
}
fetchPersonData() {
fetch(api).then((return response) => {
return response;
})
}
async initializePerson() {
const data = await fetchPersonData();
}
}
I'm trying to write a test for initializePerson function but it doesn't get called.
test("pass initializePerson", async( ) => {
const personInstance = new person();
let spy = jest.spyOn(personInstance, "initializePerson").mockImplementationOnce(async () => {
return mock;
});
await personInstance.initializePerson();
expect(spy).toBeCalledTimes(1);
});
There are few mistakes.
(return response) is wrong.
You're spying the method which you're calling. It doesn't make any sense. Spy methods which you've used inside the methods before calling the actual method.
I think you're looking for something like this.
class Person {
constructor() {...}
fetchPersonData() {
return fetch(api).then(response => response.json())
}
async initializePerson() {
const data = await fetchPersonData();
}
}
and test.js
test("pass initializePerson", async () => {
const personInstance = new Person();
const fetchPersonDataSpy = jest.spyOn(personInstance, "fetchPersonData").mockResolvedValue({...})
await personInstance.initializePerson();
expect(fetchPersonDataSpy).toBeCalledTimes(1);
});
If you want test the other method and mock the fetch, read this article. This will help you!
I have an issue with a unit test of a function which calls a class. It seems that it always calls the "official" class instance and not my mocked class. I'm not able to force my function to use my mocked instance...
There is a file with the function I want to test:
const myClass = require('./myClass');
const instance = new myClass();
module.exports.functionToTest = async function () {
// Some stuff...
const value = await instance.myMethod();
// Some stuff that define a result variable (partially with value).
return result;
}
There is a file with my class definition:
module.exports = class myClass {
async myMethod() {
const result = await someStuffWillResolveMaybeTrueOrFalse();
console.log('We used the original myMethod... Mocking has failed.');
return result;
}
}
There is a spec file:
const myFile = require('./myFile');
const myClass = require('./myClass');
describe('My test', async () => {
it('should mock myClass.myMethod in order to return false', () => {
const instance = new myClass();
instance.myMethod = jest.fn().mockResolvedValue(false);
const result = await myFile.functionToTest();
expect(result).toBeTruthy();
}
}
Unfortunately my test is passing (because myMethod return "true") and log "We used the original myMethod... Mocking has failed."
So I want to make my test always fail by mocking that myMethod to return false.
Can you help me? Thanks for your time.
Hm. I've found a solution.
See. A change in my file with the target function.
const myClass = require('./myClass');
// const instance = new myClass(); <== Not here...
module.exports.functionToTest = async function () {
const instance = new myClass(); // <== ...but there.
// Some stuff...
const value = await instance.myMethod();
// Some stuff that define a result variable (partially with value).
return result;
}
And my spec file :
const myFile = require('./myFile');
// I specify to Jest that I'll mock a file
jest.mock('./myClass');
const myClass = require('./myClass');
// I prepare the mock function. In that case a promise wich resolve 'false'
const mMock = jest.fn().mockResolvedValue(false);
// I mock the method 'myMethod' in 'myClass'
myClass.mockImplementation(() => {
return {
myMethod: mMock
};
});
// Then, I just take the test
describe('My test', async () => {
it('should mock myClass.myMethod in order to return false', () => {
const result = await myFile.functionToTest();
expect(result).toBeFalsy();
}
}