Helper functions to help write unit tests in Angular using mocks and spies
createComponentMockhas been removed, in favor ofng-mocks. See below for more info.
Minimal feature set to bridge obvious gaps in jasmine's support of modern JavaScript features. Ideally, I'd like to be using an auto-mocking library, but they don't play well with Angular's TestBed and RxJS\BehaviorSubject.
Here's a great article about auto-mocking libraries: https://hackernoon.com/with-typescript-3-and-substitute-js-you-are-already-missing-out-when-mocking-or-faking-a3b3240c4607.
Add the package to your Angular project with npm:
npm i -D angular-unit-test-helperCheck 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.
Usage
npm i -D ng-tester
npx ng g ng-tester:unitFor more information see https://github.com/bjsawyer/ng-tester/.
autoSpyObj(classUnderTest: Function, spyProperties: string[] = [], observableStrategy = ObservablePropertyStrategy.Observable)
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 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.
Usage
const weatherServiceSpy = autoSpyObj(WeatherService)Alternate Usage
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.
Usage
weatherServiceMock = jasmine.createSpyObj('WeatherService', ['getCurrentWeather'])
addPropertyAsBehaviorSubject(weatherServiceMock, 'currentWeather', null)
...
spyOnProperty(weatherServiceMock, 'currentWeather$').and.returnValue({ temp = 72})Convenience method to configure a property as a BehaviorSubject, so you can update its value before each test by calling .next on it.
Usage
weatherServiceMock = jasmine.createSpyObj('WeatherService', ['getCurrentWeather'])
addPropertyAsBehaviorSubject(weatherServiceMock, 'currentWeather$')
...
weatherServiceMock.currentWeather$.next(fakeWeather)Deprecated Use ng-mocks instead. https://ng-mocks.sudo.eu/api/MockComponent.
- Install using
install i -D ng-mocks - Refactor
createComponentMockfunction calls to look like the sample below:
TestBed.configureTestingModule({
declarations: [
// for a single component
MockComponent(Component),
// for a set of components
...MockComponents(Component1, Component2),
],
})Creates a mock class decorated with @Component, if not specified selector is inferred to be MyClassComponent -> app. Provides an option to override empty template.
Usage
TestBed.configureTestingModule({
declarations: [ ..., createComponentMock('CurrentWeatherComponent')]
...
})Note: Inferred selector in the above example is 'app-current-weather'.
Replaces boilerplate
@Component({
selector: 'app-current-weather',
template: '',
})
class MockCurrentWeatherComponent {}Helper function to retrieve the native element associated with a specific `data-testid`` within a component fixture.
Usage
<span class="left-pad" data-testid="title">LemonMart</span>it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent)
fixture.detectChanges()
const titleElement = getNativeElementByTestId(fixture, 'title')
expect(titleElement.textContent).toContain('LemonMart')
})Helper function to inject a dependency, like a service, into the TestBed with a typed return variable.
Usage
beforeEach(() => {
weatherService = injectClass(WeatherService)
})Replaces
beforeEach(() => {
weatherService = TestBed.inject(WeatherService)
})injectSpy<TDependency>(dependency: Type<TDependency> | AbstractType<TDependency>): jasmine.SpyObj<TDependency>
Similar to injectClass, but more descriptive to read for developers if returning a mocked SpyObj.
Usage
beforeEach(() => {
weatherServiceMock = injectSpy(WeatherService)
})Replaces
beforeEach(() => {
weatherServiceMock = TestBed.inject(WeatherService) as any
})Helper function that return all functions in a given Class using reflection, so you don't have to provide an instance of the object.
Helper function that return all property getters in a given Class using reflection, so you don't have to provide an instance of the object.
- Send PR, will accept
- To setup the project, run
npm install - Test against the example project listed below using
npm packto create a.tgzfile and install the.tgzfile usingnpm install -D ../path/to/your.tgz- Using
npm linkdoesn't work as expected duedevDependenciesbeing symlinked to the parent Angular project, causing issues with the framework.
- Using
- To publish the project, run
npm version major|minor|patchRead more about that setup by Isaac Schlueter here