Unit Testing Lazily Loaded AngularJS Artefacts

March 15, 2014 — 3 Comments

As an accompaniment to my blog post entitled ‘Lazy Loading In AngularJS’, I provided a link to a sample app that was intended to provide a very basic example of how to lazy load artefacts using RequireJS. This sample app, however, was not initially setup with unit testing in mind. This was because my aim was merely to give a very simple and easy to follow example of lazy loading and nothing more. My thinking was that the more I added to the project, the more complicated it would become for others to follow. As others have been trying to integrate the concepts found in the sample app, however, some have found that they are not able to test artefacts that are to be lazy loaded. As a result, I have now refactored the app slightly so that these artefacts can now also be easily tested. If you have not yet read the blog post in question, I would recommend that you first do so in order to gain the proper context before reading any further.

Now in an effort to better explain the problem, permit me to recap a bit. For artefacts such as controllers that have been loaded after the application bootstrap to be registered with the app, they have to be registered using their equivalent providers instead of the regular registration methods that are on the module.  In other words, $controllerProvider.register() has to be used to register a lazily loaded controller instead of app.controller(). As a result, these providers have to be retrieved using a config block and then persisted so that they can be used to register lazily loaded artefacts after the configuration phase (i.e., outside of config blocks). The reason that they have to be persisted is because they can only be retrieved during the configuration phase of the app. An example of a config block that is persisting the registration methods of these providers is as follows:

Then a controller, for example, will be registered to be lazily loaded like this:

This is essentially how the sample app was initially setup to work. The problem with this setup, however, is that when in the context of a unit test, the module config block will not be executed and as a result, app.lazy will resolve to undefined, thus causing an error when SomeController.js is executed. Luckily, the solution for this is simple, though it may not initially be obvious. Instead of persisting the relevant provider registration methods to properties of app.lazy, the provider registration methods should instead be used to override their counterparts on the module itself. In other words, the config block should now become…

This way we can get rid of the lazy property and simply allow the controller to be registered on the module directly as follows:

The benefit of this approach is that it allows lazily loaded artefacts to be successfully registered when in the context of a browser, while also allowing these same artefacts to be registered in the regular eagerly loaded manner when in the context of a unit test.

Now while this method may itself seem like a trick or hack (depending on ones perspective), I’m ok with it because it isolates the mechanics that make lazy loading possible, and allows the app to be built in a more ’normal’ fashion (so to speak). This has the benefit that things that require a ’normal’ setup (such as unit testing for example), is more likely to work out of the box. One such benefit is that it may also ‘potentially’ future proof the app in the sense that if the lazy loading implementation in AngularJS 2.0 will simply allow lazily loaded artefacts to be registered against the module in the regular fashion, then I won’t have to do much to upgrade the app (where lazy loading is concerned at least). The only thing I will have to do is to simply remove the portion of code that overrides the registration methods on the module. Only time, however, (or the AngularJS team) will tell if this particular point will actually be of any benefit. I share the point regardless, to highlight the thinking behind it.

So there you have it. This is how I would allow artefacts that will be lazily loaded to also be available for unit testing. It simply involves overriding the registration methods on the module with their provider equivalents, thus allowing artefacts to be registered in the regular fashion (at least on the surface), that supports unit testing. Please don’t hesitate to share any other solutions or insights that you may have. Also, as stated at the beginning of this post, I have updated the sample app so please feel free to check that out as well.

TwitterFacebookGoogle+LinkedInDiggRedditStumbleUponEmail

Ifeanyi Isitor

Posts Twitter Google+

I'm a web application developer / independent contractor, currently residing in London.
  • visormatt

    Great post(s) and thank you for putting these together! Very well written and incredibly useful.

    Thought it might be worth noting that for anyone that hits any dependency errors like: “Error: [$injector:modulerr] Failed to instantiate module ngRoute due to:” that you do need to also load any application dependencies to the the karma.conf.js file in the files array as well. I banged my head on that for a bit :/

  • tishya kaul

    I have tried this approach and now I do not get the ‘undefined object’ error. However, I do get an ‘uncaught object’ error when I write
    ‘app.controllerProvider = $controllerProvider.register’ in the app.js config method. I have followed all your steps correctly. Am I missing out any library?

  • Manoj singh

    What about unit test cases ?