Angular CLI автоматически настраивает Karma и Jasmine при создании проекта. Конфигурация находится в:
karma.conf.js
- настройки Karma (браузеры, reporters)test.ts
- точка входа для тестовangular.json
- раздел test
с конфигурациейimport { ComponentFixture, TestBed } from '@angular/core/testing';
import { MyComponent } from './my.component';
describe('MyComponent', () => {
let component: MyComponent;
let fixture: ComponentFixture<MyComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [MyComponent]
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(MyComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
it('should accept input', () => {
component.inputValue = 'test';
fixture.detectChanges();
expect(fixture.nativeElement.textContent).toContain('test');
});
it('should emit output', () => {
spyOn(component.outputEvent, 'emit');
component.triggerOutput();
expect(component.outputEvent.emit).toHaveBeenCalledWith('expected value');
});
it('should render title', () => {
component.title = 'Test Title';
fixture.detectChanges();
const el = fixture.nativeElement.querySelector('h1');
expect(el.textContent).toContain('Test Title');
});
it('should have disabled button when isLoading is true', () => {
component.isLoading = true;
fixture.detectChanges();
const button = fixture.nativeElement.querySelector('button');
expect(button.disabled).toBeTrue();
});
let mockService = jasmine.createSpyObj('DataService', ['fetchData']);
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
{ provide: DataService, useValue: mockService }
]
});
});
it('should call service method', () => {
mockService.fetchData.and.returnValue(of([]));
component.loadData();
expect(mockService.fetchData).toHaveBeenCalled();
});
it('should show data after async operation', fakeAsync(() => {
let service = TestBed.inject(DataService);
spyOn(service, 'getData').and.returnValue(
of({ data: 'test' }).pipe(delay(1000))
);
component.loadData();
tick(1000);
fixture.detectChanges();
expect(fixture.nativeElement.textContent).toContain('test');
}));
import { RouterTestingModule } from '@angular/router/testing';
beforeEach(() => {
TestBed.configureTestingModule({
imports: [RouterTestingModule]
});
});
it('should navigate on click', () => {
const router = TestBed.inject(Router);
spyOn(router, 'navigate');
const link = fixture.nativeElement.querySelector('a');
link.click();
expect(router.navigate).toHaveBeenCalledWith(['/expected-route']);
});
it('should update form control value', () => {
const input = fixture.nativeElement.querySelector('input');
input.value = 'new value';
input.dispatchEvent(new Event('input'));
fixture.detectChanges();
expect(component.form.value.name).toBe('new value');
});
it('should validate required field', () => {
const control = component.form.get('name');
control.setValue('');
expect(control.valid).toBeFalse();
});
@Component({ template: `<app-test [input]="value"></app-test>` })
class HostComponent {
value = 'initial';
}
describe('TestComponent with host', () => {
let hostFixture: ComponentFixture<HostComponent>;
beforeEach(() => {
hostFixture = TestBed.createComponent(HostComponent);
hostFixture.detectChanges();
});
it('should react to input changes', () => {
hostFixture.componentInstance.value = 'changed';
hostFixture.detectChanges();
expect(hostFixture.nativeElement.textContent).toContain('changed');
});
});
class Page {
get buttons() {
return this.fixture.nativeElement.querySelectorAll('button');
}
constructor(private fixture: ComponentFixture<TestComponent>) {}
}
it('should show error when service fails', () => {
mockService.fetchData.and.returnValue(throwError('error'));
component.loadData();
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('.error')).not.toBeNull();
});
Избегайте избыточных тестов - тестируйте поведение, а не реализацию
Используйте Angular Testing Utilities:
async/fakeAsync
для асинхронных операцийComponentFixtureAutoDetect
для автоматического обнаружения измененийBy.css
для поиска элементовТестирование Angular-компонентов с Jasmine/Karma требует понимания как работы Angular, так и принципов модульного тестирования. Начинайте с простых тестов на создание компонента, постепенно переходя к более сложным сценариям с моками зависимостей, асинхронными операциями и проверкой шаблонов. Правильно написанные тесты становятся надежной защитой от регрессий и документацией к вашему коду.