** Prior knowledge of unit testing angular apps with Jasmine and Karma is required to understand this post.
Modularity in Angular allows us to unit tests the pieces of application with ease. One can easily mock out services and objects and can unit test the angular component (service, controller, filter etc). But there are objects that need to be mocked out in more than one unit test and sometimes they can be big making the test file look really ugly. For instance, the $modal service can be used a lot in the application and we need to test our apps behavior every time a modal opens or closes. So we will need to mock the $modal service in each unit test and here is one way of doing it:
In the above example, the code highlighted by the red box is a very comprehensive mocked instance and promise of the $modal service of Angular. It is so often used that it is cumbersome to make these two variables in each test file and since it takes a lot of vertical space, it is not readable at all. How cool it would be if I could make it look like this …
It is pretty much possible and here is how …
Create a new directory under tests. You can name it anything; I have named it as ‘mocks’ and then add a new file under that directory and name it as you like; I have named it as ‘FPMocks.js’. This file would contain a module in which you can define factories, values, constants. An example of a factory is shown below.
This factory returns an object which contains a mocked object for promise returned by a modal and modal instance which is injected into the modal controller.
Now we need to tell karma to load this file when running our tests through the karma.conf.js
Please make sure you pay attention to the position of the file in the files array. We want to load this file before any of the tests are loaded on the browser.
Once we have configured karma to use our file, we can add this module (FPMocks) to our test file and inject its service ($modalMock) into our beforeEach function like this …
Now, our spy will always return the promise from the $modalMock service. You can easily call close() ordismiss() in your tests and it will behave the exact same way as it does in the controllers and services in your app making it easier to test your code if the modal is resolved or rejected.
Taking this approach provides us with the following benefits:
- Cleaner and more readable test files
Our tests/specs look a lot cleaner and are easier on the eyes.
- Reusable code
Using Angular module makes this piece of code reusable. It is as simple as a service injection and requires very little, and I mean very little configuration. One can always pull it to other projects as well.
- Code is scale-able and maintainable
Since my mocks are at one place, it is easier to update them if the requirement or the API changes at some point in the future. Going and editing each file is more error prone and tiresome.
You can add as many services, constants, values, factories to this module and they will simply become injectable in the unit tests.
Any questions or suggestions are welcome.