angular-testing
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAngular Testing
Angular 测试
Version: Angular 21 (2025)
Tags: Testing, Jasmine, Karma, Vitest, Cypress, Jest
版本: Angular 21 (2025)
标签: 测试, Jasmine, Karma, Vitest, Cypress, Jest
API Changes
API变更
This section documents recent version-specific API changes.
-
NEW: Vitest as default — Angular 21+ uses Vitest instead of Jasmine/Karma source
-
NEW: Template coverage — Coverage reports now include HTML templates source
-
NEW: Component input testing — Test component inputs directly with
Component输入 -
NEW: Functional interceptors for testing — Easier to test HTTP interceptors
-
DEPRECATED: Karma test runner — Migrate to Vitest for modern Angular
Best Practices
最佳实践
- Use TestBed for component testing
ts
import { TestBed } from '@angular/core/testing';
import { MyComponent } from './my.component';
describe('MyComponent', () => {
let component: MyComponent;
let fixture: ComponentFixture<MyComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [MyComponent]
}).compileComponents();
fixture = TestBed.createComponent(MyComponent);
component = fixture.componentInstance;
});
it('should create', () => {
expect(component).toBeTruthy();
});
});- Use AAA pattern (Arrange-Act-Assert)
ts
it('should calculate total correctly', () => {
// Arrange
const service = new CalculatorService();
// Act
const result = service.add(2, 3);
// Assert
expect(result).toBe(5);
});- Use and
fakeAsyncfor async operationstick
ts
import { fakeAsync, tick } from '@angular/core/testing';
it('should fetch data after delay', fakeAsync(() => {
service.getData();
tick(1000);
expect(component.data).toBeTruthy();
}));- Use spies for mocking
ts
it('should call service', () => {
spyOn(service, 'getData').and.returnValue(of({ name: 'Test' }));
component.loadData();
expect(service.getData).toHaveBeenCalled();
});- Use for querying DOM elements
by.css
ts
import { By } from '@angular/platform-browser';
it('should display title', () => {
fixture.detectChanges();
const el = fixture.debugElement.query(By.css('.title'));
expect(el.nativeElement.textContent).toBe('Hello');
});- Test behavior, not implementation
ts
// ❌ Bad - tests implementation details
expect(component['privateMethod']).toHaveBeenCalled();
// ✅ Good - tests behavior
expect(fixture.nativeElement.querySelector('.result')).toContain('expected');- Use for HTTP testing
provideHttpClient
ts
TestBed.configureTestingModule({
providers: [
provideHttpClient(withInterceptors([authInterceptor]))
]
});- Use mock services for dependencies
ts
const mockAuthService = {
isAuthenticated: jasmine.createSpy().and.returnValue(true),
getToken: jasmine.createSpy().and.returnValue('fake-token')
};
TestBed.configureTestingModule({
providers: [{ provide: AuthService, useValue: mockAuthService }]
});- Test form validation
ts
it('should show error for invalid email', () => {
component.form.controls.email.setValue('invalid');
component.form.controls.email.markAsTouched();
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('.error')).toBeTruthy();
});- Use appropriately
detectChanges
ts
// After changing component properties
component.value = 'new value';
fixture.detectChanges();
// For async operations
fixture.detectChanges();
await fixture.whenStable();
fixture.detectChanges();- 组件测试使用TestBed
ts
import { TestBed } from '@angular/core/testing';
import { MyComponent } from './my.component';
describe('MyComponent', () => {
let component: MyComponent;
let fixture: ComponentFixture<MyComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [MyComponent]
}).compileComponents();
fixture = TestBed.createComponent(MyComponent);
component = fixture.componentInstance;
});
it('should create', () => {
expect(component).toBeTruthy();
});
});- 使用AAA模式(Arrange-Act-Assert / 安排-执行-断言)
ts
it('should calculate total correctly', () => {
// 准备阶段
const service = new CalculatorService();
// 执行阶段
const result = service.add(2, 3);
// 断言阶段
expect(result).toBe(5);
});- 异步操作使用和
fakeAsynctick
ts
import { fakeAsync, tick } from '@angular/core/testing';
it('should fetch data after delay', fakeAsync(() => {
service.getData();
tick(1000);
expect(component.data).toBeTruthy();
}));- 使用spies实现依赖模拟
ts
it('should call service', () => {
spyOn(service, 'getData').and.returnValue(of({ name: 'Test' }));
component.loadData();
expect(service.getData).toHaveBeenCalled();
});- 使用查询DOM元素
by.css
ts
import { By } from '@angular/platform-browser';
it('should display title', () => {
fixture.detectChanges();
const el = fixture.debugElement.query(By.css('.title'));
expect(el.nativeElement.textContent).toBe('Hello');
});- 测试行为而非实现逻辑
ts
// ❌ 错误写法 - 测试了实现细节
expect(component['privateMethod']).toHaveBeenCalled();
// ✅ 正确写法 - 测试了实际行为
expect(fixture.nativeElement.querySelector('.result')).toContain('expected');- HTTP测试使用
provideHttpClient
ts
TestBed.configureTestingModule({
providers: [
provideHttpClient(withInterceptors([authInterceptor]))
]
});- 依赖项使用mock服务
ts
const mockAuthService = {
isAuthenticated: jasmine.createSpy().and.returnValue(true),
getToken: jasmine.createSpy().and.returnValue('fake-token')
};
TestBed.configureTestingModule({
providers: [{ provide: AuthService, useValue: mockAuthService }]
});- 测试表单校验
ts
it('should show error for invalid email', () => {
component.form.controls.email.setValue('invalid');
component.form.controls.email.markAsTouched();
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('.error')).toBeTruthy();
});- 合理使用
detectChanges
ts
// 修改组件属性后
component.value = 'new value';
fixture.detectChanges();
// 针对异步操作
fixture.detectChanges();
await fixture.whenStable();
fixture.detectChanges();