Since its inception it’s always been one of the eminent design goals of Angular (and AngularJS before it) to make JavaScript applications easily testable and bake unit and E2E testing right into the development process.
While the Angular team certainly has succeeded at this there’s still the occasional snag you hit when writing unit or end-to-end tests. For the most part, the testing experience with Angular is terrific and testing JavaScript codebases has come a long way but of course there’s always room for improvement, especially in terms of test maintainability.
Tests should be considered first-class citizens and your test code should be as well-maintained and as regularly refactored as your production code.
In that respect, a particular pet peeve of mine has been that when setting up the dependencies (or mocks thereof) of the system under test in Jasmine unit tests / specs enumerating dependencies such as providers or imported modules can become tedious and repetitive, which makes the resulting test code inherently less maintainable:
1 2 3 4 5 6 7 8 9 10 11 12 13 | beforeEach(() => { TestBed.configureTestingModule({ declarations: [ ... ], imports: [ ... ], providers: [ ... ] }); }); |
If for example you use a particular component or service in many of your own components you’ll end up listing the same dependencies over and over again in your components’ respective unit tests.
In order to alleviate this I came up with an approach (thanks to Martin, who pointed me in that direction) that allows you to define a component’s required dependencies in a function and share that function with unit tests that transitively depend on those as well because the system under test makes use of that component.
In practice, this means that when configuring TestBed by running TestBed.configureTestingModule I don’t pass an object created ad-hoc but a function that returns an object of type TestModuleMetadata. This function in turn both defines the component’s own dependencies in the familiar way as well as merging those with its transitive dependencies (i.e. dependencies required because the component itself depends on other components).
For merging both local and transitive dependencies I created a function called compileTestModuleMetadata that’s exported globally in the application’s app.module.ts
You can find example code for how to implement this yourself in this Gist.
If you have any questions about this approach or any software or business matters (Angular-related or otherwise) please don’t hesitate to get in touch.