AngularJS Unit Testing and service mocking
- November 29, 2018
Testing is one of the main aspect in software development and it is more of a vital concern when you follow a practice such as TDD (Test driven development). Developing an AngularJS application can also be done using a TDD approach and it can be implemented in such a way that all the components, services are tested properly as Units.
So above is a basic introduction on the Testing with AngularJS, now we can dig into the real world problem that we encountered while improving the coverage of a Js code base we had.
Actual requirement we had was to display some text label value in a partial. This template contains a list of items where it’s display tag can be either “All Sections” or the section code name. This text label denoted whether the item we are listing belongs to all sections or to a specified section. And this text label is a localized one, so based on the language it should display different localized text label. For an instance consider the below set of items on a template.
Above items set has their display tags listed along with them, see that item “Food” is marked with tag “all_section” so this is based on some business logic we have within the application. And it is bit complicated. It uses some logic based on the Item’s attributes. If the given item is not having a section code it should display the relevant tag name as “all_sections” . So all this display tag population was given as a single unit within the code. Then we wanted to test that Unit , to make sure we are following the logic correctly with some Unit tests. Then we started implementing this test case with AngularJS test cases. As i mentioned earlier this display tag needs to be a localized message (i18N support). So we have used some localization service that we implemented using a third party library to get this localized text label. But this part of the code is not our business logic , it is some third party service we are using hence we do not need to test that part but the business logic of populating the tag code name. At this kind of scenarios we can use AngularJS service mocking mechanism to mock the services that we do not need to test but needed for the testing as utilities to the main concern we have.
Then let’s dig more into the code we have come up with, for Jasmine test cases we can always integrate same kind of test executions to a single test suit. See the below snippet.
So we start with describe, where we denote that we need to group our test cases within this as a single Test suit. And each and every test case is denoted with it(); within that you can execute all your assertions to verify business logic.
Then when you execute each and every test cases within the file, you might need to use some services again and again. So for this we have beforeEach() with us.
Also we would like to inject a reference once, in a beforeEach() block and reuse this in multiple it() clauses. For this we can always assign a reference to a variable that is declared in the scope of the describe() block. So when defining this variable we most of the time tend to use the same name as the service name. For an instance if we have a service called itemService we will just declare it in the describe block and when it comes to get it injected to the code we have inject() function. So the injected parameters can be enclosed with underscores. These are ignored by the injector when the reference name is resolved. For example we have the parameter _itemService_ which would be resolved as the reference itemService . Since it is available in the function body as _itemService_, we can then assign it to a variable defined in an outer scope. So then we can use our itemService reference from all the test cases we have.
Now consider the below much simplified snippet where we populate the item tag name in one of our controller.
So in the above code snippet it uses a TranslationService as said before. So we do not need to test that but mock it . So we can use the same beforeEach() as earlier and create a mock for the above.
Now see that we are using a mocked snippet above in the BeforeEach, so when you call getMessageLabel(), it returns a constant text message. Now we can use the mocked service in our test cases as below.
See above when making the controller we are injecting the mocked TranslationService reference for the controller. itemService is also injected here , the one we loaded at the beforeEach.
Now note that whenever you call the TranslationService ‘s getMessageLabel() it is supposed to return “All Sections” as the text label. So it is a mocked reply for us. So this is a simple way of mocking required AngularJs services in Test coding. Likewise you can mock any number of services in your actual implementation and inject them to be used in the Test code.