[[oktatas:web:angular|< Angular]] ====== Angular tesztelés ====== * **Szerző:** Sallai András * Copyright (c) Sallai András, 2021, 2022, 2023 * Licenc: [[https://creativecommons.org/licenses/by-sa/4.0/|CC Attribution-Share Alike 4.0 International]] * Web: https://szit.hu ===== Jasmine ===== ==== Fontosabb Jasmine eszközök ==== * describe - Egy blokk definiálása * Minden egyedi tesztet egy ilyen blokkba kerül. * Egyedi teszt, angolul individual test, röviden it. * beforeEach * Minden egyes teszt előtt lefut. * Ha 5 teszt van 5-ször fut le. * A teszt beállításait tartalmazza. * afterEach * Minden egyes teszt után lefut. * Leépítési funkciók. * beforeAll * Az összes teszt előtt egyszer fut le. * afterAll * Az összes teszt után egyszer fut le. Figyelmeztetések * toBeDefined * toBeTruthy * toContain * toEqual * toThrow * toBeNull Példa: expect(myData).toBeGreaterThan(4); Az állítás megfordítása: expect(myData).not.toBeGreaterThan(4); Készíthetünk saját illeszkedést is. ==== TestBed ==== A TestBed **Angular** teszteléshez elérhető eszköz. import { TestBed } from '@angular/core/testing'; ==== By ==== A By segítségével elérhetők a HTML oldal elemei. import { By } from '@angular/platform-browser'; ===== Beállítás ===== ==== Véletlen végrehajtás ==== A tesztek alapértelmezetten véletlen sorrendben futnak le. Ezt állíthatjuk a **karma.conf.js** fájl jasmine részben: module.exports = function (config) { config.set({ client: { jasmine: { random: false }, }, }); }; ==== Több információ ==== * https://jasmine.github.io/api/edge/Configuration.html (2022) ===== Kezdeti teszt ===== 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!'); }); }); ===== Kiegészítve ===== Egy beforeAll() függvényt teszünk 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!'); }); }); ===== Teszt indítása ===== 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. ===== Teszt kiegészítése ===== 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); }) } ===== Kiegészítő osztályhoz tesztírás ===== 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); }); }); ===== Futó alkalmazás ===== Az alapértelmezetten létrejött utolsó, "should render title" nevű teszt megkövetel egy HTML tartalmat:
{{ title }} app is running!
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. ===== FormsModule ===== 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(); }); ===== HTML elemek tesztje ===== 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); }); }); ==== Osztály vizsgálata ==== 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); }); ==== Tartalom ==== 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 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'); }); ===== HttpClien tesztje ===== 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(); }); }); ===== Függelék ===== ==== Példa ==== import { ComponentFixture, TestBed } from '@angular/core/testing'; import { AppComponent } from './app.component'; import { By } from '@angular/platform-browser'; describe('AppComponent', () => { let fixture : ComponentFixture; 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 elem tartalma', () => { fixture.detectChanges(); const aElem = fixture.debugElement.queryAll(By.css('.nav-item')); expect(aElem[1].nativeElement.textContent.trim()).toBe('Valami'); }); }); ===== Gyermek komponens ===== Angular gyermek komponens tesztje: it('app-vehicle meg van', () => { fixture.detectChanges(); const childElem = fixture.debugElement.queryAll(By.css('app-vehicles')); expect(childElem).toBeTruthy(); }); ===== Linkek ===== * https://angular.io/guide/testing-components-basics (2022) * https://angular.io/guide/testing-services (2022) * https://testing-angular.com/ (2022)