3.3测试 (Testing)


文档摘要

3.3测试 (Testing) 3.3 测试 (Testing) 在 Angular 应用中,测试是保证代码质量、可维护性和可靠性的关键环节。良好的测试策略能够帮助我们尽早发现并修复错误,减少回归,并确保应用在不断迭代更新的过程中保持稳定。Angular 社区提供了强大的测试工具和框架,使得编写各种类型的测试变得更加容易。 3.3.1 测试类型 在 Angular 应用中,常见的测试类型包括: 单元测试 (Unit Testing): 针对应用中最小的可测试单元(通常是一个函数、方法或组件)进行测试。单元测试的目标是验证每个单元的逻辑是否正确,独立于其他单元。 集成测试 (Integration Testing): 测试应用中多个单元之间的交互和协作。

3.3测试 (Testing)

3.3 测试 (Testing)

在 Angular 应用中,测试是保证代码质量、可维护性和可靠性的关键环节。良好的测试策略能够帮助我们尽早发现并修复错误,减少回归,并确保应用在不断迭代更新的过程中保持稳定。Angular 社区提供了强大的测试工具和框架,使得编写各种类型的测试变得更加容易。

3.3.1 测试类型

在 Angular 应用中,常见的测试类型包括:

  • 单元测试 (Unit Testing): 针对应用中最小的可测试单元(通常是一个函数、方法或组件)进行测试。单元测试的目标是验证每个单元的逻辑是否正确,独立于其他单元。

  • 集成测试 (Integration Testing): 测试应用中多个单元之间的交互和协作。集成测试的目标是验证不同单元是否能够正确地协同工作,例如组件之间的数据传递、服务之间的调用等。

  • 端到端测试 (End-to-End Testing, E2E): 模拟真实用户的使用场景,从用户界面的角度对整个应用进行测试。E2E 测试的目标是验证应用的整体功能是否符合预期,例如用户登录、数据提交、页面跳转等。

  • 组件测试 (Component Testing): 组件测试是一种特殊的集成测试,它专注于测试单个 Angular 组件及其模板的交互。它通常使用组件测试框架来模拟用户交互和验证组件的输出。

graph TD A[测试] --> B(单元测试); A --> C(集成测试); A --> D(端到端测试); A --> E(组件测试); B --> F{函数/方法/组件}; C --> G{多个单元交互}; D --> H{模拟用户行为}; E --> I{组件及其模板交互};

3.3.2 测试工具和框架

Angular 社区提供了丰富的测试工具和框架,其中最常用的包括:

  • Jasmine: 一个流行的 JavaScript 测试框架,提供了丰富的断言和测试结构,易于使用和学习。

  • Karma: 一个测试运行器,可以启动浏览器并执行测试代码,支持多种浏览器和测试框架。

  • Protractor: 一个专门用于 Angular 应用的 E2E 测试框架,基于 WebDriver,可以模拟用户在浏览器中的行为。

  • Angular CLI: Angular CLI 集成了测试工具,方便创建、运行和管理测试。

  • Jest: 一个流行的 JavaScript 测试框架,具有快速、零配置和快照测试等优点。

graph TD A[测试工具和框架] --> B(Jasmine); A --> C(Karma); A --> D(Protractor); A --> E(Angular CLI); A --> F(Jest);

3.3.3 单元测试实践

3.3.3.1 准备工作

使用 Angular CLI 创建一个新项目:

ng new angular-testing-example cd angular-testing-example

创建一个简单的服务:

ng generate service calculator

src/app/calculator.service.ts

import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class CalculatorService { constructor() { } add(a: number, b: number): number { return a + b; } subtract(a: number, b: number): number { return a - b; } }

3.3.3.2 编写单元测试

Angular CLI 已经为我们生成了 src/app/calculator.service.spec.ts 文件,我们可以在其中编写单元测试。

import { TestBed } from '@angular/core/testing'; import { CalculatorService } from './calculator.service'; describe('CalculatorService', () => { let service: CalculatorService; beforeEach(() => { TestBed.configureTestingModule({}); service = TestBed.inject(CalculatorService); }); it('should be created', () => { expect(service).toBeTruthy(); }); it('should add two numbers', () => { expect(service.add(2, 3)).toBe(5); }); it('should subtract two numbers', () => { expect(service.subtract(5, 2)).toBe(3); }); });
  • describe 函数用于定义一个测试套件,其中包含多个相关的测试用例。

  • beforeEach 函数用于在每个测试用例执行之前执行一些准备工作,例如创建服务实例。

  • it 函数用于定义一个测试用例,其中包含一个或多个断言。

  • expect 函数用于创建一个断言,验证代码的执行结果是否符合预期。

3.3.3.3 运行单元测试

使用 Angular CLI 运行单元测试:

ng test

Karma 会启动浏览器并执行测试代码,并在终端中显示测试结果。

3.3.4 组件测试实践

3.3.4.1 准备工作

创建一个简单的组件:

ng generate component hello

src/app/hello/hello.component.ts

import { Component, Input } from '@angular/core'; @Component({ selector: 'app-hello', templateUrl: './hello.component.html', styleUrls: ['./hello.component.css'] }) export class HelloComponent { @Input() name: string = ''; }

src/app/hello/hello.component.html

<p>Hello, {{ name }}!</p>

3.3.4.2 编写组件测试

Angular CLI 已经为我们生成了 src/app/hello/hello.component.spec.ts 文件,我们可以在其中编写组件测试。

import { ComponentFixture, TestBed } from '@angular/core/testing'; import { HelloComponent } from './hello.component'; import { By } from '@angular/platform-browser'; describe('HelloComponent', () => { let component: HelloComponent; let fixture: ComponentFixture<HelloComponent>; beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [ HelloComponent ] }) .compileComponents(); fixture = TestBed.createComponent(HelloComponent); component = fixture.componentInstance; fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); }); it('should display the name', () => { component.name = 'World'; fixture.detectChanges(); const paragraph = fixture.debugElement.query(By.css('p')); expect(paragraph.nativeElement.textContent).toContain('Hello, World!'); }); });
  • TestBed.createComponent 函数用于创建一个组件实例,并返回一个 ComponentFixture 对象。

  • ComponentFixture 对象提供了访问组件实例、组件模板和组件宿主元素的方法。

  • fixture.detectChanges 函数用于触发 Angular 的变更检测,更新组件模板中的数据绑定。

  • fixture.debugElement.query 函数用于在组件模板中查找元素。

3.3.4.3 运行组件测试

使用 Angular CLI 运行组件测试:

ng test

Karma 会启动浏览器并执行测试代码,并在终端中显示测试结果。

3.3.5 集成测试实践

集成测试通常涉及测试组件之间的交互,或者服务与组件之间的交互。

例如,假设我们有一个 UserListComponent,它从 UserService 获取用户列表并显示出来。我们需要测试 UserListComponent 是否正确地调用了 UserService,并且正确地显示了用户列表。

首先,创建 UserServiceUserListComponent

ng generate service user ng generate component user-list

src/app/user.service.ts

import { Injectable } from '@angular/core'; import { of } from 'rxjs'; export interface User { id: number; name: string; } @Injectable({ providedIn: 'root' }) export class UserService { getUsers() { const users: User[] = [ { id: 1, name: 'John Doe' }, { id: 2, name: 'Jane Smith' } ]; return of(users); } }

src/app/user-list/user-list.component.ts

import { Component, OnInit } from '@angular/core'; import { UserService, User } from '../user.service'; @Component({ selector: 'app-user-list', templateUrl: './user-list.component.html', styleUrls: ['./user-list.component.css'] }) export class UserListComponent implements OnInit { users: User[] = []; constructor(private userService: UserService) { } ngOnInit(): void { this.userService.getUsers().subscribe(users => { this.users = users; }); } }

src/app/user-list/user-list.component.html

<ul> <li *ngFor="let user of users">{{ user.name }}</li> </ul>

现在,编写集成测试:

src/app/user-list/user-list.component.spec.ts

import { ComponentFixture, TestBed } from '@angular/core/testing'; import { UserListComponent } from './user-list.component'; import { UserService, User } from '../user.service'; import { of } from 'rxjs'; import { DebugElement } from '@angular/core'; import { By } from '@angular/platform-browser'; describe('UserListComponent', () => { let component: UserListComponent; let fixture: ComponentFixture<UserListComponent>; let userService: UserService; let getUsersSpy: jasmine.Spy; const mockUsers: User[] = [ { id: 1, name: 'John Doe' }, { id: 2, name: 'Jane Smith' } ]; beforeEach(async () => { const userServiceMock = jasmine.createSpyObj('UserService', ['getUsers']); await TestBed.configureTestingModule({ declarations: [ UserListComponent ], providers: [ { provide: UserService, useValue: userServiceMock } ] }) .compileComponents(); fixture = TestBed.createComponent(UserListComponent); component = fixture.componentInstance; userService = TestBed.inject(UserService); getUsersSpy = userServiceMock.getUsers.and.returnValue(of(mockUsers)); fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); }); it('should call userService.getUsers on init', () => { expect(getUsersSpy).toHaveBeenCalledTimes(1); }); it('should display the users', () => { const listItems: DebugElement[] = fixture.debugElement.queryAll(By.css('li')); expect(listItems.length).toBe(2); expect(listItems[0].nativeElement.textContent).toBe('John Doe'); expect(listItems[1].nativeElement.textContent).toBe('Jane Smith'); }); });

在这个测试中,我们使用了 jasmine.createSpyObj 创建了一个 UserService 的模拟对象。这样我们就可以控制 getUsers 方法的返回值,并且验证它是否被调用。

3.3.6 端到端测试实践

端到端测试使用 Protractor 或 Cypress 等工具来模拟用户在浏览器中的行为。由于 E2E 测试涉及整个应用的运行,因此通常需要一个运行中的 Angular 应用。

3.3.6.1 准备工作

确保已经安装了 Protractor:

npm install -g protractor webdriver-manager update

3.3.6.2 编写 E2E 测试

Angular CLI 已经为我们生成了 e2e/src/app.e2e-spec.ts 文件,我们可以在其中编写 E2E 测试。

import { AppPage } from './app.po'; import { browser, logging } from 'protractor'; describe('workspace-project App', () => { let page: AppPage; beforeEach(() => { page = new AppPage(); }); it('should display welcome message', async () => { await page.navigateTo(); expect(await page.getTitleText()).toEqual('angular-testing-example app is running!'); }); afterEach(async () => { // Assert that there are no errors emitted from the browser const logs = await browser.manage().logs().get(logging.Type.BROWSER); expect(logs).not.toContain(jasmine.objectContaining({ level: logging.Level.SEVERE, } as logging.Entry)); }); });

3.3.6.3 运行 E2E 测试

首先,启动 Angular 应用:

ng serve

然后,运行 E2E 测试:

ng e2e

Protractor 会启动浏览器并执行测试代码,并在终端中显示测试结果。

3.3.7 测试策略和最佳实践

  • 尽早开始测试: 在开发过程中尽早开始编写测试,可以帮助我们尽早发现并修复错误。

  • 编写清晰的测试用例: 测试用例应该清晰、简洁、易于理解和维护。

  • 覆盖所有重要的代码路径: 测试用例应该覆盖所有重要的代码路径,确保代码的正确性。

  • 使用 Mock 和 Stub: 在单元测试和集成测试中,可以使用 Mock 和 Stub 来模拟外部依赖,隔离被测试单元。

  • 持续集成: 将测试集成到持续集成流程中,可以确保每次代码提交都会自动运行测试,及时发现并修复错误。

  • 测试驱动开发 (TDD): 在编写实际代码之前先编写测试用例,有助于更好地理解需求,并确保代码的质量。

3.3.8 总结

测试是 Angular 应用开发中不可或缺的一部分。通过编写各种类型的测试,我们可以确保代码的质量、可维护性和可靠性。Angular 社区提供了强大的测试工具和框架,使得编写测试变得更加容易。 掌握 Angular 的测试技术,可以帮助我们构建更加健壮和可靠的应用。


发布者: 作者: 转发
评论区 (0)
U