使用angular中的單元測試,脫離後臺獨立開發

在先後臺開發的項目中,咱們當前已經作到了後臺依靠單元測試,徹底的脫離前臺進行開發。那麼,在進行單臺開發時,是否也能夠作到只依賴於UML圖,不依賴於後臺進行獨立的開發呢?答案是確定的。css

本文旨在帶領你:在前臺徹底脫離後臺開發路上更近一步。html

前期代碼準備

在此,咱們以近期開發的Alice學生管理系統爲例,將學院管理的index組件拿出來,以展現脫離後臺的前臺。
文件列表:跨域

CollegeIndexComponent: 學院管理INDEX列表組件
College: 學院實體
CollegeService: 學院服務
其它輔助文件略

CollegeIndexComponent 學院管理INDEX列表組件瀏覽器

import {Component, OnInit} from '@angular/core';
import {CollegeService} from '../../../../../service/college.service';
import {College} from '../../../../../entity/college';
import {Page} from '../../../../../entity/page';

@Component({
    selector: 'app-college-index',
    templateUrl: './college-index.component.html',
    styleUrls: ['./college-index.component.css']
})
export class CollegeIndexComponent implements OnInit {

    page: number;

    size: number;

    total: number;
    collegeList: Array<College>;

    constructor(private collegeService: CollegeService) {
    }

    ngOnInit() {
        console.log(this.collegeService);
        this.collegeList = new Array<College>();
        this.page = 1;
        this.size = 5;
        this.queryPage();
    }

    queryPage() {
        this.collegeService.getCollegeByPage(this.page - 1, this.size)
            .subscribe((data: Page<College>) => {
                this.collegeList = data.content;
                this.total = data.totalElements;
            }, () => {
                console.log('network error');
            });
    }
}

College: 學院實體antd

import {Teacher} from './teacher';
import {Major} from './major';

/**
 * 學院實體
 */
export class College {
    id: number;                 // id
    name: string;               // 名稱
    teacherList: Teacher[];     // 負責教師
    majorList: Major[];

    [propName: string]: any;

    /**
     * 獲取學院實體
     */
    static instance(): College {
        const college = new College();
        college.id = 1;
        college.name = '測試學院';
        college.teacherList = new Array<Teacher>(
            Teacher.instance()
        );
        college.majorList = new Array<Major>();
        return college;
    }
}

CollegeService: 學院服務app

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { College } from '../entity/college';
import { Observable } from 'rxjs';
import { Page } from '../entity/page';

@Injectable({
    providedIn: 'root',
})
export class CollegeService {
    baseUrl = 'College';

    constructor(protected http: HttpClient) {}
    /**
     *獲取分頁數據
     */
    public getCollegeByPage(page: number, size: number): Observable<Page<College>> {
        const params = {
            page: page.toString(),
            size: size.toString(),
        };
        return this.http.get<Page<College>>(this.baseUrl + '/page', {
            params: params,
        });
    }
}

單元測試

代碼以下:async

import {async, ComponentFixture, TestBed} from '@angular/core/testing';

import {CollegeIndexComponent} from './college-index.component';
import {NzGridModule, NzDividerModule, NzTableModule, NzFormModule} from 'ng-zorro-antd';
import {HttpClientModule} from '@angular/common/http';
import {RouterTestingModule} from '@angular/router/testing';
import {FormsModule, ReactiveFormsModule} from '@angular/forms';

/**
 * 對測試的描述:CollegeIndexComponent
 */
describe('CollegeIndexComponent', () => {
    // 定義兩個變量
    let component: CollegeIndexComponent;
    let fixture: ComponentFixture<CollegeIndexComponent>;

    beforeEach(async(() => {
        // TestBed工做臺
        // TestBed.configureTestingModule 配置要測試的模塊
        // 這貼近於現實生活。現實生活中,咱們測試一塊電錶是否正確.
        // 1. 須要一個專門用於測試的工做臺
        // 2. 須要對這個測試的工做進行配置,以讓其符合測試電錶的基礎工做
        TestBed.configureTestingModule({
            // 聲明要上工做臺的組件
            declarations: [CollegeIndexComponent],
            // 配置測試的依賴,沒有這些模塊,測試就進行不了。
            // 好比咱們測試電錶是否準確,須要有交流電,須要有電流表,須要有電壓表等
            imports: [NzGridModule,
                HttpClientModule,
                NzDividerModule,
                NzTableModule,
                RouterTestingModule,
                ReactiveFormsModule,
                FormsModule,
                NzFormModule]
        }).compileComponents(); // 配置完後,開始編譯要測試組件
    }));

    // 每次測試前,均執行一遍
    beforeEach(() => {
        // fix = 固定。用工做臺建立一個供測試的組件。
        // 因爲其想要被測試,就必然還須要其它的一些腳手架。
        // 咱們把腳手架與被測試的組件組成的聯合體稱爲:ComponentFixture 被固定的組件
        fixture = TestBed.createComponent(CollegeIndexComponent);

        // 實例化要測試的組件
        component = fixture.componentInstance;

        // 檢測變化
        fixture.detectChanges();
    });

    /**
     * 測試方法的描述信息:should create
     */
    it('should create', () => {
        // 期待 組件 被成功建立
        expect(component).toBeTruthy();
    });
});

執行此單元測試,雖然可以經過,但將在控制檯獲得如下錯誤信息。ide

clipboard.png

clipboard.png

同時,展現的界面以下:單元測試

clipboard.png

咱們看到,在測試時依賴於httpClient發起的請求,因爲咱們沒有啓動後臺服務(即便啓動了,使用絕對地址訪問的方法也有問題),因此沒法由後臺獲取數據。其實,即便是咱們進行跨域的地址設置,因爲後臺咱們每每會進行權限驗證(是否登陸,當前登陸用戶是否有當前操做的權限等),因此:想發起一個正常的請求,並不容易。測試

下面,讓咱們在不破壞原有服務層的基礎上,自定義返回數據,從而規避http請求,達到不須要後臺,即可以看到真實數據的目的。

自定義返回數據

在單元測試時,調用beforeEachit方法時都可以將@angular/core/testing -> injetct()傳入。在此,咱們以beforeEach爲例,注入CollegeService,並在測試組件實例化之前,對其getCollegeByPage方法的返回值,進行重寫。

/**
     *  每次測試前,均執行一遍
     *  inject([], () => {}) 單元測試中的方法
     *  CollegeService 要注入的服務
     *  StudentService 要注入的服務 (僅用於展現注入多個服務)
     *  s爲服務起的別名,類型爲CollegeService,其對應注入的第一個參數
     *  t爲服務起的別名,類型爲StudentService,其對應注入的第二個參數(僅用於展現注入多個服務)
     */
    beforeEach(inject([CollegeService, StudentService], (s: CollegeService, t: StudentService) => {
        console.log(s);
        console.log(t);
        // fix = 固定。用工做臺建立一個供測試的組件。
        // 因爲其想要被測試,就必然還須要其它的一些腳手架。
        // 咱們把腳手架與被測試的組件組成的聯合體稱爲:ComponentFixture 被固定的組件
        fixture = TestBed.createComponent(CollegeIndexComponent);

        // 實例化要測試的組件
        component = fixture.componentInstance;

        // 檢測變化
        fixture.detectChanges();
    }));

clipboard.png

使用以下方法改寫返回值:

/**
     *  每次測試前,均執行一遍
     *  inject([], () => {}) 單元測試中的方法
     *  CollegeService 要注入的服務
     *  StudentService 要注入的服務(僅用於展現注入多個服務)
     *  collegeService爲服務起的別名,類型爲CollegeService,其對應注入的第一個參數
     *  studentService爲服務起的別名,類型爲StudentService,其對應注入的第二個參數(僅用於展現注入多個服務)
     */
    beforeEach(inject([CollegeService, StudentService], (collegeService: CollegeService, studentService: StudentService) => {
        // 變量初始化
        const collegePage = new Page<College>();
        collegePage.content = new Array<College>(
            College.instance()
        );

        // 改寫collegeService中的getCollegeByPage方法的返回值。
        spyOn(collegeService, 'getCollegeByPage').and.returnValue(of(collegePage));

        // fix = 固定。用工做臺建立一個供測試的組件。
        // 因爲其想要被測試,就必然還須要其它的一些腳手架。
        // 咱們把腳手架與被測試的組件組成的聯合體稱爲:ComponentFixture 被固定的組件
        fixture = TestBed.createComponent(CollegeIndexComponent);

        // 實例化要測試的組件
        component = fixture.componentInstance;

        // 檢測變化
        fixture.detectChanges();
    }));

此時,咱們再次測試,正常的經過了測試,控制檯沒有報錯,並且:咱們在瀏覽器上看到了咱們的組件。最關鍵的是:咱們即沒有使用後臺,也沒有改變CollegeService中的代碼。

clipboard.png

參考文獻:
深度好文

言簡意賅

官方文檔

相關文章
相關標籤/搜索