\$\begingroup\$

I've been taking a stab at implementing test cases using the Jasmine testing framework. To do so, I've made an application which has a User object. This User object is created by the server, but its ID is stored in a couple of client-side data storage locations. When a new User model is initialized I check those storage locations before fetching User data from the server.

My model is doing too much work in the constructor. This is making it difficult to test. I am wondering how I could improve my architecture to make it easier to test and also if there are any other mistakes with how I am implementing Backbone, Require, and Jasmine.

// A singleton representing the sole logged on user for the program. // Tries to load itself by ID stored in localStorage and then by chrome.storage.sync. // If still unloaded, tells the server to create a new user and assumes that identiy. define(['programState'], function(programState) { 'use strict'; var userIdKey = 'UserId'; // Loads user data by ID from the server, writes the ID // to client-side storage locations for future loading and then announces // that the user has been loaded fully. function fetchUser(shouldSetSyncStorage) { this.fetch({ success: function(model) { // TODO: Error handling for writing to sync too much. // Write to sync as little as possible because it has restricted read/write limits per hour. if (shouldSetSyncStorage) { chrome.storage.sync.set({ userIdKey: model.get('id') }); } localStorage.setItem(userIdKey, model.get('id')); // Announce that user has loaded so managers can use it to fetch data. model.trigger('loaded'); }, error: function (error) { console.error(error); } }); } // User data will be loaded either from cache or server. var User = Backbone.Model.extend({ defaults: { id: localStorage.getItem(userIdKey), name: '' }, urlRoot: programState.getBaseUrl() + 'User/', // TODO: I am doing too much work in this initialize constructor. initialize: function () { // If user's ID wasn't found in local storage, check sync because its a pc-shareable location, but doesn't work synchronously. if (this.isNew()) { var self = this; // chrome.Storage.sync is cross-computer syncing with restricted read/write amounts. chrome.storage.sync.get(userIdKey, function (data) { // Look for a user id in sync, it might be undefined though. var foundUserId = data[userIdKey]; if (typeof foundUserId === 'undefined') { // No stored ID found at any client storage spot. Create a new user and use the returned user object. self.save({}, { // TODO: I might need to pull properties out of returned server data and manually push into model. // Currently only care about userId, name can't be updated. success: function (model) { // Announce that user has loaded so managers can use it to fetch data. self.trigger('loaded'); }, error: function(error) { console.error(error); } }); } else { // Update the model's id to proper value and call fetch to retrieve all data from server. self.set('id', foundUserId); fetchUser.call(self, false); } }); } else { // User's ID was found in localStorage. Load immediately. fetchUser.call(this, true); } } }); // Return an already instantiated User model so that we have only a single instance available. return new User(); });

and here is my sole test case currently with notes on the others: