Medryx Observations

June 9, 2008

Building (and Testing) Dojo Widgets — MVC?

Filed under: dojo, javascript, oop, unit testing — Tags: , , , , , — maulin @ 8:04 am

There are plenty of tutorials out there about Dojo widgets, so I won’t write another one. But I would like to document here my philosophy for writing widgets, allowing for separation of concerns and perhaps even an MVC architecture.

In my mind, a widget is a hybrid of view component and model component. It builds a UI, then receives events on that UI that it passes on the model. But this can get messy, especially for testing, debugging, and changes later on down the road. So I propose that we separate the widget into its elemental components, and then only combine them in the “final widget”. Here’s what I mean:

Lets create widget called TeamList, that creates a list of teams. When you click on a team you get a dialog with team details.

/widgets
---/ui
------/tests
---------/all.js
---------/runTests.html
---------/TeamListTest.js
------/_TeamList
---------/TeamList.html
---------/TeamListView.js
---------/TeamListController.js
---------/tundra.css
------TeamList.js
---/themes
------/tundra.css

As you can see, I actually seperate my widgets into several files:

  1. TeamList.html: the standard widget template
  2. TeamListView.js: a class that DOES NOT derive from the usual _Widget or _Templated, but that “pretends to”.

    This class is used to manipulate everything related to the UI, and to create the events that the widget will generate (or translate the low level user events to more useful ones for the application).

    The key is that none of the “high-level” events will trigger ANYTHING to happen in the model. That is the job of the controller.

    This makes this class quite isolated, and fairly easy to test and change, as long as it adheres to its contract of high-level events that it will trigger.

    Here is the code for my TeamListView:

     
    dojo.declare("medryx.widgets.ui._TeamList.TeamListView", medryx.AbstractBase, {
        //*** attributes
        //filter the teams
        filter:null,
     
        //sort the teams. this can be an array of sort objects (see AbstractDao.sort)
        //or can be a comma-seperated list of fields (simpler, but less flexible)
        sort:null,
        headerContent:"",
    	footerContent:"",
     
        //*** internal
        templatePath: dojo.moduleUrl("medryx","widgets/ui/teamList/teamList.html"),
        controller:null,
        teams:null,
     
        //** nodes defined in the template -- only defined here so we remember them
        list:null,
     
        /**
         *  abstract method
         *  MUST return a valid initialized controller
         */
        $buildController:null,
     
        postCreate:function() {
            this.controller = $buildController();
            this.loadTeams({
                filter: this.filter,
                sort: this.sort
            });
        },
       /**
         * delegate to the controller.
         */
        loadTeams:function() {
            var deferred = this.controller.loadTeams(arguments);
            deferred.addCallback(dojo.hitch(this, this.displayTeams, this.list);
        },
       displayTeams:function(list, teams) {
    	dojo.forEach(teams, function(team) {
    		var li = document.createElement("li");
    		li.innerHTML = team.getName();
    		list.appendChild(li);
    		dojo.connect(list, "onclick", dojo.hitch(this, "teamClicked", team));
    	});
       },
       teamClicked:function(team, event) {
       }

    So lets point out some interesting things about this “View”. Notice that it does not do anything about actually loading teams, or handling what happens once a team gets clicked. It just translates the low level click event to a high level teamClicked event that passes the team of interest to the event.

    Next, notice that while we implement postCreate(), we are not inheriting (yet) from _Widget or _Templated. Here’s why. Lets say I want to test this class. If it had already inherited from these classes, it would automatically call the lifecycle events of the class. But I don’t necessarily want that to happen… yet. I want to call my postCreate method manually and run my tests to be sure everything in this class is working right, before I start wiring things up.

    One other small point — look at displayTeams. Notice how I actually pass the list to which I want to attach the items to this method. Obviously I could use “this.list” throughout displayTeams, and then I wouldn’t have to pass the argument. But stylistically, I find it easier to write tests for methods that are “self contained” and have as little reference to “this.*” as possible. If I hadn’t passed the list as a parameter, I would have had to remember in my tests to set this.list to a new list before running my test. And you can easily forget to do that. In other words, I try to make my widgets as “stateless” as possible in the View. I like to let the final widget inject all the state that is necessary.

    Here is the (very simple) controller class:

    dojo.declare("medryx.widgets.ui._TeamList.TeamListController", null, {
        view:null,
        constructor:function(view) {
            this.view = view;
            dojo.connect(view, "teamClicked", this, "pageTeam");
        },
     
     /**
      * return a deferred that will callback for all teams
      * that are to be display. the arguments are filter and sort
      * either as parameters or as kw arguments
      */
        loadTeams:function() {
            return medryx.dao.TeamsDao.getAll(arguments);
        },
     
        pageTeam:function(team) {
            var pageForm = new medryx.widgets.ui.PageForm();
            pageForm.setTeam(team);
            pageForm.show();
        }
    });

    This is where I connect this widget to other layers of the application. So all interactions outside of the widget should occur through this controller. Notice how I connect up the “teamClicked” event to a “pageTeam” function. Another advantage of separating the view from the controller is that allI have to do is a use a different controller, and I can get a totally different widget. Again, this is a fairly easy class to test .

    So where does this all come together? In medryx.widgets.ui.TeamList, of course!

    dojo.declare("medryx.widgets.ui.TeamList", [dijit._Widget, dijit._Templated, medryx.widgets.ui._TeamList.TeamListView], {
    	$buildController:function() {
                 return new medryx.widgets.ui._TeamList.TeamListController	(this);
              }
    });

    All this does is create the actual widget class, mixin the _Widget, _Templated to allow the magic of dojo to take over, and to define the controller that will be used.

Simplified Dojo D.O.H. Testing

Filed under: dojo, javascript, oop, unit testing — Tags: , , , , , , — maulin @ 7:19 am

In my last post, I talked about how to use Dojo’s DOH for unit testing. But the more I wrote about it and used it, the more I realized that there were a couple of things that made it difficult for me to wrap my head around.

First, setUp and tearDown were not similar to their counterparts in other unit-testing frameworks, such as JUnit.

Secondly, the whole model for programming an asynchronous test seemed a bit confusing.

So I have come up with what seems to me to be a ridiculously simple solution — a custom “test fixture”.

As you recall, when registering a set of tests, you pass the name of the set followed by an array of test methods or test fixtures. The methods are simple function calls. The test fixture had the following properties:

  1. name
  2. runTest
  3. timeout
  4. setUp
  5. tearDown

In all the examples I found, this was declared as an associative array, setting each of these properties.

So a typical test looked like:

doh.register("some.test.package", [
function testOne() {
},
{
	name:"test2",
	setUp:function() {
		doSomeSetup();
	},
	tearDown:function() {
		doSomeTearDown();
	},
	runTest:function() {
		doh.assertTrue(doSomethingTrue());
	}
},
{
	name:"test3",
	setUp:function() {
		doSomeSetup();
	},
	tearDown:function() {
		doSomeTearDown();
	},
	runTest:function() {
		doh.assertTrue(doSomethingElseTrue());
	}
}
]);

But what if instead we write a class that implements all the above properties? Then we could subclass that class for a particular set of tests that shared a setUp/tearDown series. So in my first cut, I wrote a simple TestFixture class that looks like this:

dojo.declare("medryx.util.test.TestFixture", null, {
	constructor:function(testName, test) {
		this.name = testName;
		this.runTest = test;
	}
});

Okay, so its not rocket science. But already I feel like it cleaned up my tests substantially. Because now I have:

dojo.declare("some.test.fixture", medryx.util.test.TestFixture, {
	setUp:function() {
		doSomeSetup();
	},
	tearDown:function() {
		doSomeTearDown();
	}
});
 
doh.register("some.test.package", [
function testOne() {
},
new some.test.fixture("test2", function() {
	doh.assertTrue(doSomethingTrue());
}),
new some.test.fixture("test3", function() {
	doh.assertTrue(doSomethingElseTrue());
})
]);

Well that’s a start. But that got me thinking. Could I use this to simplify asynchronous tests?

As I discussed in my previous post, there is essentially a template for doing asynchronous testing:

  1. Create a doh.Deferred
  2. Do your deferred thing
  3. Attach the doh.Deferred.getTestCallback(), instantiated with your own assertions, to you deferred thing’s callback/errback
  4. Return the doh.Deferred

So I wrote a new and improved fixture:

/**
 * Use this test fixture for asynchronous tests.
 * 
 * @param {Object} testName
 * @param {Object} asyncTest: a method that ALWAYS returns a Deferred (dojo or doh)
 * @param {Object} assertionsCallback: a method that you will use to test your assetions once your defered is done
 */
dojo.declare("medryx.util.test.AsyncTestFixture", null, {
    constructor:function(testName, asyncTest, assertionsCallback) {
        this.name = testName;
        this.asyncTest = asyncTest;
		this.assertionsCallback = assertionsCallback;
    },
	runTest:function() {
		var d = new doh.Deferred();
		var actualDeferred = this.asyncTest();
		actualDeferred.addBoth(d.getTestCallback(dojo.hitch(this, "assertionsCallback")));
		return d;
	}
});

So now my asynchronous tests derive from this AsyncTextFixture and are created as:

	new subclass.of.AsyncTestFixture("testName",  function() {
		var def = doSomethingDeferred();
		return def;
	}, function(result) {
		doh.assertEqual("expected", result);
	});

I just think its easier to “remember” how to do aync testing with this fixture. Thoughts?

June 8, 2008

Dojo D.O.H. Unit Testing

Filed under: dojo, javascript, oop, unit testing — Tags: , , , , , — maulin @ 7:23 am

I have been spending the last few days experimenting with Dojo’s DOH for unit testing my javascript applications. While it has a relatively steep learning curve, and relatively little documentation, once you figure it out, it is actually quite simple and powerful. But it took me almost a whole day just to get up and running!

There are two great and simple articles about DOH on site pen that I found indispensible in learning DOH. The first talks about the logistics of setting up the tests and running them, and the second talks about how to do tests of asynchronous functions. There is a section of the Book of Dojo dedicated to this as well, but I found it hard to follow.

Setting up your test environment

The first trick to getting DOH running is to understand the “DOH Runner” application. This application lives in your /dojotoolkit/util/doh directory. If you launch it directly, it will run all the dojo unit tests. (This, by the way may be the most confusing part of runner.html. I’ve seen lots of posts about how people can’t figure out how to get runner working. I may work on cleaning this up to make it more intuitive for people to use without any difficult initial setup.) But you want to run your tests? To do this you provide the runner with a querystring argument called testModule that is your package with tests. For example, your url may look like:

../dojotoolkit/util/doh/runner.html?testModule=medryx.dao.tests.all

But that assumes that the module you pass in is a “sister” (i.e. at the same folder level) to dojo, dijit, and dojox. If it is not, then you will need a Dojo 1.2 implementation of runner.html and runner.js. Since that is not available yet, you will have to get a snapshot of the latest nightly build or access these files from subversion. These build in support for registerModulePath.

So lets say you have the following structure:

	/
	---/lib
	------/dojotoolkit
	---------/dojo
	---------/dijit
	---------/dojox
	---------/util
	------/mytoolkit
	---------/medryx

That means that when you call dojo.require(”medryx.something”) you want it to look at a path that dojo does not natively understand. So when loading dojo you have to configure djConfig with modulePaths or by calling dojo.registerModulePath() afer loading dojo. Well if you have this structure, and you are running dojo, then I am sure you know how to do this with dojo. The problem is that runner.html loads and tries to run your tests before you even get a chance to run any of your code. So the runner needs to know about the module paths when it is being called. The solution to this was adding registerModulePath as an option on the querystring for runner, so that now your url would look like:

../dojotoolkit/util/doh/runner.html?testModule=medryx.dao.tests.all&registerModulePath=medryx,../../medryx

So what does

medryx.dao.tests.all

look like? My general practice mirrors that of Dojo — I put a tests folder in every package.
The tests folder has the following content:

  1. tests: each group in its own file (roughly a 1:1 number of test files to classes in the package)
  2. all.js: a simple file that dojo.requires all the tests above
  3. runTests.html file which is a simple meta redirect to runner.html with the appropriate querstring arguments already set. Such as:
    <html>
        <head>
        <title>Dojox Unit Test Runner</title>
        <meta http-equiv="REFRESH" content="0;url=../../../dojotoolkit/util/doh/runner.html?testModule=medryx.dao.tests.all&registerModulePath=medryx,../../medryx"></HEAD>
        <BODY>
            Redirecting to D.O.H runner.
        </BODY>
    </HTML>
    

So now execute runTests.html, and you run all the tests in that package. Hurray! But what does a test look like?

Writing Tests with DOH

To register a set of test methods with DOH to be run, you call doh.register with the name of the test, and an array of the actual tests. The tests in the array can be in one of two forms: a simple function(), or an associative array with the following properties: name, runTest (the test method), setUp, and tearDown — similar to junit. Notice that there is no setUp or tearDown that can be written once and then called from before and after every test (as there is in junit). The setUp and tearDown methods here only apply to the one call to runTest that is contained in that fixture. You can sort of get around this by creating a setUp and tearDown method of your own, as a sparsely populated fixture, and then just fill in name and runTest. For example:

dojo.provide("medryx.dao.tests.TeamsDaoTest");
dojo.require("medryx.dao.TeamsDao");
dojo.require("doh.runner");

dojo.declare("medryx.dao.tests.TeamsDaoTest.fixture", null, {
	constructor(name, runTest) {
		this.name = name;
		this.runTest = runTest;
	}
	setUp:function() {
		//do something before every test you want to run
	},
	tearDown:function() {
		//do something after every test you want to run
	}
});

doh.register("medryx.dao.tests.AbstractDaoTest",
[
	new medryx.dao.tests.TeamsDaoTest.fixture("someTest", function(){
		//do your test
	}),
	new medryx.dao.tests.TeamsDaoTest.fixture("someTest2", function(){
		//do your test2
	})
]);

I personally think that is a more natural use of setUp and tearDown. I mean, if I can only use the setUp and tearDown once, then why whouldn’t I just put it in the runTest method?

So now you have a simple set of synchronous tests. You have a set of assertions supported in doh (though I would like to see more in the future). But everything you need is there: assertTrue, assertFalse, and assertEqual. Note that these DO NOT support a message that is displayed during failure. I think that is a problem — it makes finding the reason for your failed test more work than it has to. I would love to see them extend each of these methods to contain an optional 3rd parameter that is a message that gets displayed when there is a failure.

There is a nifty assertError method that I would like to spend a couple seconds on as well. This assertion takes and expected error (i.e. an Error object — which is another reason to always subclass Error for your own exceptions!) as its first parameter. You then pass a scope, and the name of a function with an array of args, and it will execute the method inside of a try-catch, and if it does not get your expected error, it will fail. This is a little complicated, but here is an example that makes it perhaps a little more simple to understand.

{
	name:"testOneInstance",
	instantiate:function() {
		new medryx.dao.TeamsDao();
	},
	runTest:function() {
	   doh.assertError(medryx.exceptions.SingletonException, this, "instantiate");
	}
},

This will fail if creating a new medryx.dao.TeamsDao() does not return a SingletonException.

Asynchronous Tests

DOH really comes into its own with asynchronous testing. This is certainly not intuitive at first, but after you understand the pattern, it becomes fairly easy to implement. I have actually posted some suggestions to the dojo forum about ways that asynchronous testing could be made simpler with a minimal set of modifications, so we’ll see how that goes. Until then, this is how I suggest you implement asynchronous tests. Before you begin, make sure you really understand Deferred’s!

First, you need to understand the contract you are making with DOH when you run an asynchrounous test. You “mark” a test as asynchronous by having your runTest method return an instance of doh.Deferred. Notice that is NOT dojo.Deferred. DOH has its own Deferred implementation that you must return.

When you return a doh.Deferred from your runTest method, you are agreeing to, at some point run the .callback() or the .errback() methods on the deferred you return. The

d.errback(e)

method should be called with any exceptions that occur in your test, or if your assertions fail. Otherwise, call

d.callback(true)

So to summarize:

To Make Your Test Do This Call This
Pass deferred.callback(true);
Fail deferred.errback(some new assertion error)
Fail in Error deferred.errback(error);

Notice how this is VERY different than the usual methodology for running tests in which you do something, and then make some assertions about the outcome, and the assertions themselves handle telling the test-framework whether the test passed or failed.

So an example of implementing an asynchronous test using this method would be: (crediting Dustin Machi)

doh.register("tests.DeferredExample",[
     function myDeferredTest(t){
           var dohDeferred = new doh.Deferred();
           var expectedResult = "foo";         

           var realDeferred = someAsyncFunction();

           realDeferred.addBoth(function(result){
                    if (result==expectedResult) {
                       //our test succeeded, fire the callback
                       dohDeferred.callback(true)
                    }else{
                       dohDeferred.errback(new Error("Unexpected Return: ", result));
                    }
           });

           return dohDeferred;
    },
    .... more test functions
]);

So can you even you assertions in this? Well, not quite. A failed assertion throw an error. If an error occurs in your test you must call errback. So if you surround your assertions in a try-catch block you can use them. So your new test looks like:

doh.register("tests.DeferredExample",[
     function myDeferredTest(t){
           var dohDeferred = new doh.Deferred();
           var expectedResult = "foo";         

           var realDeferred = someAsyncFunction();

           realDeferred.addBoth(function(result){
                    try{
		doh.assertEqual(result, expectedResult);

                       //our test succeeded, fire the callback
                       dohDeferred.callback(true)
                    } catch(e){
                       dohDeferred.errback(new Error("Unexpected Return: ", result));
                    }
           });

           return dohDeferred;
    },
    .... more test functions
]);

Well that is starting to feel a little more natural, but I’m still not quite satisfied. Its still a lot of glue code, and it still feels to hard to write quickly and easily and reliably. Enter the template method pattern.

doh.Deferred contains a method called getTestCallback that is a template for all of the above glue code. All you pass to getTestCallback is a function in which you perform your testing as usual, and the glue code is handled for you. So the above can be simplified to:

doh.register("tests.DeferredExample",[
     function myDeferredTest(t){
           var dohDeferred = new doh.Deferred();
           var expectedResult = "foo";         

           var realDeferred = someAsyncFunction();

           realDeferred.addBoth(dohDeferred.getTestCallback(function(result) {
		   doh.assertEqual(result, expectedResult);
	});

           return dohDeferred;
    },
    .... more test functions
]);

Now that is not too bad! Create the doh.Deferred, run your asynchronous test, and attach the doh.Deferred’s getTestCallback method (containing all of your assertions) to your asynchronous method’s callback and errback handlers. Excellent!

I hope this helps get you started on the wonderful road of javascript unit testing!

June 7, 2008

A Mock RPC Service

Filed under: dojo, javascript — Tags: , , , — maulin @ 6:55 am

I’m tired of server side frameworks. There, I’ve said it. Take your pick — Struts, Tapestry, SpringMVC, php, ASP, ASP.NET. No matter which you choose, I’ve always felt like server side scripting to create web pages and flow in web applications seemed like an obvious violation of seperation of concerns.

But aside from the conceptual argument, I’ve always felt like building pages with these languages was more time consuming than it should be. Edit your page, make sure the server is running, make sure the database is running, make sure you are hitting the database in a known state… blah blah blah. It can take minutes to see the smallest of changes.

As I have grown more and more enamored by extreme programming and unit testing, creating a very rapid cycle of test, code, and test again was of utmost importance. And its just hard to do with heavy weight frameworks.

Now don’t get me wrong, obviously you need server side code. But I think server side code is for managing the model and the business logic. Leave the UI to the client!

So, if you are reading this, I am probably preaching to the choir. But I had to get that off my chest. Now, for the real business of this post. With RPC, we have the ability to use the server to simply expose services to the front end. And with dojo rpc, it is very easy to connect to these services in a very agnostic manner.

Okay, so now you can ditch the heavyweight frameworks for doing things they are not very good at, and use them for things they are good at. So I’ve addressed the SoC issue. (There’s loads of information about using JSON RPC out there, so I won’t repeat it all here.) But what about the extreme programming issue? We want to be able to write client side unit tests and develop against them. (I’ll be posting about my use of Dojo DOH for unit testing sometime soon.) This means not having to wait for the server, or worry about the server in any way.

Well, if you write a web application whose only connection to the server is via RPC, couldn’t you just change the type of RPC Service you are using, and create a “Mock” RPC that appears to talk to a server, but doesn’t? Couldn’t it then always return the data you want, in a known state, without ever having to make any changes on the server? And wouldn’t this then mean that you could test this application in a completely disconnected state? YES!

So here’s how I am doing it.

First, we create a data access layer for all communication with the server. This data access layer does little more than enforce some client side business logic, and delegate to an RpcService. For example:

dojo.declare("medryx.dao.AbstractDao", medryx.AbstractBase, {
        _service:null,
   constructor:function() {
      this._service = new medryx.config.rpcService(medryx.rpc.TeamsService);
   },
        getById:function(id) {
                 return this._service.getById(id);
        }
});

(I will address handling all the Deferred objects in a future post).

So this is a reasonable start. I could probably just change my SMD for the service to point to some static files that have a static JSON response object, and just ignore the parameters. For example:

dojo.provide("medryx.rpc.TeamsService");

medryx.rpc.TeamsService = {
   serviceType:"JSON",
   serviceUrl:"someStaticTeamFile.json",
   methods:[
      {
         name: "getById",
         parameters: [{
         name: "id",
         type: "integer"
      }
]
}

“someStaticTeamFile.json” could be simple text file with the json of a simple response to my service request. However, f I get the same response when the id=1, id=2, or id=null, its not going to feel like the app is really working until I connect to a real server. And what should I do if I want to add more methods to the service?

The SMD is really what I see as a CONTRACT with the server-developers. You promise to adhere to it, and they promise to adhere to it, and beyond that you never need to know how they are working, or what technology they are using. And vice-versa. This is especially true I think for the “methods” property of the SMD. So I would like to avoid changing that.

For a while, I thought my only way around this was to create a “mock” service, using server side code that could interpret the post request and return a different static file for each request, depending on the parameters. That too would have been better than hitting a live database, but it still was slower than I wanted.

And then it hit me. What if we mock the JsonService altogether? This mocked service is a static JSON file that contains all the functions defined in the SMD, and accepts the same parameters described in the SMD. Each function returns the object to the service. So lets look at this code, and you’ll get an idea of what I am talking about:

dojo.provide("medryx.rpc.MockJsonService");
dojo.require("dojo.rpc.JsonService");

dojo.declare("medryx.rpc.MockJsonService", [dojo.rpc.JsonService], {

/**
* this overwrites the bind in the original service.
* it loads the page in the service url first.
* the page should have be javascript object
* that has the method that accepts the given
* params and returns the result object
* @param {Object} method
* @param {Object} parameters
* @param {Object} deferredRequestHandler
* @param {Object} url
*/
bind: function(method, parameters, deferredRequestHandler, url){
   var def = dojo.xhrPost({
   url: url||this.serviceUrl,
   timeout: this.timeout,
   handleAs: "json-comment-optional"
   });

        def.addCallbacks(this.resultCallback(deferredRequestHandler, method, parameters), this.errorCallback(deferredRequestHandler));
},

/**
* this will apply the method and params of the request
* to the received object, and then pass the results
* to the callback of the deferredRequestHandler
*
* @param {Object} deferredRequestHandler
*/
resultCallback:function(deferredRequestHandler, method, parameters) {

var tf = dojo.hitch(this,
   function(mock){
      var obj;
      if (dojo.isArray(mock)) {
         mock = mock[0];
      }
                if (!mock[method]) {
         obj = {id:'unknown', error:{code:-1, message:"Method not implemented."}};
      } else {
         obj = mock[method](parameters);
      }
      if(obj.error!=null){
         var err;
         if(typeof obj.error == 'object'){
            err = new Error(obj.error.message);
            err.code = obj.error.code;
            err.error = obj.error.error;
         } else {
            err = new Error(obj.error);
            err.id = obj.id;
            err.errorObject = obj;
         }
         deferredRequestHandler.errback(err);
      } else{
         deferredRequestHandler.callback(this.parseResults(obj));
      }
   });
   return tf;
}

});

So now, I alter my SMD just a bit:

dojo.provide("medryx.rpc.TeamsService");

medryx.rpc.TeamsService = {
   serviceType:"JSON",
   serviceUrl:"MockTeamService.js", //note the change
   methods:[
      {
         name: "getById",
         parameters: [{
            name: "id",
            type: "integer"
         }]
      }
   ]
}

and then create MockTeamService.js as:

[{
getAll: function(){
      var ret = {};
      ret.result = this.teams;
      ret.id = 1;
      return ret;
   },

getById: function(id) {
   var ret = {};
   ret.id = id;
   for(var i in this.teams) {
      if (this.teams[i].id == id) {
         ret.result = this.teams[i];
         break;
      }
   }
   return ret;
},

teams: [{
   id: 18,
   name: "Adult Triage",
   color: "#C5C3C3",
   abbreviation: "T",
   category: "Triage Teams",
   categorySortIndex: "0",
   pagers: [{
      type: "Attending",
      id: "4900",
      number: "216-6338 x4900"
   }]
}
//... snip... add as many teams as you want here
]
}]

The final piece is making some generic configurations that can be swapped in and out for dev versus prod:

dojo.provide("medryx.config.mock");
dojo.require("medryx.rpc.MockJsonService");

(function() {
   var config = medryx.config;
   config.rpcService = medryx.rpc.MockJsonService;
   config.rpc = {
      userService: config.libUrl + "/mock/MockUserService.js",
      teamService: config.libUrl + "/mock/MockTeamService.js"
   }

})();

and last but not least, I change the “hardwired” information to these config variables:

In TeamsDao change

this._service = dojo.rpc.JsonService()

to

this._service= new medryx.config.rpcService()

and change the serviceUrl of the SMD to medryx.config.teamService from its previous hardcoded “MockTeamService.js”.

So what have we accomplished? Now that all the glue code has been written, each time we want to create or add to a new service, we simply add to or create more “Mock” services that implement the SMD contract. Then we just use them as we would a dynamically loaded server side service. And when you want to switch to the server-side service, simply change you config file (or better yet, swap in the new one).

Questions? Comments? Let me know!

Powered by WordPress