Angular 测试¶
此文件使用 Angular 特定内容扩展 common/testing.md。
测试运行器¶
请使用项目中配置的测试运行器。请检查 angular.json 和 package.json。Angular 项目通常使用 Vitest、Jest 或 Jasmine + Karma。
TestBed 设置¶
对于独立组件,直接导入该组件。对于具有外部模板的组件,请调用 compileComponents()。
describe('UserCardComponent', () => {
let fixture: ComponentFixture<UserCardComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [UserCardComponent],
}).compileComponents();
fixture = TestBed.createComponent(UserCardComponent);
});
});
信号输入¶
基于信号的输入通过 fixture.componentRef.setInput() 设置:
组件 Harness¶
对于 UI 交互,请优先使用 Angular CDK 组件 Harness,而不是直接 DOM 查询。Harness 对标记变更更具容错性。
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatButtonHarness } from '@angular/material/button/testing';
let loader: HarnessLoader;
beforeEach(() => {
loader = TestbedHarnessEnvironment.loader(fixture);
});
it('triggers save on button click', async () => {
const button = await loader.getHarness(MatButtonHarness.with({ text: 'Save' }));
await button.click();
expect(saveSpy).toHaveBeenCalled();
});
路由测试¶
对于依赖路由的组件,请使用 RouterTestingHarness:
import { RouterTestingHarness } from '@angular/router/testing';
it('renders user on navigation', async () => {
const harness = await RouterTestingHarness.create();
const component = await harness.navigateByUrl('/users/1', UserDetailComponent);
expect(component.userId()).toBe('1');
});
异步测试¶
对于受控异步,请使用 fakeAsync + tick。对于真实异步,请使用 waitForAsync 和 fixture.whenStable()。
it('loads user after delay', fakeAsync(() => {
const service = TestBed.inject(UserService);
vi.spyOn(service, 'getUser').mockReturnValue(of(mockUser));
fixture.detectChanges();
tick();
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('.name').textContent).toBe(mockUser.name);
}));
HTTP 测试¶
import { provideHttpClientTesting } from '@angular/common/http/testing';
import { HttpTestingController } from '@angular/common/http/testing';
beforeEach(() => {
TestBed.configureTestingModule({
providers: [provideHttpClient(), provideHttpClientTesting()],
});
httpMock = TestBed.inject(HttpTestingController);
});
afterEach(() => httpMock.verify());
服务测试¶
在没有组件 fixture 的情况下直接注入服务:
describe('UserService', () => {
let service: UserService;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [provideHttpClient(), provideHttpClientTesting()],
});
service = TestBed.inject(UserService);
});
});
测试对象¶
- 服务:所有公共方法、错误路径、HTTP 交互
- 组件:输入/输出绑定、主要状态的渲染结果、使用 Harness 的用户交互
- 管道:纯转换 — 无需 TestBed 的普通单元测试
- 守卫/解析器:使用
RouterTestingHarness的允许和拒绝状态返回值
E2E 测试¶
对于关键用户流程,请使用项目中配置的 E2E 框架,例如 Cypress 或 Playwright。
describe('Login flow', () => {
it('redirects to dashboard on valid credentials', () => {
cy.visit('/login');
cy.get('[data-cy=email]').type('user@example.com');
cy.get('[data-cy=password]').type('password123');
cy.get('[data-cy=submit]').click();
cy.url().should('include', '/dashboard');
});
});
- 为可交互元素添加
data-cy属性,以获得稳定的选择器 - 在 E2E 测试中,选择器不要依赖 CSS 类或文本内容
覆盖率¶
服务和管道的覆盖率目标应为 80% 以上。组件:请测试行为,而不是实现细节。
技能参考¶
有关全面的测试模式、Harness 用法和异步最佳实践,请参阅技能:angular-developer。
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 ev/ECC