Last active
April 14, 2020 22:26
-
-
Save WMcKibbin/6f8adfefbff6d2cf60d0d61ab9f6092f to your computer and use it in GitHub Desktop.
A gist to explain mocking constructor
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/////////////////////////////////////////////////////////////// | |
//Dependency Injection | |
/////////////////////////////////////////////////////////////// | |
/** | |
* Provides Inversion of Control interface wrapping Bottlejs | |
*/ | |
class ApplicationContext { | |
/** | |
* Create a new ApplicationContext with a given name | |
* @param {String} name - The name of the application context | |
*/ | |
constructor(name = 'main') { | |
if (!name) throw new Error('ApplicationContext name must be defined'); | |
if (!typeof (name) === 'string') throw new Error('ApplicationContext name must be a string.'); | |
try { | |
this._bottle = new Bottle(name); | |
} catch (error) { | |
console.error(error) | |
throw new Error(`Unable to create new Application Context with name ${name}`); | |
} | |
this._name = name | |
} | |
/** | |
* Register a new service into the Application Context. Will throw error if name|service is undefined | |
* @param {String} name - The name of the service that is being registered into the ApplciationContext | |
* @param {any} service - The service object to register to the given name. | |
*/ | |
register(name, service) { | |
if (!name) throw new Error('No name given to register service.'); | |
if (!typeof (name) === 'string') throw new Error('Service name must be a string.'); | |
if (!service) throw new Error('No service object given.'); | |
this._bottle.service(name, service); | |
} | |
/** | |
* Get the registered service with a given name. Throws error if name undefined or service not registered. | |
* @param {String} name - The name of the service to retrieve. | |
* @returns {any} - The Service object | |
*/ | |
get(name) { | |
if (!name) throw new Error('Serice name must be defined') | |
if (!typeof (name) === 'string') throw new Error('Service name must be a string.'); | |
const service = this._bottle.container[name]; | |
if (!service) throw new Error(`Service ${name} not found. Please ensure all required services are registered into the Application Context`); | |
return service; | |
} | |
/** | |
* Clear the Application context of all services | |
*/ | |
clear() { | |
Bottle.clear(this._name) | |
} | |
} | |
//Export Singleton Application Context to be used across the application. | |
const appContext = new ApplicationContext() | |
Object.freeze(appContext) | |
export default appContext; | |
/////////////////////////////////////////////////////////////// | |
//User Database | |
/////////////////////////////////////////////////////////////// | |
/** | |
* Data Access object for user profiles | |
*/ | |
export default class UserDao extends Dao { | |
/** | |
* Returns a new UserDao object | |
* @param {Object} user - the populated user object | |
*/ | |
constructor(user) { | |
super() | |
this._user = new User(user); | |
} | |
/** | |
* Returns a new UserDao for the user with the username specified | |
* @param {String} usernameDb - The unique username for the user to fetch | |
* @return {Promise<UserDao>} - The user with the given username | |
*/ | |
static async fromUserName(usernameDb) { | |
if (!await Dao._init()) new Error('Database cannot be initialized'); | |
let foundUser = await User.findOne({ 'usernameDb': usernameDb }).exec(); | |
if (!foundUser) return null | |
return new UserDao(foundUser) | |
} | |
/** | |
* Update user contained in this UserDao | |
* @param {String} type - The type of update for the UserDao | |
* @param {String} key - The key in the UserDao to update | |
* @param {String|Object|Array|Boolean|Date|Number} update - The update for the UserDao | |
* @return {Promise<UserDao>} - The updated UserDao | |
*/ | |
async updateUser(key, update, type) { | |
if (!await Dao._init()) new Error('Database cannot be initialized') | |
const updateSuccess = type ? await this._user.updateOne({[type]:{[key]: update}},{new: true}) : await this._user.updateOne({[key]: update},{new: true}); | |
if (updateSuccess.ok === 1) return this._user.toObject() | |
return null | |
} | |
} | |
/////////////////////////////////////////////////////////////// | |
//Testing | |
/////////////////////////////////////////////////////////////// | |
/* Test Suite for Authentication Controller*/ | |
describe("AuthController Class", function () { | |
//Tests for login functionality | |
describe("#login", function () { | |
before(async function () { | |
context.clear() | |
//Need to mock the user retrieval from the DB, done via UserDao Class | |
sinon.stub(UserDao.fromUserName, async () => { | |
return { | |
username: 'userperson', | |
profile: {}, | |
password: 'bcrypt_hash_goes_here' | |
} | |
}) | |
//This apparently does not work. | |
sinon.stub(UserDao.prototype.constructor, async () => { | |
return { updateUser: async () => { return true } } // | |
}) | |
//This is an application context class that provides dependency injection for the application | |
// Modules grab their dependencies with context.get('service_name') | |
context.register('UserDao', UserDao) | |
}) | |
it("login: Should throw a 400 for a password less than 8 characters", async function () { | |
const auth = new AuthController(process.env.SERVER_SECRET); | |
const shortPassword = '@1234gH' | |
try { | |
const JWT = await auth.login(testUser, shortPassword) | |
throw new Error('AuthController Should have thrown an 400 HttpError.') | |
} catch (error) { | |
if (!error instanceof HttpError) throw error; | |
expect(error.code).to.equal(400) | |
return true | |
} | |
}); | |
}) | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment