如何开始Angular的单元测试
介绍
如果您的项目是使用 Angular CLI 创建的,那么一切准备就绪,您可以开始使用 Jasmine 作为测试框架并使用 Karma 作为测试运行器来编写测试.
Angular 还提供了诸如 TestBed
和 async
之类的实用程序,以简化异步代码、组件、指令或服务的测试。
在本文中,您将了解如何使用 Jasmine 和 Karma 在 Angular 中编写和运行单元测试。
先决条件
要完成本教程,您需要:
- Node.js 安装在本地,您可以按照【X57X】如何安装Node.js 并创建本地开发环境【X126X】进行。
- 熟悉 设置 Angular 项目 。
本教程已使用 Node v16.2.0、npm
v7.15.1 和 @angular/core
v12.0.4 进行了验证。
第 1 步 — 设置项目
您的测试文件通常放在他们测试的文件旁边,但如果您愿意,它们也可以放在自己单独的目录中。
这些规范文件使用 *.spec.ts
的命名约定。
首先,使用 @angular/cli
创建一个新项目:
ng new angular-unit-test-example
然后,导航到新创建的项目目录:
cd angular-unit-test-example
除了 app.component
之外,还有一个 app.component.spec.ts
文件。 打开此文件并检查其内容:
src/app/app.component.spec.ts
import { TestBed } from '@angular/core/testing'; import { AppComponent } from './app.component'; describe('AppComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [ AppComponent ], }).compileComponents(); }); it('should create the app', () => { const fixture = TestBed.createComponent(AppComponent); const app = fixture.componentInstance; expect(app).toBeTruthy(); }); it(`should have as title 'angular-unit-test-example'`, () => { const fixture = TestBed.createComponent(AppComponent); const app = fixture.componentInstance; expect(app.title).toEqual('angular-unit-test-example'); }); it('should render title', () => { const fixture = TestBed.createComponent(AppComponent); fixture.detectChanges(); const compiled = fixture.nativeElement; expect(compiled.querySelector('.content span').textContent).toContain('angular-unit-test-example app is running!'); }); });
了解茉莉花
首先,关于茉莉花的一些重要知识:
describe
块定义了一个测试套件,每个it
块用于单独的测试。beforeEach
在每次测试之前运行,用于测试的setup
部分。afterEach
在每次测试后运行,用于测试的teardown
部分。- 您还可以使用
beforeAll
和afterAll
,它们在所有测试之前或之后运行一次。 - 您使用
expect
并使用toBeDefined
、toBeTruthy
、toContain
、toEqual
、[ X121X]、toBeNull
、... 例如:expect(myValue).toBeGreaterThan(3);
- 您可以使用
not
进行否定断言:expect(myValue).not.toBeGreaterThan(3);
- 您还可以定义自定义匹配器。
TestBed
是可用于 Angular 特定测试的主要实用程序。 您将在测试套件的 beforeEach
块中使用 TestBed.configureTestingModule
并为其提供一个与 declarations
、[ 的常规 NgModule
具有相似值的对象X142X] 和 imports
。 然后你可以调用 compileComponents
来告诉 Angular 编译声明的组件。
您可以使用 TestBed.createComponent
创建一个 component fixture
。 夹具可以访问 debugElement
,这将使您能够访问组件夹具的内部。
更改检测不会自动完成,因此您将在夹具上调用 detectChanges
来告诉 Angular 运行更改检测。
用 async
包装测试的回调函数或 beforeEach
的第一个参数允许 Angular 执行异步编译并等待 async
块内的内容准备好之前继续。
了解测试
第一个测试名为 should create the app
,它使用 expect
检查是否存在具有 toBeTruthy()
的组件。
第二个测试名为 should have as title 'angular-unit-test-example'
,它使用 expect
来检查 app.title
值是否等于字符串 'angular-unit-test-example'
和 toEqual()
。
第三个测试名为 should render title
,它使用 expect
来检查文本 'angular-unit-test-example app is running!'
和 toContain()
的编译代码。
在您的终端中,运行以下命令:
ng test
所有三个测试都将运行并显示测试结果:
Output3 specs, 0 failures, randomized with seed 84683 AppComponent * should have as title 'angular-unit-test-example' * should create the app * should render title
目前所有三个测试都通过了。
第 2 步 — 构建示例组件
让我们创建一个增加或减少值的组件。
在代码编辑器中打开 app.component.ts
并将以下代码行替换为 increment
和 decrement
逻辑:
src/app/app.component.ts
import { Component } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { value = 0; message!: string; increment() { if (this.value < 15) { this.value += 1; this.message = ''; } else { this.message = 'Maximum reached!'; } } decrement() { if (this.value > 0) { this.value -= 1; this.message = ''; } else { this.message = 'Minimum reached!'; } } }
在代码编辑器中打开 app.component.html
并将内容替换为以下代码:
src/app/app.component.html
<h1>{{ value }}</h1> <hr> <button (click)="increment()" class="increment">Increment</button> <button (click)="decrement()" class="decrement">Decrement</button> <p class="message"> {{ message }} </p>
此时,您应该已经有了 app.component.ts
和 app.component.html
的修订版本。
第 3 步 — 构建测试套件
使用您的代码编辑器重新访问 app.component.spec.ts
并将其替换为以下代码行:
src/app/app.component.spec.ts
import { TestBed, async, ComponentFixture } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { DebugElement } from '@angular/core'; import { AppComponent } from './app.component'; describe('AppComponent', () => { let fixture: ComponentFixture<AppComponent>; let debugElement: DebugElement; beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [ AppComponent ], }).compileComponents(); fixture = TestBed.createComponent(AppComponent); debugElement = fixture.debugElement; })); it('should increment and decrement value', () => { fixture.componentInstance.increment(); expect(fixture.componentInstance.value).toEqual(1); fixture.componentInstance.decrement(); expect(fixture.componentInstance.value).toEqual(0); }); it('should increment value in template', () => { debugElement .query(By.css('button.increment')) .triggerEventHandler('click', null); fixture.detectChanges(); const value = debugElement.query(By.css('h1')).nativeElement.innerText; expect(value).toEqual('1'); }); it('should stop at 0 and show minimum message', () => { debugElement .query(By.css('button.decrement')) .triggerEventHandler('click', null); fixture.detectChanges(); const message = debugElement.query(By.css('p.message')).nativeElement.innerText; expect(fixture.componentInstance.value).toEqual(0); expect(message).toContain('Minimum'); }); it('should stop at 15 and show maximum message', () => { fixture.componentInstance.value = 15; debugElement .query(By.css('button.increment')) .triggerEventHandler('click', null); fixture.detectChanges(); const message = debugElement.query(By.css('p.message')).nativeElement.innerText; expect(fixture.componentInstance.value).toEqual(15); expect(message).toContain('Maximum'); }); });
我们直接在 beforeEach
块中分配 fixture
和 debugElement
因为我们所有的测试都需要这些。 我们还通过从 @angular/core/testing
导入 ComponentFixture
和从 @angular/core
导入 DebugElement
来强类型化它们。
在我们的第一个测试中,我们调用组件实例本身的方法。
在剩下的测试中,我们使用我们的 DebugElement
来触发按钮点击。 注意 DebugElement
有一个 query
方法接受一个谓词。 在这里,我们使用 By
实用程序及其 css
方法在模板中查找特定元素。 DebugElement
还有一个 nativeElement
方法,用于直接访问 DOM。
我们还在最后 3 个测试中使用 fixture.detectChanges
来指示 Angular 在使用 Jasmine 的 expect
进行断言之前运行更改检测。
完成更改后,从终端运行 ng test
命令:
ng test
这将以监视模式启动 Karma,因此您的测试将在每次文件更改时重新编译。
Output4 specs, 0 failures, randomized with seed 27239 AppComponent * should increment value in template * should increment and decrement value * should stop at 0 and show minimum message * should stop at 15 and show maximum message
所有四项测试都将通过。
结论
在本文中,您将了解如何使用 Jasmine 和 Karma 在 Angular 中编写和运行单元测试。 现在您已经了解了主要的 Angular 测试实用程序,并且可以开始为简单组件编写测试了。
继续学习测试具有依赖关系的组件、测试服务以及使用 模拟、存根 和间谍。
您也可以参考 官方文档 以获得深入的 Angular 测试指南。