Helper functions to help write unit tests in Angular using mocks and spies.
angular-unit-test-helper, published on npm, is a unit testing library with a minimal feature set designed to bridge gaps in jasmine’s support of modern JavaScript features.
Excellians Doguhan Uluca and Brendan Sawyer created this library to support Test Driven Development (TDD) in Angular projects. In the spirit of TDD ~99% the project itself is covered by unit tests.
TDD is a pillar of Agile Engineering and is fundamental to delivering quality code to production with speed. In unit testing, it is essential to only test a single function at a time. The use of external classes and imports in functions make it impossible to write a unit test without the use of test doubles.
Creating test doubles of all classes in a large project can quickly become very time consuming and difficult to maintain. Ideally, developers should be able to use an auto-mocking library to create mocks of complex objects however, existing libraries don’t play well with Angular’s TestBed
and RxJS\BehaviorSubject
objects.
angular-unit-test-helper covers this gap.
Add the package to your Angular project with npm:
$ npm i -D angular-unit-test-helper
The example projects and code generator below explain how you can best leverage the library.
Check out my sample projects that leverage angular-unit-test-helper
:
Use the ng-tester
package to generate robust and efficient unit tests using angular-unit-test-helper
.
$ npm i -D ng-tester $ npx ng g ng-tester:unit
For more information see https://github.com/bjsawyer/ng-tester/.
Here are the main features of the library.
An extension of jasmine.createSpyObj
with automatic discovery of functions and property getters given a Class, without requiring an instance of an object.
If you’d want to spy on a property without a getter, then you can simply pass in the property name like autoSpyObj(WeatherService, ['currentWeather$'])
.
Return value of autoSpyObj
will be a true mock of the Class with spy-able methods and properties, making it easy to control and modify the return values of external dependencies during testing.
If the property name ends with $
indicating that the property is an Observable, then you can specify an optional ObservablePropertyStrategy
to prefer {}
, new Observable()
or new BehaviorSubject(null)
default values for your mocked properties.
const weatherServiceSpy = autoSpyObj(WeatherService)
const weatherServiceSpy = autoSpyObj( WeatherService, ['currentWeather$'], ObservablePropertyStrategy.BehaviorSubject )
autoSpyObj
replaces the more verbose and difficult to maintain code, shown below:
jasmine.createSpyObj(WeatherService.name, [ 'getCurrentWeather', 'getCurrentWeatherByCoords', 'updateCurrentWeather', ]) addPropertyAsBehaviorSubject(weatherServiceSpy, 'currentWeather$')
When creating a mock object, add a property to that object with a property getter, so you can use a jasmine.spyOnProperty
.
weatherServiceMock = jasmine.createSpyObj('WeatherService', ['getCurrentWeather']) addPropertyAsBehaviorSubject(weatherServiceMock, 'currentWeather', null) ... spyOnProperty(weatherServiceMock, 'currentWeather$').and.returnValue({ temp = 72})
Convenience method to configure a property as a RxJS\BehaviorSubject
, so you can update its value before each test by calling .next
on it.
weatherServiceMock = jasmine.createSpyObj('WeatherService', ['getCurrentWeather']) addPropertyAsBehaviorSubject(weatherServiceMock, 'currentWeather$') ... weatherServiceMock.currentWeather$.next(fakeWeather)
Creates a mock class decorated with @Component
, if not specified selector
is inferred to be app-my-class
given MyClassComponent
. Provides an option to override empty template.
TestBed.configureTestingModule({ declarations: [ ..., createComponentMock('CurrentWeatherComponent')] ... })
Note: Inferred selector
in the above example is 'app-current-weather'
.
@Component({ selector: 'app-current-weather', template: '', }) class MockCurrentWeatherComponent {}
Helper function to inject a dependency, like a service, into the TestBed
with a typed return variable.
beforeEach(() => { weatherService = injectClass(WeatherService) })
beforeEach(() => { weatherService = TestBed.inject(WeatherService) })
Similar to injectClass
, but more descriptive to read for developers if returning a mocked SpyObj
.
beforeEach(() => { weatherServiceMock = injectSpy(WeatherService) })
beforeEach(() => { weatherServiceMock = TestBed.inject(WeatherService) as any })
Helper function that returns all functions in a given Class
using reflection, so you don’t have to provide an instance of the object.
Helper function that returns all property getters in a given Class
using reflection, so you don’t have to provide an instance of the object.
We welcome your issues, questions, ideas, and contributions on GitHub. We’d love for you to check out, star, or fork the project at:
https://github.com/duluca/angular-unit-test-helper
The Risks of Insecure YAML Deserialization in Python
YAML is a python favorite for object serialization, especially around application configuration. So, it is...
Agile eXamined: The Sprint Goal
Daily standups, acceptance criteria, user stories, retrospectives…am I right Agilists? In the day-to-day, it’s easy...
Contextualizing Responsible AI Practices for Fraud Detection
Developing and sharing Responsible Artificial Intelligence (AI) practices are a key element in protecting the...