Angular Unit Test - Tuong-Nguyen/Angular-D3-Cometd GitHub Wiki
Continuous testing
Angular-cli application runs unit test with command npm test
This is continuous testing, ie: file update -> tests are run again.
Test file are suffix with spec.ts
and are generated automatically by Generator.
Angular Test Bed (ATB)
Configuration
import { TestBed } from '@angular/core/testing';
describe('Login component', () => {
// Configuration
beforeEach(() => TestBed.configureTestingModule({
declarations: [LoginComponent],
providers: [RaceService]
}));
});
Instantiate component & Resolve dependency
import { TestBed } from '@angular/core/testing';
describe('Component: Login', () => {
// Configure providers
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [LoginComponent],
providers: [RaceService]
}));
// create component and test fixture
fixture = TestBed.createComponent(LoginComponent);
// get test component from the fixture
component = fixture.componentInstance;
// AuthService provided to the TestBed
authService = TestBed.get(AuthService);
}
});
fixture
is a wrapper for a component and its template- component = fixture.componentInstance
- Resolve dependency with TestBed.get
Usage: - Specify mock dependencies.
Change detection
// create component and test fixture
fixture = TestBed.createComponent(LoginComponent);
// get test component from the fixture
component = fixture.componentInstance;
// query the wrapper DebugElement of <a> element
el = fixture.debugElement.query(By.css('a'));
// Update View
fixture.detectChanges();
// Get the DOM element of <a>
expect(el.nativeElement.textContent.trim()).toBe('');
}
});
- DebugElement: wrapper to the low level DOM element that represents the components view
- By: By.css, By.directive - query functions
- fixture.detectChanges: update view according to state.
- nativeElement: DOM object
Note: Life cycle hooks (ngOnInit, ...) must be called explicitly in test. Angular TestBed does not call them. The first call of fixture.detectChanges() also calls ngOnInit. Therefore, one practice is to call fixture.detectChanges right after creating the component in beforeEach.
Asynchronous testing
Async method and whenStable
it('Button label via async() and whenStable()', async(() => {
spyOn(authService, 'isAuthenticated').and.returnValue(Promise.resolve(true));
component.ngOnInit();
fixture.whenStable().then(() => {
fixture.detectChanges();
expect(el.nativeElement.textContent.trim()).toBe('Logout');
});
}));
- Mock authService.isAuthenticated: return an Promise
- whenStable() return a promis.
async use zones for keeping track all timeouts, listeners, callbacks ... It helps to not complete until all asynchronous actions are done. Only when all of those pending promises have been resolved does it then resolves the promise returned from whenStable.
fakeAsync and tick
it('Button label via fakeAsync() and tick()', fakeAsync(() => {
spyOn(authService, 'isAuthenticated').and.returnValue(Promise.resolve(true));
component.ngOnInit();
tick();
fixture.detectChanges();
expect(el.nativeElement.textContent.trim()).toBe('Logout');
}));
fakeAsync executes code in fake async test zone. tick() blocks execution and wait until asynchronous activities complete.
Note: fakeAsync has drawbacks; ex: cannot track XHR requests.
Nested component testing
Problem
Testing component depends on other components and its service that we don't want to test
Solution
- Declare what we want to test and its directly dependencies
- Tells the compiler not to error on unknown elements and attributes
TestBed.configureTestingModule({
// ...
schemas: [NO_ERRORS_SCHEMA]
})
Unit Test Guidelines
Component
@Input
- Verify basic appearance
- Test UI Logic.
@Output
- Check event is notified with correct argument after an action.