Angular 单元测试实践 (2)

在实际的应用开发中,组件会依赖一个或多个服务。在对组件进行单元测试时,不应该创建一个真实的服务,更不应该在此时去测试真实的服务,而是应该模拟一个真实的服务,满足单元测试就可以了。这里介绍两个方案:

  • Stubbing: 该方法会让依赖注入器注入一个依赖的 stub 对象,而不是真实的依赖对象。一个 stub 就是一个实际依赖的仿制对象,可以控制他的行为,满足单元测试需要。
  • Spying: 该方法会注入实际的依赖,并为调用的依赖的方法添加一个监视器。这样,既可以让依赖返回模拟的数据,也可以只是完成方法的调用。

接下来,我们看看如何使用这两种方案。

Stubbing

使用 stubbing 方法创建一个依赖的仿品,有两个方式:

  1. 创建一个常量,包含真实依赖的属性和方法。
  2. 创建一个实例,完全模拟真实依赖的定义。

两个方式是完全相似的,我们介绍如何使用第一个方法。

  1. 首先,我们创建一个服务,作为组件的一个依赖。
import { Injectable } from '@angular/core’;

@Injectable({
  providedIn: ‘root’
})
export class StubsService {

  name: string;
  isBusy: boolean;

  constructor() { }
}
  1. 在组件里,我们调用服务,输出一条消息。
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 + " 服务正忙”;
  }

}
  1. 在编写单元测试时,先声明一些全局变量:
  let component: StubsComponent;
  let fixture: ComponentFixture;
  let messageToDisplay: HTMLElement;
  let service: StubsService;

  const serviceStubs: Partial = {
    name: ‘HelloWorld’
  };

Partial 关键字的使用,可以只设置服务的 name 属性。

  1. 在初始化方法中,进行模块的配置,并注入依赖服务。
  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);
  });
  1. 测试服务名称正确。
  it('should display the service name', () => {
    fixture.detectChanges();
    expect(messageToDisplay.textContent).toContain(serviceStubs.name);
  });
  1. 测试服务正忙的消息输出是否正确。
  describe('status', () => {
    it('should be unavailable', () => {
      service.isBusy = true;
      fixture.detectChanges();
      expect(messageToDisplay.textContent).toContain('服务正忙’);
    });
  1. 测试服务可用的消息输出是否正确。
    it('should be available', () => {
      service.isBusy = false;
      fixture.detectChanges();
      expect(messageToDisplay.textContent).toContain('服务可用’);
    });

单元测试输出页面效果:

Stubbing 单元测试输出页面

Spying

虽然使用 Spying 方法需要创建一个真实依赖的实例,但是在测试过程中,我们能够获得更多的信息,比如方法调用次数,传入的参数等。

我们使用 Angular 框架提供的 Title 服务,来介绍如何使用 Spying 方法。

  1. 创建一个用于测试的组件。
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();
  }

}
  1. 在初始化方法中,进行模块的配置,并创建组件测试装置实例。
  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [ SpyComponent ],
      providers: [Title]
    });
    fixture = TestBed.createComponent(SpyComponent);
    component = fixture.componentInstance;
  });
  1. 使用 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() 方法, 验证最近一次的调用是比较安全的。

单元测试输出页面效果:

Spying 单元测试输出页面

版权声明:
作者:dingding
链接:https://www.techfm.club/p/46101.html
来源:TechFM
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
< <上一篇
下一篇>>