Angular弹珠测试中,因为未获取路由参数致使数据不返回的问题

概述

最近在进行angular单元测试在组件初始化渲染方面的测试,使用的是RxJS提供的弹珠测试来模拟http请求延迟。segmentfault

可是在某些编辑组件中,ngOnInit方法中使用了router来获取路由参数。在单元测试中,组件不会接收真的路由参数,所以并无接收到测试数据,初始化时,渲染的组件是空数据。dom

本文的目的,是经过手动发送路由参数,来使组件成功获取测试数据。ide

本文使用的技术基于:angularu在单元测试中如何模拟HTTP请求延迟单元测试

问题复现

在未出现问题的状况下,当咱们为服务层设置好测试桩、在测试文件中模拟好返回值以后,启动测试以后,组件会正常渲染,而且会填入模拟返回的随机数据。
若是在断言非空的语句Assert.isDefined后面加入console.log,控制台能够输出字符串。测试

以下:this

image.png
image.png

然而,在后来测试的这几个组件中,按照示例代码写完、测试以后,渲染的组件并无模拟数据,而是空组件。
虽然测试也能经过,但经过是由于断言的代码根本没有执行,代码中加入console.log,控制台没有输出。以下:spa

image.png
image.png

尽管在这种状况下,单元测试也能经过,但事实上,因为根本没有接收到返回值,咱们没法验证服务层的返回值是否正确,这种作法对项目来讲,是不负责任的,这增大了生产环境出错的风险。
image.png3d

问题分析

先上组件的ngOnInit初始化代码:code

ngOnInit() {
    this.getEditCollege();
  }

  /**
   * 获取要编辑的学院
   */
  public getEditCollege() {
    // 发起订阅,获取路由参数,得到参数后执行回调
    this.route.params.subscribe((params: {id: number}) => {
      console.log("subscribe");
      this.collegeService.getCollegeById(params.id).subscribe((data) => {
        Assert.isDefined(data.id, 'id');
        Assert.isDefined(data.name, 'name');
        Assert.isDefined(data.code, 'code');
        Assert.isDefined(data.number, 'number');
        console.log("college->edit");
        this.initForm(data);
      });
    });
  }

经过概括发现,全部出错的组件都有用route获取路由参数的过程:发起订阅,当结果返回时执行回调方法,向服务层获取对象。component

用打断点的方式得出结论,route发起的订阅根本没有返回,所以后面的回调方法也就没有执行。

由于route是经过路由的变化,来获取参数路由参数的,但在单元测试中,路由并不会发生变化。

因此须要用一个假的ActiveRouteStub来代替真的ActiveRoute完成单元测试。

ActiveRouteStub

/**
 * ActiveRouteStub
 * 路由功能服务桩
 */

import { Observable, Subject } from 'rxjs';

export class ActivatedRouteStub {
  paramsSubject = new Subject<any>();
  params: Observable<any>;
  parent: any;
  snapshot = {
    paramMap: {
      get: () => {
        return 0;
      }
    }
  };

  constructor() {
    this.params = this.paramsSubject.asObservable();
  }
}

而后从Provider引入(也能够引入到单元测试专用的辅助模块中,例如ServiceTestingModule):

providers: [
    {provide: ActivatedRoute, useClass: ActivatedRouteStub}
  ]

在测试类中改变路由参数

接下来经过代码,收到改变路,传入和组件接收的类型相对应的参数(示例组件中使用ID):

it('should create', fakeAsync(() => {
    // 字段断言,调用getCollegeById
    expect(component).toBeTruthy();
    // 尝试获取真的,实际上获取的假的
    const route = TestBed.get(ActivatedRoute) as ActivatedRouteStub;
    // 发送路由参数,参数为要编辑的对象的ID
    route.paramsSubject.next({id: randomNumber()});
  }));

效果

这样,由于route发起的订阅有了返回值,回调方法被执行,因此被测组件就能得到测试数据。

image.png

只有当测试数据真真切切的被填入组件中,咱们才能判定这个组件的初始化确实是正常的。

总结

单元测试中,咱们尝尝使用RxJS提供的弹珠测试来模拟http请求延迟,因为route须要检测路由变化来获取参数,而单元测试中组件的路由不会发生变化,所以会形成订阅没法返回的状况,进而致使测试时的组件渲染不正常。

解决办法就是使用ActiveRouteStub代替ActiveRoute完成测试,手动设置路由变化,以便后面的代码正常执行,使得被测组件能够获取到随机的测试数据,进而成功渲染。

相关文章
相关标签/搜索