<<< Part 1 - Angular Test Basic 了解Angular单元测试
>>> Part 3 - Angular 单元测试简单起步
在前一篇文章中,基本了解了整个测试文件的结构,也写了一个极简的测试用例,接下来具体的了解以下Jasmine的语法,来看之前写的那个简单的测试用例:
it('should return sum of two',()=>{
let result = Convert.sum(1, 2);
expect(result).toEqual(3);
});
整个测试用例的代码包含在一个it方法中:
- 其中,第一个参数是string类型的description,用来描述该测试用例的目的;
- 第二个参数接受一个回调,即具体的测试执行代码;
- it方法可接收number类型的值作为第三个参数,用来设置setTimaOut,使代码延时执行。
Ecpectation
整个测试用例最复杂的测试逻辑,都包含在第二个回调中,我们可以尝试从语意角度分析一个测试用例。在每个测试用例中,都会包含一个测试期望,通过一个expect操作符开始,在上述代码中expect(result).toEqual(3)
就是一个期望;一个期望有包含三个部分:
- current value:即expect函数接受的值,上述的result,即为实际值;
- matcher:可以将其称之为匹配器,每个匹配器实现实际值和期望值之间的布尔比较,比较结果即为该测试pass or fail的结果,如上述的toEqual方法;另外,每个matcher前都可通过.not链接变为否定断言,如
expect(result).not.toEqual(3)
。jasmine提供了很全面的matcher,如not/toBe/noting/toBeCloseTo/toBeFalse等等,具体查阅官方API,也可根据自己的需要,自定义matcher。 - expect value:即为期望的运行结果。
before & afrer
要相对一个组件或一部分功能做全面的测试,通常我们针对其写构造个单元测试,如测试一个DOM的显示的宽、高及颜色,我们需要分别构造三个单元测试,每个测试测试中都要拿到当前的DOM对象,通常这种情况下,重复的代码就会出现。 为了防止在测试中出现大量重复的代码,jasmine专业提供了全局的beforeEach/afterEach/beforeAll/afterAll方法,根据字面意思即可理解,beforeEach和afterEach会分别在每个测试用例执行的前、后分别执行,而beforeAll和afterAll会在整个测试结合执行前、后执行。 简单改造convert.spec.ts的测试代码,观察上述代码的执行顺序:
describe('convert util testing', ()=>{
beforeAll(() => {
console.log('before all')
}),
beforeEach(() => {
console.log('before each')
}),
afterEach(() => {
console.log('after each')
}),
afterAll(() => {
console.log('after all')
}),
it('should return sum of two',()=>{
let result = Convert.sum(1, 2);
console.log('unit spec1');
expect(result).toEqual(3);
});
it('should return a - b', ()=>{
let result = Convert.sum(1,-1);
console.log('unit spec2');
expect(result).toEqual(0);
})
})
控制台中的输出结果:

this
同时,jasmine提供了this关键字作为全局变量,通过this关键字,可以在it/beforeEach/afterEach等方法中共享变量。 通过一个简单的例子进行测试,我们在arc/app/utils文件夹中分别创建multiply.ts和multiply.spec.ts文件,分别写入如下代码:
// multiply.ts
export class Multiply {
public static multiply(x: number, y:number){
return x*y;
}
}
// multiply.spec.ts
import { Multiply } from './multiply';
describe('multiple util testing', ()=>{
beforeAll(function(){
this.value = 1;
});
it('Any number multiplied by 1 remians the same',function(){
let result = Multiply.multiply(this.value, 3);
expect(result).toEqual(3);
});
})
上述代码实现一个简单的乘法,以及通过this对象进行数值的传值,测试1 * 3 = 3,单独运行这个测试代码,我们得到如下测试结果:

fail
有些情况下,一些单元测试需要覆盖程序执行失败时的情况,如测试一些错误信息是否显示,这是,我们可以通过fail方法使测试结果变成failing。我们对上述的multiply.spec.ts文件做如下改动,添加一个test error的用例:
describe('multiple util testing', ()=>{
beforeAll(function(){
this.value = 1;
});
it('Any number multiplied by 1 remians the same',function(){
let result = Multiply.multiply(this.value, 3);
expect(result).toEqual(3);
});
it('test error',function(){
fail('manually failing')
});
})
运行测试结果,显示如下:

describe的嵌套
describe方法内部可以嵌套多层子describe,当需要测试的动能复杂时,通过嵌套,可以很好的划分测试逻辑;同样,每个describe方法有其独立的作用域,各自的全局方法或独行保持相互独立。对上述的multiply.spec.ts文件做如下改动,添加子describe,并同样通过this.value传递数值:
describe('multiple util testing', ()=>{
beforeAll(function(){
this.value = 1;
});
it('Any number multiplied by 1 remians the same',function(){
let result = Multiply.multiply(this.value, 3);
expect(result).toEqual(3);
});
// it('test error',function(){
// fail('manually failing')
// });
describe('nested inside a second describe', function(){
beforeAll(function(){
this.value = 0;
});
it('Any number multiplied by 0 equals to 0',function(){
let result = Multiply.multiply(this.value, 3);
expect(result).toEqual(0);
});
})
})
在测试结果报告中,describe的层级关系通过行缩进的方式展现出来,测试结果如下:

xdescribe & xit
在某些情况下,我们需要暂时禁用一些测试用例,可以通过x前缀进行控制,通过xdescribe可已经整个测试单元禁用,包括其内部的测试用例;而xit可以针对每个测试用例进行disable操作。禁用后可以在控制台输出中看到如下结果(最后一行,显示skipped 2):

github: github.com/sunrun93/ap…