使用angular中的单元测试,脱离后台独立开发

21次阅读

共计 5281 个字符,预计需要花费 14 分钟才能阅读完成。

在前后台开发的项目中,我们当前已经做到了后台依靠单元测试,完全的脱离前台进行开发。那么,在进行单台开发时,是否也可以做到只依赖于 UML 图,不依赖于后台进行独立的开发呢?答案是肯定的。
本文旨在带领你:在前台完全脱离后台开发路上更近一步。
前期代码准备
在此,我们以近期开发的 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: 学院实体
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: 学院服务
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,
});
}
}
单元测试
代码如下:
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();
});
});
执行此单元测试,虽然能够通过,但将在控制台得到以下错误信息。

同时,展示的界面如下:

我们看到,在测试时依赖于 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 中的代码。

参考文献:深度好文
言简意赅
官方文档

正文完
 0