在开始撰写测试之前,先带大家来了解一下 Angular 预设使用的测试框架 ─ Karma 。
Karma 的原名是 Testacular , Google 在 2012 年的时候将其开源, 2013 年时将其改名为 Karma ,它是基於 Jasmine 与 Selenium 所开发出来的 JavaScript 测试执行过程管理工具(Test Runner)。
一般我们会使用它来撰写单元测试与整合测试,测试的档案名称通常会命名为 xxx.spec.ts
,而只要是使用 Angular CLI 所建立的档案,在预设的情况下都会连带产生该档案,像是: xxx.component.spec.ts
、 xxx.service.spec.ts
。
当我们想要执行测试程序时,只要使用指令 npm test
or yarn test
or ng test
,就可以看到它的执行结果:
当 Karma 执行起来後,只要我们不停掉它的 server 且不关掉它的视窗,只要我们有修改我们的测试并存档後,它就会侦测到我们的变动并再重新跑一次测试,是个很方便且强大的功能。
关於执行测试时的更多参数,请参考Angular 官方 API 文件
想了解更多的话,可参考网路文章:JavaScript 测试工具之 Karma-Jasmine 的安装和使用详解与 Karma 官方文件
上述提到,在 Angular 里的测试档案一般我们会将其命名为 xxx.spec.ts
,而档案内容大致上会长这样:
或是这样:
从中我们可以发现,它是一种巢状式的结构,外层会是一个名字叫 describe
的函式,内层则有许多名为 it
的函式,这些函式各是什麽意思呢?
it
指的是 测试案例(Test case),通常会在 describe
函式的里面,使用方式如下所示:
it('说明文字', () => {
// test content
});
第一个参数是该测试案例的说明文字,让我们在阅读时可以很清楚、直接地知道这个测试案例会有什麽结果,通常建议以 should
做开头,整体阅读起来较为顺畅,例如:
it('should be created', () => {
// test content
});
或者像是:
it('should have as title "Angular"', () => {
// test content
});
第二个参数是一个函式,里面就是该测试案例所要执行的程序码,也就是我们实际上要测试的内容。
describe
指的是 测试集合(Test suite),主要是用於将测试案例分组、分类,类似资料夹的概念,这样我们在阅读程序码的时候与其测试结果时,才会比较好阅读
使用方式如下所示:
describe('说明文字', () => {
// test cases
});
跟 it
一样,第一个参数是该测试集合的说明文字,让我们在阅读时可以很清楚、直接地知道这个测试集合的主要测试目标,例如:
describe('LoginComponent', () => {
describe('Component logic', () => {
describe('login', () => {
// test cases
});
});
describe('Template logic', () => {
describe('When login button be clicked', () => {
// test cases
});
});
});
第二个参数是一个函式,里面是该测试集合所要执行的测试案例。
describe
除了分类、分组的功能外,他还有一个很重要的特性 ─ 作用域(Scoping) 。
在写测试案例的时候,我们可能会遇到某些情况是在需要事先做一些配置,又或者是验证完之後需要把某些状态还原,如果将这些事情写在每一个 it
里又觉得很罗嗦且不好维护,这时候我们就会使用以下这些函式来帮我们:
beforeAll
─ 在执行所有的测试案例之前,会先执行这里面的程序码。beforeEach
─ 在执行每一个测试案例之前,会先执行这里面的程序码。afterAll
─ 在执行完所有的测试案例之後,会再执行这里面的程序码。afterEach
─ 在执行完每一个测试案例之後,会再执行这里面的程序码。举个例子,如果我们有个测试集合长这样:
describe('Test Suite', () => {
beforeAll(() => {
console.log('beforeAll');
});
beforeEach(() => {
console.log('beforeEach');
});
it('test case - 1', () => {
console.log('test case - 1');
});
it('test case - 2', () => {
console.log('test case - 2');
});
afterEach(() => {
console.log('afterEach');
});
afterAll(() => {
console.log('afterAll');
});
});
它的执行结果会是这样:
// beforeAll
// beforeEach
// test case - 1
// afterEach
// beforeEach
// test case - 2
// afterEach
// afterAll
从上述结果中可以看出,在一个测试集合里会先执行的是 beforeAll
里的程序,接着会是 beforeEach
,然後才会是测试案例;而在测试案例之後,则会先执行 afterEach
才会轮到下一个测试案例之前的 beforeEach
,再接着下一个测试案例,之後一样会是那个测试案例之後的 afterEach
。直到最後没有测试案例时,就执行 afterAll
里面的程序,结束这个测试集合。
有比较理解了吗?如果有的话,我们来试试比较复杂一点的巢状结构:
describe('Test Suite - 1', () => {
beforeAll(() => {
console.log('beforeAll - 1');
});
beforeEach(() => {
console.log('beforeEach - 1');
});
it('test case - 1', () => {
console.log('test case - 1');
});
it('test case - 2', () => {
console.log('test case - 2');
});
describe('Test Suite - 2', () => {
beforeAll(() => {
console.log('beforeAll - 2');
});
beforeEach(() => {
console.log('beforeEach - 2');
});
it('test case - 3', () => {
console.log('test case - 3');
});
it('test case - 4', () => {
console.log('test case - 4');
});
afterEach(() => {
console.log('afterEach - 2');
});
afterAll(() => {
console.log('afterAll - 2');
});
});
afterEach(() => {
console.log('afterEach - 1');
});
afterAll(() => {
console.log('afterAll - 1');
});
});
它的执行结果会是这样:
// beforeAll - 1
// beforeEach - 1
// test case - 1
// afterEach - 1
// beforeEach - 1
// test case - 2
// afterEach - 1
// beforeAll - 2
// beforeEach - 1
// beforeEach - 2
// test case - 3
// afterEach - 2
// afterEach - 1
// beforeEach - 1
// beforeEach - 2
// test case - 4
// afterEach - 2
// afterEach - 1
// afterAll - 2
// afterAll - 1
为让大家比较好阅读,我将每个测试案例稍微隔开方便大家观察其中规律。
虽然这个例子比较复杂,但逻辑上来说跟上一个例子一样:在开始测试某测试集合里面的测试案例之前,会先执行该测试集合的 beforeAll
,接着是每一个测试案例的 beforeEach
,然後执行测试案例,执行完测试案例後就是 afterEach
。
比较特别需要注意的就是当要开始执行 test case - 3
之前,会先执行的是 Test Suite - 2
的 beforeAll
。原因就像上面提过的:「在开始测试某测试集合里面的测试案例之前,会先执行该测试集合的 beforeAll
」, test case - 3
是 Test Suite - 2
里面的测试案例,所以在开始测试 test case - 3
之前,自然会先执行该测试集合里的 beforeAll
,接着是父层测试集合里的 beforeEach
,才会轮到 Test Suite - 2
里面的 beforeEach
。
这个概念在大多数的前端测试框架里是差不多的,学一次基本适用在大多数的测试框架里, CP 值非常之高。
虽然上述的测试执行过程看似有序,但实际上我们不能依赖这种有序,原因跟如何撰写出优秀的测试有关,不过相信今天的内容应该已经够烧脑了,所以明天再跟大家分享如何撰写出优秀的测试吧!
今天的文章内容主要是要让大家在开始撰写测试之前,先对 Angular 的测试框架、测试档案的内容结构有个初步的理解,如此一来有两个好处:
此外,今天的重点主要是以下三点:
尤其是关於作用域(Scoping) 的部份,这在後续撰写测试时,会非常常使用,所以如果有任何的问题或是回馈,请务必留言给我让我知道噢!
<<: [DAY04] 建立 Datastore 和 Dataset (下)
>>: Day5|【Git】动手建立、初始储存库(Repository)!
前言 之後几天会拿来做一个 app~ 在查了一些资料之後, 整理了 开发 APP 的步骤。 开发步骤...
今天要跟大家介绍的是 script 工具,在 Zabbix Server 安装好预设只会有三个写好的...
写在前面 Placeholder for test test Placeholder for tes...
「认知」是你观望世界的窗,不时擦拭,光线才能穿透。 Your assumptions are you...
最後终於来到了我们最後一个章节:『 Transforms 』。 Transform 在 slate...