在先後臺開發的項目中,咱們當前已經作到了後臺依靠單元測試,徹底的脫離前臺進行開發。那麼,在進行單臺開發時,是否也能夠作到只依賴於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
同時,展現的界面以下:單元測試
咱們看到,在測試時依賴於httpClient
發起的請求,因爲咱們沒有啓動後臺服務(即便啓動了,使用絕對地址訪問的方法也有問題),因此沒法由後臺獲取數據。其實,即便是咱們進行跨域的地址設置,因爲後臺咱們每每會進行權限驗證(是否登陸,當前登陸用戶是否有當前操做的權限等),因此:想發起一個正常的請求,並不容易。測試
下面,讓咱們在不破壞原有服務層的基礎上,自定義返回數據,從而規避http
請求,達到不須要後臺,即可以看到真實數據的目的。
在單元測試時,調用beforeEach
及it
方法時都可以將@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(); }));
使用以下方法改寫返回值:
/** * 每次測試前,均執行一遍 * 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
中的代碼。
參考文獻:
深度好文