Az Angular a Jasmine tesztelő rendszert használja, és Karma tesztfuttatót használja.
Ha kapcsolók nélkül létrehozunk egy Angular projektet, azonnal kapunk hozzá tesztet is.
ng new app01 cd app01
A teszt futtatása:
ng test
A Jasmine teszteket a describe() függvény paramétereként írjuk. Egy teszt egy it() függvény paraméterében van leírva. A tesztek írást segítik a beforeEach, afterEach, beforeAll, afterAll horgok.
A tesztekben figyelmeztetéseket generálunk az elvárt értékek és viselkedések alapján. Ha nem az elvárt értéket vagy viselkedést kapjuk figyelmeztetést küldünk. A figyelmeztetéseket az expect() függvénnyel vezetjük be, amin futtatunk egy konkrét figyelmeztetést.
Figyelmeztetések, illesztések:
A következő példában azt várom, hogy a myData változó értéke nagyobb mint 4:
expect(myData).toBeGreaterThan(4);
Az állítás megfordítása egy not taggal:
expect(myData).not.toBeGreaterThan(4);
Készíthetünk saját illeszkedést is.
Az összes egyezés:
A TestBed Angular teszteléshez elérhető eszköz. 1)
import { TestBed } from '@angular/core/testing';
A By segítségével elérhetők a HTML oldal elemei.
import { By } from '@angular/platform-browser';
Az újabb Angular verziók ng parancsa nem hozza létre a karma.conf.js állományt, a beállítások memóriában vannak.
Ha saját konfigurációt szeretnénk, a konfigurációs fájl létrehozása és bejegyzése:
ng generate config karma
Az nem elég, ha létrehozzuk és írunk bele. Szükség van egy bejegyzésre az angular.json konfigurációs fájlban.
A tesztek véletlenszerűen vagy sorrendbe fussanak:
client: { jasmine: { random: false }, },
Milyen böngészőben fusson:
browsers: ['Chromium'],
A Chorome böngésző úgy veszi, hogy még sosem volt elindítva, ezért mindig megkérdezi, hogy milyen keresőt választunk. Ebből a szempontból jobb a Chromium.
Ha kapcsolók nélkül létrehozunk egy projektet az app.component számára létre jön egy alapértelmezett teszt is.
import { TestBed } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; import { AppComponent } from './app.component'; describe('AppComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ imports: [ RouterTestingModule ], declarations: [ AppComponent ], }).compileComponents(); }); it('should create the app', () => { const fixture = TestBed.createComponent(AppComponent); const app = fixture.componentInstance; expect(app).toBeTruthy(); }); it(`should have as title 'app08'`, () => { const fixture = TestBed.createComponent(AppComponent); const app = fixture.componentInstance; expect(app.title).toEqual('app08'); }); it('should render title', () => { const fixture = TestBed.createComponent(AppComponent); fixture.detectChanges(); const compiled = fixture.nativeElement as HTMLElement; expect(compiled.querySelector('.content span')?.textContent).toContain('app08 app is running!'); }); });
Kezdetnek tegyünk egy beforeAll() függvényt a tesztek elejére.
import { TestBed } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; import { AppComponent } from './app.component'; describe('AppComponent', () => { beforeAll(()=>{ console.log('Egyszer lefut') }) beforeEach(async () => { await TestBed.configureTestingModule({ imports: [ RouterTestingModule ], declarations: [ AppComponent ], }).compileComponents(); }); it('should create the app', () => { const fixture = TestBed.createComponent(AppComponent); const app = fixture.componentInstance; expect(app).toBeTruthy(); }); it(`should have as title 'app08'`, () => { const fixture = TestBed.createComponent(AppComponent); const app = fixture.componentInstance; expect(app.title).toEqual('app08'); }); it('should render title', () => { const fixture = TestBed.createComponent(AppComponent); fixture.detectChanges(); const compiled = fixture.nativeElement as HTMLElement; expect(compiled.querySelector('.content span')?.textContent).toContain('app08 app is running!'); }); });
ng test
A tesztek alapértelmezetten véletlenszerű sorrendben hajtódnak végre. A teszt egy új böngészőablakban fut, és az eredményt is látjuk.
Írjunk egy metódust az app.component.ts fájlban:
calcArea(base: number, height: number) { return (base * height) / 2; }
Egy negyedik tesztet adunk a meglévő tesztekhez:
describe('AppComponent', () => { //... it('Háromszög tesztelése', () => { const fixture = TestBed.createComponent(AppComponent); const app = fixture.componentInstance; expect(app.calcArea(30, 35)).toEqual(525); }) }
Ha készítünk egy triangle.ts fájlt a Triangle osztály számára, készíthetünk hozzá tesztállományokat is. Követelmény a .spec.ts kiterjesztés.
export class Triangle { base: number; height: number; area: number; constructor() { this.base = 0; this.height = 0; this.area = 0; } calcArea() { this.area = this.base * this.height / 2; } }
import { Triangle } from './triangle'; describe('Triangle osztály tesztje', () => { let triangle: Triangle; beforeEach(() => { triangle = new Triangle(); }); it('Területszámítás', () => { triangle.base = 30; triangle.height = 35; triangle.calcArea(); let actual = triangle.area; let expected = 525; expect(expected).toEqual(actual); }); });
Az alapértelmezetten létrejött utolsó, „should render title” nevű teszt megkövetel egy HTML tartalmat:
<div class="content"><span>{{ title }} app is running!</span></div>
Az app/app.component.html fájlt tartalmát törölni szoktuk, mivel saját tartalommal töltjük azt meg. A „should render title” teszt ezért hibára fut.
Ha az app.module.ts fájlban importáljuk a FormsModule modult, és input elemeket hozunk létre az app.component.html fájlban akkor az importálást a tesztfájlban is meg kell csinálni:
import { TestBed } from '@angular/core/testing'; import { AppComponent } from './app.component'; import { FormsModule } from '@angular/forms'; describe('AppComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [ AppComponent ], imports: [ FormsModule ], }).compileComponents(); }); it('should create the app', () => { const fixture = TestBed.createComponent(AppComponent); const app = fixture.componentInstance; expect(app).toBeTruthy(); }); it(`should have as title 'triangle'`, () => { const fixture = TestBed.createComponent(AppComponent); const app = fixture.componentInstance; expect(app.title).toEqual('triangle'); }); it('should render title', () => { const fixture = TestBed.createComponent(AppComponent); fixture.detectChanges(); const compiled = fixture.nativeElement as HTMLElement; expect(compiled.querySelector('.content span')?.textContent).toContain('triangle app is running!'); }); });
A HttpClient teszthez:
beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [ TasksComponent ], imports: [HttpClientModule] }) .compileComponents(); });
<nav> <div></div> <div></div> </nav>
import { TestBed } from '@angular/core/testing'; import { AppComponent } from './app.component'; import { By } from '@angular/platform-browser'; describe('AppComponent', () => { it('létrehozható az app objektum', () => { const fixture = TestBed.createComponent(AppComponent); const app = fixture.componentInstance; expect(app).toBeTruthy(); }); it('van nav elem', () => { const fixture = TestBed.createComponent(AppComponent); const nav = fixture.debugElement.queryAll(By.css('nav')); expect(nav.length).toBe(1); }); it('van nav elemben két div elem', () => { const fixture = TestBed.createComponent(AppComponent); const nav_div = fixture.debugElement.queryAll(By.css('nav div')) expect(nav_div.length).toBe(2); }); });
it('a nav elemnek van navbar osztálya', () => { const fixture = TestBed.createComponent(AppComponent); const navbar = fixture.debugElement.queryAll(By.css('.navbar')); expect(navbar.length).toBe(1); });
A query() függvény egyetlen elemmel tér vissza. A queryAll() függvény az összes elemmel tér vissza, tömbként.
it('második <a> elem tartalma', () => { const fixture = TestBed.createComponent(AppComponent); fixture.detectChanges(); const aElem = fixture.debugElement.queryAll(By.css('.nav-item')); expect(aElem[1].nativeElement.textContent.trim()).toBe('Features'); });
Legyen a példa kedvért egy ApiService osztály, ami HttpClientModule-t igényli. A kezdeti tesztfájl így néz ki:
import { TestBed } from '@angular/core/testing'; import { ApiService } from './api.service'; describe('ApiService', () => { let service: ApiService; beforeEach(() => { TestBed.configureTestingModule({}); service = TestBed.inject(ApiService); }); it('should be created', () => { expect(service).toBeTruthy(); }); });
A HttpClienModule-t külön be kell itt is tölteni.
import { HttpClientModule } from '@angular/common/http'; import { TestBed } from '@angular/core/testing'; import { ApiService } from './api.service'; describe('ApiService', () => { let service: ApiService; beforeEach(() => { TestBed.configureTestingModule({ imports: [ HttpClientModule, ] }); service = TestBed.inject(ApiService); }); it('should be created', () => { expect(service).toBeTruthy(); }); });
<nav> <div></div> <div> <ul> <li><a class="nav-link">Valami</a></li> </ul> </div> </nav>
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { AppComponent } from './app.component'; import { By } from '@angular/platform-browser'; describe('AppComponent', () => { let fixture : ComponentFixture<AppComponent>; let app: AppComponent; beforeEach(() => { fixture = TestBed.createComponent(AppComponent); app = fixture.componentInstance; }); it('létrehozható az app objektum', () => { expect(app).toBeTruthy(); }); it('van nav elem', () => { const nav = fixture.debugElement.queryAll(By.css('nav')); expect(nav.length).toBe(1); }); it('van nav elemben két div elem', () => { const nav_div = fixture.debugElement.queryAll(By.css('nav div')) expect(nav_div.length).toBe(2); }); it('a nav elemnek van navbar osztálya', () => { const navbar = fixture.debugElement.queryAll(By.css('.navbar')); expect(navbar.length).toBe(1); }); it('második <a> elem tartalma', () => { fixture.detectChanges(); const aElem = fixture.debugElement.queryAll(By.css('.nav-item')); expect(aElem[1].nativeElement.textContent.trim()).toBe('Valami'); }); });
Angular gyermek komponens tesztje:
it('app-vehicle meg van', () => { fixture.detectChanges(); const childElem = fixture.debugElement.queryAll(By.css('app-vehicles')); expect(childElem).toBeTruthy(); });