angular-testing

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Angular 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
本部分记录了近期对应版本的专属API变动。
  • 新增:Vitest作为默认测试工具 — Angular 21+版本使用Vitest替代Jasmine/Karma 来源
  • 新增:模板覆盖率 — 覆盖率报告现在包含HTML模板 来源
  • 新增:组件输入测试 — 可直接使用
    Component输入
    测试组件输入
  • 新增:面向测试的功能型拦截器 — 更易于测试HTTP拦截器
  • 已弃用:Karma测试运行器 — 现代化Angular项目请迁移至Vitest

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
    fakeAsync
    and
    tick
    for async operations
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
    by.css
    for querying DOM elements
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
    provideHttpClient
    for HTTP testing
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
    detectChanges
    appropriately
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);
});
  • 异步操作使用
    fakeAsync
    tick
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();
});
  • 使用
    by.css
    查询DOM元素
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();