Testing
Angular testing — unit (Jasmine/Karma yoki Jest), TestBed, component fixture, service mock va E2E (Cypress/Playwright). Bu guruh intervyuda amaliy test yozish va mock strategiyalarini qamrab oladi.
Unit test — Jasmine, Karma
Nima bu?
Unit test — bitta funksiya, service yoki komponentni izolyatsiya qilib tekshirish. Angular default: Jasmine (assertion + spy) va Karma (browser test runner). Yangi loyihalarda Jest yoki Vitest ham ishlatiladi. describe — test suite, it — bitta test, expect — assertion, beforeEach — har test oldidan setup.
Kod misoli
// user.utils.ts
export function formatDisplayName(first: string, last: string): string {
return `${first.trim()} ${last.trim()}`.trim() || 'Noma\'lum';
}
export function isAdult(birthYear: number, now = new Date()): boolean {
return now.getFullYear() - birthYear >= 18;
}
// user.utils.spec.ts
import { formatDisplayName, isAdult } from './user.utils';
describe('formatDisplayName', () => {
it('ism va familiyani birlashtiradi', () => {
expect(formatDisplayName('Ali', 'Valiyev')).toBe('Ali Valiyev');
});
it('bo\'sh qiymatda Noma\'lum qaytaradi', () => {
expect(formatDisplayName('', '')).toBe('Noma\'lum');
});
});
describe('isAdult', () => {
it('18 yoshdan katta bo\'lsa true', () => {
const now = new Date(2026, 0, 1);
expect(isAdult(2000, now)).toBe(true);
});
it('18 yoshdan kichik bo\'lsa false', () => {
const now = new Date(2026, 0, 1);
expect(isAdult(2015, now)).toBe(false);
});
});
# Karma orqali ishga tushirish
ng test
# Headless CI
ng test --no-watch --browsers=ChromeHeadless
Imtihonda
- Jasmine
describe/it/expectvazifasi? - Karma va Jest farqi?
- Pure function test qilish nima uchun oson?
Yodlash uchun
describe → suite; it → test; expect → assertion; spy → mock funksiya.
TestBed — Angular test modulini o'rnatish
Nima bu?
TestBed — Angular testing utility; test uchun mini Angular modul yaratadi. configureTestingModule — component, pipe, provider'larni ro'yxatga oladi. TestBed.inject() — service olish; overrideProvider — mock bilan almashtirish. Har test beforeEach da sozlanadi.
Override pitfall: TestBed.createComponent() (yoki compileComponents()) chaqirilgandan keyin modul konfiguratsiyasi muzlaydi — overrideProvider/overrideComponent shundan keyin chaqirilsa hech qanday effekt bermaydi yoki xato tashlaydi ("Cannot override... after the TestBed has been compiled"). Barcha override'lar configureTestingModule bilan bitta zanjirda, createComponentdan oldin bo'lishi kerak.
Kod misoli
import { TestBed } from '@angular/core/testing';
import { provideHttpClient } from '@angular/common/http';
import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
import { UserService } from './user.service';
describe('UserService', () => {
let service: UserService;
let httpMock: HttpTestingController;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
UserService,
provideHttpClient(),
provideHttpClientTesting(),
],
});
service = TestBed.inject(UserService);
httpMock = TestBed.inject(HttpTestingController);
});
afterEach(() => {
httpMock.verify(); // ochiq so'rov qolmaganini tekshirish
});
it('foydalanuvchilarni GET qiladi', () => {
const mockUsers = [{ id: 1, name: 'Ali' }];
service.getUsers().subscribe((users) => {
expect(users).toEqual(mockUsers);
});
const req = httpMock.expectOne('/api/users');
expect(req.request.method).toBe('GET');
req.flush(mockUsers);
});
});
// Provider override — mock service
beforeEach(() => {
TestBed.configureTestingModule({
imports: [UserListComponent],
providers: [
{ provide: UserService, useValue: { getUsers: () => of([]) } },
],
});
});
Imtihonda
TestBed.configureTestingModulevaTestBed.createComponentfarqi?HttpTestingControllernima uchun kerak?- Standalone component TestBed'da qanday import qilinadi?
Yodlash uchun
TestBed = mini Angular app; configure → inject/create.
Component testing — fixture, debugElement
Nima bu?
Component test — DOM render, binding, event va change detection tekshiriladi. TestBed.createComponent() → ComponentFixture. fixture.detectChanges() — CD ishga tushirish. fixture.debugElement — By.css, query, triggerEventHandler. nativeElement — haqiqiy DOM. ComponentFixtureAutoDetect — avtomatik CD.
Kod misoli
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { CounterComponent } from './counter.component';
describe('CounterComponent', () => {
let fixture: ComponentFixture<CounterComponent>;
let component: CounterComponent;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [CounterComponent],
}).compileComponents();
fixture = TestBed.createComponent(CounterComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('boshlang\'ich qiymatni ko\'rsatadi', () => {
const el = fixture.debugElement.query(By.css('[data-testid="count"]'));
expect(el.nativeElement.textContent).toContain('0');
});
it('increment tugmasi ishlaydi', () => {
const button = fixture.debugElement.query(By.css('[data-testid="increment"]'));
button.triggerEventHandler('click', null);
fixture.detectChanges();
expect(component.count()).toBe(1);
expect(fixture.nativeElement.textContent).toContain('1');
});
it('@Input orqali boshlang\'ich qiymat', () => {
fixture.componentRef.setInput('initial', 10);
fixture.detectChanges();
expect(fixture.nativeElement.textContent).toContain('10');
});
});
import { Component, input, signal, effect } from '@angular/core';
@Component({
selector: 'app-counter',
standalone: true,
template: `
<span data-testid="count">{{ count() }}</span>
<button data-testid="increment" type="button" (click)="increment()">+</button>
`,
})
export class CounterComponent {
initial = input(0);
count = signal(0);
constructor() {
effect(() => this.count.set(this.initial()));
}
increment(): void {
this.count.update((v) => v + 1);
}
}
fakeAsync/tick — nega real async emas? Component ichida setTimeout, debounceTime yoki Promise bo'lsa, real vaqtda kutish testni sekinlashtiradi va flaky qiladi (timing'ga bog'liq). fakeAsync() — testni virtual vaqt zonasida ishga tushiradi; tick(ms) esa vaqtni sun'iy ravishda oldinga suradi — real kutishsiz, deterministik. waitForAsync() (eski async()) esa haqiqiy vaqtda ishlaydi va faqat mikrotasklarni (Promise) avtomatik flush qiladi — timer asosidagi kodni test qilish uchun mos emas.
it('debounce qidiruv 300ms dan keyin ishga tushadi', fakeAsync(() => {
component.onSearchInput('angular');
fixture.detectChanges();
tick(299);
expect(searchServiceSpy.search).not.toHaveBeenCalled();
tick(1); // 300ms to'ldi — debounceTime endi trigger bo'ladi
expect(searchServiceSpy.search).toHaveBeenCalledWith('angular');
flush(); // qolgan barcha pending timer/macrotask'larni tozalash
}));
Imtihonda
detectChanges()qachon chaqirish kerak?debugElementvanativeElementfarqi?data-testidselector nima uchun tavsiya etiladi?fakeAsync+tick()va haqiqiyasync/awaitorasida qachon farq muhim bo'ladi (masalan,setTimeout,debounceTimebilan)?
Yodlash uchun
fixture → component + DOM; detectChanges → render; By.css → element topish.
Service mocking — jasmine.createSpyObj
Nima bu?
Service mock — haqiqiy HTTP yoki DB'siz test. jasmine.createSpyObj — metodlari kuzatiladigan fake object. provide: Service, useValue: mock — DI orqali almashtirish. of() / throwError() — Observable mock. Spy chaqirilganini toHaveBeenCalledWith bilan tekshirish.
Kod misoli
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { of, throwError } from 'rxjs';
import { ProductListComponent } from './product-list.component';
import { ProductService } from './product.service';
import { ToastService } from './toast.service';
describe('ProductListComponent', () => {
let fixture: ComponentFixture<ProductListComponent>;
let component: ProductListComponent;
let productServiceSpy: jasmine.SpyObj<ProductService>;
let toastSpy: jasmine.SpyObj<ToastService>;
beforeEach(async () => {
productServiceSpy = jasmine.createSpyObj('ProductService', ['getProducts', 'delete']);
toastSpy = jasmine.createSpyObj('ToastService', ['show']);
productServiceSpy.getProducts.and.returnValue(
of([{ id: 1, name: 'Kitob', price: 50_000 }])
);
await TestBed.configureTestingModule({
imports: [ProductListComponent],
providers: [
{ provide: ProductService, useValue: productServiceSpy },
{ provide: ToastService, useValue: toastSpy },
],
}).compileComponents();
fixture = TestBed.createComponent(ProductListComponent);
fixture.detectChanges();
});
it('mahsulotlarni yuklaydi va ko\'rsatadi', () => {
expect(productServiceSpy.getProducts).toHaveBeenCalled();
expect(fixture.nativeElement.textContent).toContain('Kitob');
});
it('o\'chirish xatosida toast ko\'rsatadi', () => {
productServiceSpy.delete.and.returnValue(
throwError(() => new Error('Server xato'))
);
component.onDelete(1);
fixture.detectChanges();
expect(toastSpy.show).toHaveBeenCalledWith('Xato yuz berdi', 'error');
});
});
Imtihonda
createSpyObjva manual{ getUsers: jasmine.createSpy() }farqi?- Observable mock uchun
and.returnValue(of(...))qachon ishlatiladi? HttpClientTestingModuleva service spy qachon tanlanadi?
Yodlash uchun
createSpyObj + useValue; Observable → of/throwError; chaqiruvni verify qiling.
E2E — Cypress, Playwright
Nima bu?
E2E (End-to-End) test — haqiqiy brauzerda butun ilova oqimini tekshiradi: login → navigatsiya → form → natija. Cypress — developer-friendly, time-travel debug. Playwright — multi-browser, parallel, Microsoft tomonidan. Angular'da ng e2e yoki alohida Cypress/Playwright loyihasi. Unit test'dan farqi — tezroq yoziladi, sekinroq ishlaydi, flaky bo'lishi mumkin.
Kod misoli
// cypress/e2e/login.cy.ts
describe('Login flow', () => {
beforeEach(() => {
cy.visit('/login');
});
it('to\'g\'ri credential bilan dashboard\'ga o\'tadi', () => {
cy.get('[data-cy=email]').type('user@example.com');
cy.get('[data-cy=password]').type('secret123');
cy.get('[data-cy=submit]').click();
cy.url().should('include', '/dashboard');
cy.get('[data-cy=welcome]').should('contain', 'Salom');
});
it('noto\'g\'ri parol xato xabar ko\'rsatadi', () => {
cy.get('[data-cy=email]').type('user@example.com');
cy.get('[data-cy=password]').type('wrong');
cy.get('[data-cy=submit]').click();
cy.get('[data-cy=error]').should('be.visible')
.and('contain', 'Login yoki parol noto\'g\'ri');
});
});
// playwright/tests/checkout.spec.ts
import { test, expect } from '@playwright/test';
test('savatdan buyurtma berish', async ({ page }) => {
await page.goto('/products');
await page.getByRole('button', { name: 'Savatga' }).first().click();
await page.getByRole('link', { name: 'Savat' }).click();
await page.getByRole('button', { name: 'Buyurtma berish' }).click();
await expect(page.getByText('Buyurtma qabul qilindi')).toBeVisible();
await expect(page).toHaveURL(/\/orders\/\d+/);
});
// Angular — stable selectorlar
@Component({
template: `
<input data-cy="email" formControlName="email" />
<button data-cy="submit" type="submit">Kirish</button>
`,
})
export class LoginComponent {}
Imtihonda
- E2E va component test qaysi holatlarda kerak?
- Cypress va Playwright afzalliklari?
- Flaky test'larni qanday kamaytirish (
data-cy, intercept)?
Yodlash uchun
E2E = butun oqim; data-cy selector; API intercept flaky'ni kamaytiradi.
