import in Java, a
#include in C/C++, or a
The good news was that we were not the only ones who felt this way. While the framework was firmly entrenched in the application, the team began to chip away at the globals and the whole approach that encouraged creation of these globals. The first step was adding asynchronous module definition to the frameworks dependency management function with
require functions. We took this old school code:
And converted it to:
Here SubjectController is defined by subject_controller.js. This makes it trivial to define SubjectController as a mock. However one of the problems is that now the mock has replaced the SubjectController for the entirety of the scope. Having to put all the tests that depend on the mock in one file and the other tests in a different file seemed like a serious limitation, particularly when one might want to use different mocks for the same module.
In response, the team added a
factoryFor function that would behave like so:
Now if subjectcontroller.js_ depends on modules like so:
We can, with the factory, inject our own definitions for these modules:
While these examples are simple, they are obfuscating some aspects. So lets rewind and see a practical example.
The point of this code is to check the web browser engine and version. The code in browser.js is a slightly modified version of the deprecated jQuery.browser. Remember the part about how modules are basically shared scope? We can see this in browser.js, because any consumer of the module is going to get the exact same instance as the first consumer. The first time the code is consumed, lines 3-13 are executed. These extract the engine and the version. The scope within the module now has engine and version defined. All consumers after the first will have only the return value supplied to them. This return value is an anonymous object with
version methods. These methods are run within the scope of the module so they will of course have access to the already set variables.
Moving on to the test, it becomes clear that we need to be able to inject a userAgent string that typically would be accessed from the current browser via
navigator.userAgent (as it is done on line 3 of browser.js). After getting the factory for
browser, we inject jQuery and a known userAgent string for Internet Explorer 10.