Tartalomjegyzék

< Angular

Angular tesztelés

Az Angular tesztelése

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

Jasmine

Fontosabb Jasmine eszközök

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:

TestBed

A TestBed Angular teszteléshez elérhető eszköz. 1)

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

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.

Hasznos beállítások

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.

Lásd még

Kezdeti teszt

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.

app.component.spec.ts
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

Kezdetnek tegyünk egy beforeAll() függvényt a tesztek elejére.

app.component.spec.ts
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

Írjunk egy metódust az app.component.ts fájlban:

src/app/app.component.ts
  calcArea(base: number, height: number) {
    return (base * height) / 2;
  }

Egy negyedik tesztet adunk a meglévő tesztekhez:

app.component.spec.ts
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.

triangle.ts
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;
    }
}
triangle.spec.ts
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:

<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.

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:

app.component.spec.ts
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

app.component.html
<nav>
  <div></div>
  <div></div>
</nav>
app.component.spec.ts
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 <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');    
  });

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:

src/app/shared/api.service.spec.ts
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.

src/app/shared/api.service.spec.ts
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

<nav>
   <div></div>
   <div>
     <ul>
        <li><a class="nav-link">Valami</a></li>
     </ul>
   </div>
</nav>
app.component.spec.ts
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');    
  });
 
});

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

1)
Ha Angular nélkül használjuk a Jasmin keretrendszert, a TestBed nem áll rendelkezésre.