Unit Testing Objects Dependent on ArcGIS Server Javascript API

Wed, Apr 14, 2010 4-minute read

Recently, I’ve created a custom Dojo dijit that contains a esri.Map from the ArcGIS Server Javascript API.  The dijit, right now, creates the map on the fly, so it calls new esri.Map(…) and map.addLayer(new esri.layers.ArcGISTiledMapServiceLayer(url)), for example.  This can cause heartburn when trying to unit test the operations that my custom dijit is performing.  I am going to run through the current (somewhat hackish) way I am performing unit tests without having to create a full blown HTML representation of the dijit.

I’ll be using JSpec for this example, so you may want to swing over to that site and brush up on the syntax, which is pretty easy to grok, especially if you’ve done any BDD/spec type unit testing before.

The contrived, but relatively common, scenario for this post is:

  1. My custom dijit makes a call to the server to get information about some feature.
  2. The service returns JSON with the extent of the object in question.
  3. I want my map control to go to that extent.
Following the Arrange-Act-Assert pattern for our unit tests, the vast majority of the work here will be in the Arrange part.  I don’t want to pull in the entire AGS Javascript API for my unit tests.  It’s big and heavy and I am not testing it so I don’t want it.    Also, I don’t want to invoke the call to the server in this test.  Again, it’s not really what I am testing, and it slows the tests down.  I want to test that, if my operation gets the right JSON, it sets the extent on the map properly.

The Whole Test

Here is the whole JSpec file:

[sourcecode language=“javascript”] describe ‘VersionDiff’

before_each vd = new esi.dijits.VersionDiff(); end describe ‘updateImages()’ it ‘should change the extent of the child map’ //Arrange esri={ Map:function(){ this.setExtent=function(obj){ this.extent=obj; }; }, geometry:{ Extent:function(xmin,ymin,xmax,ymax){ var obj={}; obj.xmin=xmin; obj.ymin=ymin; obj.xmax=xmax; obj.ymax=ymax; return obj; } } }; vd.childMap = new esri.Map(); var text = fixture("getFeatureVersionGraphics.txt") text = dojo.fromJson(text) //Act vd.updateMapToFeatureExtent(text) //Assert vd.childMap.extent.xmin.should.be 7660976.8567093275 vd.childMap.extent.xmax.should.be 7661002.6869258471 vd.childMap.extent.ymin.should.be 704520.0600393787 vd.childMap.extent.ymax.should.be 704553.8080708608 end end end

[/sourcecode]

//Arrange

The Arrange portion of the test stubs out the methods that will be called in the esri namespace.  Since the goal of the test is to make sure my djiit changes the extent of the map, all I need to do is stub out the setExtent method on the Map.  setExtent takes an Extent object as an argument, so I create that in my local, tiny esri namespace.  Now I can set the property on my dijit  using my stubbed out map.  Thanks to closures global variables (ahem), the esri namespace I just created will be available inside my function under test.  Closures are sexy, and I only know enough about them to be dangerous.  Yay!  I don’t have to suck in all the API code for this little test.  That fixture function is provided by JSpec, and basically pulls in a text file that has the JSON I want to use for my test.  I created the fixture my saving the output of a call to my service, so now I don’t have to invoke the service inside the unit test.

//Act

This is the easy part.   Call the function under test, passing in our fixture.

//Assert

How do I know the extent was changed?  When I created my tiny esri namespace, my esri.geometry.Extent() function returns an object that has the same xmin/ymin/xmax/ymax properties of an esri.geometry.Extent object.  The setExtent() function on the map stores this object in an extent property.  All I have to do is make sure the extent values match what was in my fixture.

I didn’t include the source to the operation being tested, because I don’t think it adds much to the point.  Suffice it to say that it calls setExtent() on the childMap property.

So What?

I realize this may not be the greatest or cleanest approach, but it is serving my needs nicely.  I am sure in my next refactor of the unit tests that I’ll find a new approach that makes me hate myself for this blog post.  As always, leave a comment if you have any insight or opinion.  Oh, and regardless of this test, you should really look into JSpec for javascript unit testing.  What I show here is barely the tip of what it offers.
Reblog this post [with Zemanta]