KEYWORDS:
- describe: optional, describe the entire unit testing class
- it: must include it when writing isolated unit test
- async: tell the test framework to wait until the return promise or observable is completed before treating the test as completed. when all async operations are completed the next test (if exists) is able to start executing
- beforeEach: for creating new instances for another test to start from the beginning
- TestBed: creates components instance, configure module, simulate module
- Access field/variable: fixture.componentInstance.some_variable_name OR if already instantiated then component.some_variable_name
- detectChanges: only triggered when an property change occurs inside a .ts file
- configureTestingModule: configures a “fake” module
- toBe vs toEqual: toBe object(general) equality vs deep equality
- fixture.whenStable().then(() => {}); executes after all async operations are executed/finished
- tick() simulates the passage of time until all async operations above are finished. Must go with fakeAsync keyword, otherwise error will appear
- fixture.whenStable() vs tick();
They both do the same. tick() is more prudent to use because you have to give it (in the header of the test) a fakeAsync which means that everything is called synchronously inside the test. The code after the tick() method indicates that all code above has executed (async operations above) and that you can now proceed with testing the result of these above async operations.
Fixture.whenStable() however can give a false positive if async() is omitted in the header of the test. Therefore the test will complete before fixture.whenStable executions.
- A Spy is a feature of Jasmine which lets you take an existing class, function, object and mock it in such a way that you can control what gets returned from functions.
Configuring Test Module
...
innerHTML
let fixture= TestBed.createComponent(AppComponent);
let element1= fixture.debugElement.query(By.css(".heading")).nativeElement.textContent; //must include the point prefix (.)
expect(element1).toContain("come")
});
OR
it ("test input innerHTML example", async()=>{
let fixture= TestBed.createComponent(AppComponent);
let element2= fixture.debugElement.nativeElement.querySelector(".heading");
expect(element2.textContent).toContain("come");
});
input HTML tag
it ("test input input-value attribute example", async()=>{
let fixture= TestBed.createComponent(AppComponent);
let element1= fixture.debugElement.nativeElement.querySelector("input");
expect(element1.getAttribute("value")).toContain("som");
});
CSS style
it ("test input input-value attribute example", async()=>{
let fixture= TestBed.createComponent(AppComponent);
let element1= fixture.debugElement.nativeElement.querySelector("input");
expect(element1.getAttribute("value")).toContain("som");
});
it ("test css style", async()=>{
let fixture= TestBed.createComponent(AppComponent);
let element1= fixture.debugElement.nativeElement.querySelector("h1");
expect(element1.style.backgroundColor).toBe("blue"); // style attribute is the key for testing css
});
Class property testing
import { TestBed, async } from '@angular/core/testing';
import { AppComponent } from './app.component';
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [AppComponent]
});
});
it('check title class property', async()=> {
let fixture= TestBed.createComponent(AppComponent);
expect(fixture.componentInstance.title).toBeDefined()
});
Service testing
import { TestBed, async, inject } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { AppModule } from './app.module';
import { AppComponent } from './app.component'
import { MyServiceService } from './my-service.service'
beforeEach(() => {
let userServiceStub = {
isLoggedIn: true,
user: { name: 'Test User'}
};
//simulate module
TestBed.configureTestingModule({
declarations: [ AppComponent],
providers: [ {provide: MyServiceService, useValue: userServiceStub } ]
});
});
it('should test service object property value', inject([MyServiceService], (userService) => {
let fixture=TestBed.createComponent(AppComponent);
fixture.detectChanges();
expect(userService.user.name).toContain('se');
}));
Pipe testing
import { TestBed, async } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { AppModule } from './app.module';
import { AppComponent } from './app.component'
import { TryPipe} from './try.pipe'
it ("should check pipe content", async()=>{
let piping= new TryPipe(); //create an instance of the pipe
expect(piping.transform("")).toEqual("something"); //transform returns something initially
});
Directive testing
import { TestBed, async } from '@angular/core/testing';
import { AppComponent } from './app.component';
import { By } from '@angular/platform-browser';
import { FirstOneDirective } from './first-one.directive'
import { NO_ERRORS_SCHEMA } from '@angular/core';
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [ AppComponent, FirstOneDirective ],
schemas: [ NO_ERRORS_SCHEMA ]
}).createComponent(AppComponent);
});
it('should have skyblue css background, set from the directive', async()=> {
//checking the code from the directive implementation
const fixture=TestBed.createComponent(AppComponent);
const de = fixture.debugElement.query(By.css('.nesto'));
const bgColor = de.nativeElement.style.backgroundColor;
console.log(de.nativeElement.textContent);
expect(bgColor).toBe('cyan');
});
([ngModel]) testing
it('should pass twice, passes but because of two way data binding ngModel', async(() => {
const fixture1 = TestBed.createComponent(ChangeRestApiComponent);
fixture1.detectChanges();
fixture1.whenStable().then(() => {
let input_val = fixture1.debugElement.query(By.css('#first')).nativeElement;
expect(input_val.getAttribute('value')).toBeNull();
input_val.value = 'localhost';
fixture1.detectChanges();
input_val.dispatchEvent(new Event('input'));
expect(fixture1.componentInstance.tempUrl).toContain('localhost');
});
}));
Check something to be false (not to contain a substring)
it('should not contain full url paths, protocol e.g. http://', () => {
component.array.forEach((item) => {
expect(item.url.indexOf('http') === 1).toBe(false);
});
});
Checking html CHECKBOX element “checked” attribute
it('check if checkbox checked attribute changed from default false to true', async() => {
const input = fixture.debugElement.query(By.css('.className')).nativeElement;
expect(component.testing).toBe(false);
component.testing = true;
fixture.detectChanges();
expect(input.checked).toBe(true);
});
Check value after BUTTON onClick event executes
it('check if on-button (click) event changes the value in the HTML', async() => {
const div = fixture.debugElement.query(By.css('.clicking')).nativeElement;
expect(component.clickMe).toBeUndefined();
component.clickThisButton();
fixture.detectChanges();
expect(div.innerText).toEqual(component.clickMe);
});
Check new INPUT value after change event executes
it('change input value and check if change occured', async() => {
const input = fixture.debugElement.query(By.css('.inputlast')).nativeElement;
expect(input.value).toBe('undefined');
component.changeMe = 'changed';
fixture.detectChanges();
expect(input.value).toEqual('changed');
});
EventHandler(‘click’) testing
...
Routing testing
...
Local storage testing
...