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完成測試,手動設置路由變化,以便後面的代碼正常執行,使得被測組件能夠獲取到隨機的測試數據,進而成功渲染。

相關文章
相關標籤/搜索