What’s an ADP?
Every one of our image, footage and audio files on gettyimages.com is referred to as an asset, and all of the details associated with an asset are viewable on its Asset Detail Page (ADP). Here’s the ADP for asset AA045492:
Notice that in 25 lines of code it introduces 5 distinct external dependencies: ADCookie, AssetSlotTrackingCookie, SetADPCookie, TrackingManager and Redirector. Also notice that it’s not particularly cohesive. It’s responsible for setting values on cookies, doing some sort of tracking, building a URL and finally, redirecting to that URL. Our task was to replace the original version of the URL (http://www.gettyimages.com/detail/AA045492) at the end of the function with the SEO-friendly version requested by the business (http://www.gettyimages.com/detail/photo/brown-bear-roaring-side-view-royalty-free-image/AA045492).
Making our Code Testable
Essentially we wanted our SEO-friendly URL tests to fail when we failed to provide the SEO-friendly URL and not for any other reason. To accomplish this we at least had to deal with any of the external dependencies above which would interfere with testing. We also decided to move each of the responsibilities above into their own object. This would make the existing code more cohesive and enable us to isolate the SEO-friendly URL code into its own object.
Starting from the top, we needed to wrap the ADCookie, AssetSlotTrackingCookie and SetADPCookie dependencies so that these could be injected as mocks from test code. We considered all of these to be a reasonable single responsibility under the heading of “cookie stuff.”
The next unmocked dependency was TrackingManager. It turns out that whatever this object does, it did not need to be mocked. Our first unit test was getting past this line in the original implementation without any problem. So, on we went to the next dependency, the Redirector. Again we used a closure to make a mockable wrapper.
Now, all that was left to do was to wire all of these wrappers back together in a new version of the original function “ADPGoTo”. Once again this is written as a closure, so it is, itself, mockable, and external dependencies are passed in as function arguments so they can be mocked in unit tests.
Testing our Code
Now I’ll add in the arrange steps. The first object declared is an instance of the object under test, the AdpTrafficController. Following this is a set of “any” objects to hold some fake test data. Following that is a set of “mock” objects that will be the targets of Jasmine spies. Notice that the declarations are scoped separately from the assignments. The variable declarations here are in a scope that is global to both the “beforeEach” and “it” functions so they can be accessed by both. However, the assignments must be in the “beforeEach” to retain their value at runtime.
I mentioned Jasmine spies. In the code below you see calls to “spyOn”. These are setting up mock objects that wrap the object they are spying on. In our case we wanted to mock calls out to our external dependencies: the cookie setter, the URL provider and the redirector. This is the simplest possible way to set up mocks in Jasmine. What you see are a set of stubs replacing void methods. Jasmine spies can do a lot more, much like mocks in Moq. They can even call through to the real object they are wrapping if you just want to monitor interactions (more information on spies here). The last line initializes the object under test with our mock objects.
Here, the one and only act step has been added. Our decision to nest “describe” and “beforeEach” calls to represent the “When” and “act” is simply a matter of style. We could have placed the call to adpGoTo in the parent “beforeEach” but we like the approach below better. There’s a clear Given-When description and it maps directly to the arrange-act steps in code. Again, the call to adpGoTo must be inside the scope of a “beforeEach” for any of the objects to retain their value at runtime.
There you have it! While there are other scenarios that we defined and implemented for this work, they generally follow the same structure as outlined here. Below is the generated output from running the spec runner file in a browser.