Angular 单元测试实践 (2)
在实际的应用开发中,组件会依赖一个或多个服务。在对组件进行单元测试时,不应该创建一个真实的服务,更不应该在此时去测试真实的服务,而是应该模拟一个真实的服务,满足单元测试就可以了。这里介绍两个方案:
- Stubbing: 该方法会让依赖注入器注入一个依赖的 stub 对象,而不是真实的依赖对象。一个 stub 就是一个实际依赖的仿制对象,可以控制他的行为,满足单元测试需要。
- Spying: 该方法会注入实际的依赖,并为调用的依赖的方法添加一个监视器。这样,既可以让依赖返回模拟的数据,也可以只是完成方法的调用。
接下来,我们看看如何使用这两种方案。
Stubbing
使用 stubbing 方法创建一个依赖的仿品,有两个方式:
- 创建一个常量,包含真实依赖的属性和方法。
- 创建一个实例,完全模拟真实依赖的定义。
两个方式是完全相似的,我们介绍如何使用第一个方法。
- 首先,我们创建一个服务,作为组件的一个依赖。
import { Injectable } from '@angular/core’;
@Injectable({
providedIn: ‘root’
})
export class StubsService {
name: string;
isBusy: boolean;
constructor() { }
}
- 在组件里,我们调用服务,输出一条消息。
import { Component, OnInit } from '@angular/core’;
import { StubsService } from "./stubs.service”;
@Component({
selector: 'app-stubs’,
template: `{{message}}`
})
export class StubsComponent implements OnInit {
message: string;
constructor(private stubs: StubsService) { }
ngOnInit(): void {
this.message = !this.stubs.isBusy ? this.stubs.name + ' 服务可用' : this.stubs.name + " 服务正忙”;
}
}
- 在编写单元测试时,先声明一些全局变量:
let component: StubsComponent;
let fixture: ComponentFixture;
let messageToDisplay: HTMLElement;
let service: StubsService;
const serviceStubs: Partial = {
name: ‘HelloWorld’
};
Partial
关键字的使用,可以只设置服务的 name
属性。
- 在初始化方法中,进行模块的配置,并注入依赖服务。
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [ StubsComponent ],
providers: [
{ provide: StubsService, useValue: serviceStubs}
]
});
fixture = TestBed.createComponent(StubsComponent);
component = fixture.componentInstance;
messageToDisplay = fixture.nativeElement.querySelector('span’);
service = TestBed.inject(StubsService);
});
- 测试服务名称正确。
it('should display the service name', () => {
fixture.detectChanges();
expect(messageToDisplay.textContent).toContain(serviceStubs.name);
});
- 测试服务正忙的消息输出是否正确。
describe('status', () => {
it('should be unavailable', () => {
service.isBusy = true;
fixture.detectChanges();
expect(messageToDisplay.textContent).toContain('服务正忙’);
});
- 测试服务可用的消息输出是否正确。
it('should be available', () => {
service.isBusy = false;
fixture.detectChanges();
expect(messageToDisplay.textContent).toContain('服务可用’);
});
单元测试输出页面效果:
Spying
虽然使用 Spying 方法需要创建一个真实依赖的实例,但是在测试过程中,我们能够获得更多的信息,比如方法调用次数,传入的参数等。
我们使用 Angular 框架提供的 Title
服务,来介绍如何使用 Spying 方法。
- 创建一个用于测试的组件。
import { Component, OnInit } from '@angular/core’;
import { Title } from '@angular/platform-browser’;
@Component({
selector: 'app-spy’,
template: '{{caption}}’
})
export class SpyComponent implements OnInit {
caption: string;
constructor(private title: Title) { }
ngOnInit(): void {
this.title.setTitle('Angular Spying’);
this.caption = this.title.getTitle();
}
}
- 在初始化方法中,进行模块的配置,并创建组件测试装置实例。
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [ SpyComponent ],
providers: [Title]
});
fixture = TestBed.createComponent(SpyComponent);
component = fixture.componentInstance;
});
- 使用 spy 测试标题设置是否正确。
it('should set the title', () => {
const title = TestBed.inject(Title);
const spy = spyOn(title, 'setTitle’);
fixture.detectChanges();
expect(spy.calls.mostRecent().args[0]).toBe('Angular Spying’);
});
spyOn
方法接收两个参数:要监控的对象和要调用的方法。expect
方法验证了传递给 setTitle
方法的参数。在一个组件的生命周期里,一个方法可能被调用很多次,使用 spy.calls.mostRecent()
方法, 验证最近一次的调用是比较安全的。
单元测试输出页面效果:
共有 0 条评论